Angular ngOptions 'track by' and null value validation issue - angularjs

I have a select list using ngOptions and the 'track by' feature like this:
<select validdate="{{date}}" ng-model="code" ng-options="code.description for code in codelist track by code.value">
<option value=""></option>
</select>
The code object has various properties, including a start and enddate which determines the validity of the code. There is a seperate 'date' property which is used to check if the code can be selected or not. For this validdate-directive I need the whole object, therefore I use the track by and cannot use the 'as' to fill the ng-model property.
This works properly, except for the initial null value.
When the validation returns false (and marks the select-box accordingly) and the user then selects the null value, the validation doesn't get reset.
The validator isn't called, even though the ng-model property 'code' changes from an object to a null-value. It seems that because of the track by, it does not trigger the validation directive. Does anyone know a solution for this?

It was a bug in my particular version of Angular, 1.3.8. Upgrading solved it, but for reference this JS Fiddle shows the bug (and changeing the version there solves the bug) http://jsfiddle.net/2tvy2jgh/1/
enter code here

Related

ng-repeat in select tag not producing results

Having a lot of trouble with ng-repeat in a select tag. The below is not working for some reason though all documentation indicates that it should.
<select id="blahh" ng-model="log_instances" class="selectpicker" multiple>
<option>this works</option> <!-- this works -->
<option ng-repeat="comp in env.status.components">test-value</option>
</select>
The only option that ends up showing is the 'this works' one, but I would expect 'test-value' to show up for each of the items described in the ng-repeat's.
Additionally, I also checked the console for angular.element(document.getElementById('blahh')).scope() and it shows the proper data that I would expect to see. Also, if I include a table right below this select with the same ng-repeat's and fields, it produces the expected output just fine. I'm using Angular 1.6.5
Any help is appreciated!
The HTML snippet included with the original question had <span> tags as immediate children of the <select> tag, which would've produced invalid markup as only <option> or <optgroup> elements are permitted.
Whenever you have a data collection that needs to be presented as a select list, Angular's ng-options attribute makes it easy.
DEMO
Also, if you need a default option (for when the collection data might be in transit due to an AJAX request), include an <option> element assigned with an empty value (i.e. value=""), then you could use ng-if to hide this default option when it is no longer necessary.
Use ng-options like below. But your code also should work check env.status.components
<select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
My question to you would be, why do you want to use a ng-repeat inside a multiple select, when you have ng-options handy?
Assuming your comp object looks like this:
{
name: 'somevalue'
}
I would change your code to look like so:
<select id="blahh" multiple="true" ng-model="log_instances" ng-options="comp.name for comp in env.status.components"></select>
Please have a look at the plunker demo I have made for you

Set selected attribute for single/multi selection with ng-options

It seems that angular's behavior with respected to the selected attribute has changed back and forth with each major version. So I'm just looking for someone to tell me if this is achievable in the 1.5.x release using ng-options rather than ng-repeat.
What I'm trying to do:
Have angular create html like the following(so that it can be interpreted by a jquery plugin)
<select multiple="multiple"...>
<option value="abc123" label="Justin" selected="selected">Justin</option>
</select>
I've tried many variations of this
<select ms-multi-listbox multiple="multiple" class="multi-select"
id="ym" name="ym" ng-model="groupCtrl.memSelection"
ng-options="mem as mem.displayName for mem in groupCtrl.selectableMembers track by mem.id">
with no luck.
the model looks like this:
groupCtrl.memSelection =["abc123"];//inbound from api as is selectableMembers
Here is a plunk I've been playing with (try changing the angular version for added confusion): Plunker
Any ideas are welcome.. I mainly wanted to avoid ng-repeat because it has a lot of overhead in longer lists but obviously slow is better than not working so I'll use it until I learn how to do this with ng-options.
Thanks!
edit:
Here is a ng-repeat that achieves the result I want:
<option ng-repeat="mem in groupCtrl.selectableMembers" value="{{mem.id}}" label="{{mem.displayName}}" ng-selected="groupCtrl.memSelection.indexOf(mem.id)>=0">{{mem.displayName}}</option>
enter code here
Be careful when using select as and track by in the same expression. AngularJS API ngOptions
Your expression for ng-options can't work. Using select as and track by in the same expression evaluates mem.id to mem.id.id. Change the expression to
ng-options="mem as mem.displayName for mem in groupCtrl.selectableMembers track by mem.id"
And how you set the selected objects don't look right to me as well. You can not just set the id, you have to set the whole object as selected otherwise your ng-options expression evaluation will also fail. Therefore change your selection to (assuming selectable data is stored in $scope.model as it is in the plunk example).
$scope.memSelection = [$scope.model[0], $scope.model[1]]
I have updated the plunk and it works for single and multi selection.
For more information read AngularJS API ngOptions.

Adding angular directives to simple_form in rails breaks selected option for collection input

Not sure if there's a workaround for this, I'm doing something wrong, or this is a collision between simple form or angular, but:
.field
= f.input :role, collection: roles.map(&:name), selected: user.role.try(:name), include_blank: 'Select', required: true, input_html: { "ng-model" => "role" }
renders (which looks correct):
<select ng-model="role" class="select required" name="user[role]" id="user_role">
<option value="">Select</option>
<option value="system">system</option>
<option selected="selected" value="fcm">fcm</option>
<option value="regulatory">regulatory</option>
<option value="operations">operations</option>
<option value="help_desk">help_desk</option>
</select>
But the selected value is the include_blank value of 'Select'. And yes, role is set on the user.
The reason it's doing this is because of the way angular models work and it requires an understanding of this. Essentially the ruby generates the select correctly choosing the selected option of the select box too.
However, once the angular is loaded and sees ng-model on this select it sets the current selected option to the value of the model, in your case, role.
Thus if you want to have an angular model for this select, you need to set the initial value of that model.
Try the following:
.field
= f.input :role, collection: roles.map(&:name), include_blank: 'Select', required: true, input_html: { "ng-model" => "role", "ng-init" => "role = #{user.role.try(:name)}" }
Note that the selected option has been removed entirely since this will be controlled by the Angular model, which is initialized to that value using ruby string interpolation.
I thought for a moment on this particular example and I am not quite sure what you expected to happen with the result.
Although, I assumed that you want to have option with attribute selected="selected" selected as a default value, instead of value="", which is only a blank field. If that I understood correctly and this is the point, that means there are several fields, which might be incorrect and We (the community) do not know how you completed them, but I think I dug up for the problem... ;)
Angular directive - At first, I do not know which version of angular you used in the example (I assume 1.X, because question have 6 months from this moment). The AngularJS select directive overrides the default behavior of select element and change it a bit.
I prepared a simple example with Angular v1.0.1 for the tests and it looks behave correctly in few aspects. If you use ng-model="role" and set the role to fcm (String), like $scope.role = 'fcm', the angular is obligate to set this value if it will find it.
I have also tested (AngularJS v1.0.1) what if the role is not set (undefined), then angular also point on empty string value like value="" and it not see into attribute selected and not set it as default. In the other side, the latest stable AngularJS v1.5.6 does support selected attribute example with AngularJS v1.5.6, so it might be the core of the problem.
Well, the solution for this is simple:
upgrade AngularJS for the latest 1.X version or
consider to use
ngSelected
or ngInit/SO
solution instead if the version AngularJS you used support that.

Roll back ngOptions select to unselected state

ngOpions creates a blank option like option html below if the model value doesn't match any of the option values:
<option value="?" selected="selected"></option>
After a selection is made, AngularJS removes this first empty option html from the select html.
I have a use case where I want to roll back the state of the html select to the unselected option which AngularJS removed. I tried ngModel methods like $setPristine(), $rollbackViewValue(), and $setUntouched(). But none of these mothods puts the state of the select back to the initial state with the empty option.
Is it possible to truely roll back an AngularJS select element to have the empty option after a selection is made?
The code for ngOptions (line 473 in ngOptions.js ) suggests that only single-select can get it, and that the writeValue function (called when the ngModel changes) has to be invoked with a null value (not undefined, as === is used.)
So you can try setting your model variable to null.
If that doesn't work, I expect you may be out of luck as far as proper solutions go, and might need to re-render the whole directive. You can create a pretty simple directive to do that, similar to what's suggested in this answer: https://stackoverflow.com/a/22133080/624590

Dynamic Select List Default Selected Item - Angular JS

I have the code listed below which works fine, however when i attempt to add ng-model and related ng-change to the select, an empty option is added. I understanding why, it is because on init the selectedOption for ng-model is empty.
What I want to do is set a default value so when I can use ng-change to set options IsSelected value to true when user selects it. I'm just not sure how to go about this, I have no issues doing this when I'm working with a static generated select list, but for some reason I can't figure it out for this dynamic generated list.
Any input is appreciated!
<div ng-repeat="optionType in productDetailModel.OptionTypes">
<select name="{{optionType.OptionTypeName}}">
<option ng-repeat="option in optionType.Options"
value="{{option.OptionValue}}">{{option.OptionValue}}
</option>
</select>
</div>
Here's plunkr I mocked to give a basic idea of what I have in mind: http://plnkr.co/edit/xBDfc0XzDwsF0mBiFZOv?p=preview
The initial option is blank because the model is initially undefined.
As tymeJV said, initializing your scope inside of your .js will define a default value that will be shown as selected initially.
$scope.modelName = $scope.optionType.Options[0];
It might be helpful to use ng-options instead of ng-repeat. One reason why it might be beneficial is that ng-options does not create a child scope (unlike ng-repeat). This could help simplify linking to your model and avoid confusion.
<select name="{{optionType.OptionTypeName}}" ng-model="modelName" ng-options="option for option in optionType.Options">
</select>
This article covers the basics of ng-options as well as discusses why it would be used as opposed to ng-repeat.
Hope this helps.
Use ng-options when using a select!
<select name="{{optionType.OptionTypeName}}" ng-model="someModel" ng-options="option as option for option in optionType.Options>
</select>
And then set the ngModel to the default option you want selected:
$scope.someModel = $scope.optionType.Options[0]
There is one directive of select element is ng-init but it call once while first time rendering, but if you change the options dynamically then it will not call again. So for that you need to set the model field with value in scope just below of your new option population logic.
$scope.modelName = $scope.Options[0]; // if array
$scope.modelName = $scope.Options[key]; // if Json Object

Resources