IE, ng-repeat for select cutting text or showing {{}} - javascript

Basic Problem
I have a multi-select (list) that depending on how I write the html/angular has a bug. In the first case the last 3 characters are cut off from the rendering. In the second case the name is not visible but instead the {{}} placeholder until the item is clicked.
I'd simply like a way for me to display the elements in a correct fashion without bugs.
Finally, this behavior seems to happen if an element is added to the categories array after the page and select has rendered.
With ng-bind
<select id="categories" name="categories" class="ep_field sumoSelect" multiple="multiple"
ng-model="selectedCategories"
ng-change="angularCategorySelectedGrants($event)"
<option ng-repeat="cat in categories" value="{{cat.id}}" ng-bind="cat.name"></option>
</select>
Without ng-bind
<select id="categories" name="categories" class="ep_field sumoSelect" multiple="multiple"
ng-model="selectedCategories"
ng-change="angularCategorySelectedGrants($event)"
<option ng-repeat="cat in categories" value="{{cat.id}}">{{cat.name}}</option>
</select>
With ng-options
With ng-options everything appears but I am unable to actually click on the elements to select them - they are frozen.
<select id="categories" name="categories" class="ep_field sumoSelect" multiple="multiple"
ng-model="selectedCategories"
ng-change="angularCategorySelectedGrants($event)"
ng-options="cat.name for cat in categories track by cat.id" >
</select>
Since no-one wrote an answer, see my own work-around as the accepted answer.

My own workaround
It seems the problem was with adding an item to the categories array after the initial rendering has taken place. There we two workarounds I found:
Add all elements to the array only once without adding again OR
Hide the dom select element utilizing ng-if for 100ms and make it visible again. This forces the browser to re-render the elemnents and renders them correctly.
In HTML (wrapping the select):
<div ng-if="categories!=undefined && categoriesLoaded">
...Select code here...
</div>
In the controller (Javascript):
$scope.categoriesLoaded = false;
//Trigger render
$timeout(function(){ $scope.categoriesLoaded = true;}, 0);

Related

AngularJS - issue with dropdown menu in <select> tag, can't have 2 custom option while using ng-option

I have a <select> that loops over an array of data and displays the selections available in the dropdown list.
the markup is like this:
<select required class="form-control btn-primary btn dropdown-toggle"
ng-options="s.name for s in list track by s.name" ng-model="list">
<option selected value=""></option>
<option value="" ng-click="">Split</option>
</select>
What I want is, to have the preselected value as an empty string, I achieved that with:
<option selected value=""></option>
but then I need a second custom <option> tag besides all the <option> tags that get generated by the ng-option,
in this tag I need to show a string, in this case "split", and there will be a ng-click on this option so the user can click on it and a second dropdown will appear.
The problem is the second <option> tag it's not shown, only the first one, and I did some tests, and apparently I can show only one option tag besides the ones from ng-option.
Is there a solution for this?
What I need as end result is my option list generated by ng-option,
then a preselected option that is EMPTY (it shows when the user loads the page), and a second option with a custom string that will be used like a button.
Here a jsfiddle where you can see that the second custom option tag isnt shown
https://jsfiddle.net/0xyt24sh/10/
thank you
It's impossible to have more than one hard-coded option with ngOptions, as explicitly mentioned in the documentation:
Optionally, a single hard-coded element, with the value set
to an empty string, can be nested into the element. This
element will then represent the null or "not selected" option.
A workaround might be to use ngRepeat:
<select class="form-control btn-primary btn dropdown-toggle" ng-model="selection">
<option value="" selected></option>
<option ng-repeat="s in list">{{s.name}}</option>
<option ng-click="" value="split">Split</option>
</select>
Updated fiddle: https://jsfiddle.net/0xyt24sh/17/
From ngOptions documentation:
Optionally, a single hard-coded element, with the value set to an empty string, can be nested into the element. This element will then represent the null or "not selected" option. See example below for demonstration.
In this case, ngOptions isn't fit for your problem; Instead, you can use <option>s with ng-repeat, as such:
<select required class="form-control btn-primary btn dropdown-toggle" ng-model="selected">
<option selected value=""></option>
<option ng-repeat="item in list track by item.code">
{{item.name}}
</option>
<option value="split" ng-click="doSomething()">Split</option>
</select>
Working JSFiddle here

Drop down value replicates in nog options Angular

I have a dynamically generated html table that adds rows based on the record that is displayed. I'm adding a column that will contain a dropdown. I used ng-options for it, however every time I change one record, the rest are also updated. Tried changing it to ng-repeat and get the same result. See code below:
<td>
<select class="form-control" ng-model="$ctrl.selectedRC" ng- options="r.ccd as (r.OName + ' -- '+ r.RCName) for r in $ctrl.RC track by r.ccd"> </select>
<!--if I have 5 records, all dropdowns in the table change -->
</td>
Using ng-repeat:
<select class="form-control" ng-model="$ctrl.selectedRC" <option value="" ng-selected="true">--Select one--</option>
<option ng-repeat="r in $ctrl.RC"
value="{{r.OName}}"
ng-selected="{{r.OName === selectedRC}}">{{r.RCName}}
</option>
</select>
I know that these two are currently displaying two different things (one a concatenated set of values, the other juts one). But my main interest is to figure out how to have each <td> have its own dropdown without affecting the rest of the rows.
Simply because you use the same ng-model for all rows.
You need to define a different one for each row.
You do this:
ng-model="$ctrl.selectedRC"
but you need something like this:
ng-model="$ctrl.selectedRC[$index]"
where $index is your reference to the row.

Angular 1.5 Selected Attribute on First Option

I recently updated to Angular 1.5, but found that it breaks a very corner case testing feature -- we test a page to see if a certain option is selected in an ng-options:
<select name="User" ng-model="userModel" ng-options="user.key as user.name for user in users">
</select>
This produces html like this:
<option value="string:user1" >User1</option>
<option value="string:user2" >User2</option>
If the first option in the list is selected, then there won't be a "selected" attribute. Otherwise it will be there on the option that is selected, like this:
<option value="string:user1" >User1</option>
<option value="string:user2" selected="selected">User2</option>
Is there any way for the "selected" attribute to appear on the first option of the list if it's selected? As far as I can tell, this worked like that back in Angular 1.2 and before, but was changed in 1.2.19. (https://github.com/angular/angular.js/issues/8366)
Is there some workaround, or another attribute I can set?

Generating options and selecting the first result automatically

This is something I've struggled with for quite a while now. My objective is to populate the select box with a variable list of greetings and have it automatically select the first one instead of simply being a blank space.
<label>Check me to select: <input type="checkbox" ng-model="selected">
</label><br/>
<select> id="greetings">
<option ng-repeat="greeting in MyCtrl.greetingList"
ng-selected="selected">{{ greeting }}</option>
</select>
The ng-selected directive (and indeed the selected attribute, which would actually be a simpler solution) do indeed select an item from the list on form load, but it always defaults to the last item on the list. I could probably capture the first element in the list and generate the rest, but I can't help but think there's a more elegant solution out there. Any ideas?
By the way, I know ng-options is better than ng-repeat. The problem is that I need each of my options to have an ng-click directive. I can't achieve that with ng-options.
You should try $first plus assign a model to select. in this way
<select data-ng-model="greetings">
<option data-ng-repeat="greeting in MyCtrl.greetingList"
data-ng-selected="$first" data-ng-value={{greeting.id}}>{{ greeting }}</option>
</select>
There are some special properties too. All of them are as below
$index
$first
$middle
$last
$even
$odd

wholesale replacement of options html string within select?

I would like to 'toggle' the list of options in a select box.
I have the two sets of options as strings (American states and Canadian Provinces). However, I notice that in the DOM, the select object has no 'innerhtml' property (at least not according to w3schools).
Do I have to go and remove and replace the options one by one? How do I do this?
Making use of jQuery, I usually do something like this:
var html = '[your html string with all the options elements]';
$('#mySelectId').empty();
$('#mySelectId').append(html);
As for the one-by-one idea, make sure you keep in mind the general slowness of DOM interaction. Wholesale replacement with the entire string is going to be pretty quick, but if you manipulate the DOM for each element in the select then expect it to be slow.
What I would do is create both, each in a DIV; then just hide whichever is not needed. This eliminates the need for heavy DOM manipulation (as you're only doing that once, on page load, there are fewer opportunities to leak memory in certain browsers cough cough), and is harder to accidentally mess up the app state (what with Alberta and Alabama sharing the same code and all that).
This would be the initial page:
<input type="radio" id="country_usa" name="country" value="USA"> USA
<input type="radio" id="country_canada" name="country" value="Canada"> Canada
<div id="usa_select"></div>
<div id="canada_select"></div>
and JavaScript to go with it (jQuery used here for brevity):
$(document).ready(function(){
// hide both <div> containers on page load
$('#canada_select').hide();
$('#usa_select').hide();
// create and populate both <select> boxes:
$('#canada_select').append('<select name="province_canada">'
+ '<option value="AL">Alberta</option>'
+ '<option value="BC">British Columbia</option>...'
+ '</select>'
);
$('#usa_select').append('<select name="state_usa">'
+ '<option value="AL">Alabama</option>'
+ '<option value="AK">Alaska</option>...'
+ '</select>'
);
};
// we'll also need handlers to show the correct list,
// depending on the selected country
$('#country_usa').click(function(){
// we want US states
$('#canada_select').hide();
$('#usa_select').show();
});
$('#country_canada').click(function(){
// we want Canadian provinces
$('#usa_select').hide();
$('#canada_select').show();
});
This should be the result:
<input type="radio" id="country_usa" name="country" value="USA"> USA
<input type="radio" id="country_canada" name="country" value="Canada"> Canada
<div id="usa_select">
<select name="state_usa">
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
...
</select>
</div>
<div id="canada_select">
<select name="province_canada">
<option value="AL">Alberta</option>
<option value="BC">British Columbia</option>
...
</select>
</div>
At the backend, process state_usa iff country=='USA'; process province_canada iff country=='Canada'.
Why not replace the entire <SELECT>? There are a number of ways to do this. Easiest is to wrap the <SELECT> in a SPAN/DIV and replace its innerHTML.
If you're pulling your lists from an array, you can set the list length to zero, then insert the new elements in a loop.
When you say they are held as strings - do you mean that each item is a seperate string or that the list items are one string (i.e. var variable = "<li>item 1</li><li>item 2</li>";)
If the latter, could you not include the <select> tag in the string and use the jQuery replaceWith function?
Just add a multiple="multiple" attribute to your select tag if you want multiple selection or add size=".." if you want single selection

Categories