Select directive not selecting option when options are loaded asynchronously - angularjs

I have a <select> directive whose options are loaded asynchronously. The variable that the ng-model attribute points to exists prior to the options being loaded. However, after the options are loaded the correct option is not selected. Instead, the select becomes blank. I've created a plunk to demonstrate this behaviour: http://plnkr.co/edit/KNunC6.

https://github.com/angular/angular.js/issues/9714#ref-pullrequest-53337829
seems like this is an angular core bug. you'd be more successful using ng-repeat and ng-checked I guess.

I think this fixes your issue. Here's a plunker. The problem was that you were initializing the ngModel before the promise was finished. The ngModel needs to be initialized after the list gets populated so angular can figure out which option is selected. Also, the ngOptions is tricky sometimes because angular assigns a unique id to every option and ignores the value attribute. I had this issue a couple of times also, nasty one!

There is a problem when the value part of the option has the same value as the label part.
In your case, value == id == 'Canada' and label == name == 'Canada' as well. If you change one of them to anything else, it will work. I am not sure why it is an issue, but just change and it will be ok.
Example: change the name from 'Canada' to 'Canada ':
[
{ id: 'Canada', name: 'Canada ' },
{ id: 'US', name: 'United States of America' }
]

Related

AngularJS: why does my dropdown have an initial blank entry?

I am fetching some JSON frm a server and using it to populate a combobox.
A JSON entry looks like this
"campaign_id": "2",
"customer_id": "1",
"title": "Purple monkey dishwasher",
"description": "perfectly cromulent",
"start_time": "19/09/2015 09:42:06",
"end_time": "19/10/2015 09:42:06"
And I declare my drop down thus-wise
<select name="SelectCampaignForConnections"
ng-model="connectionsCampaignDropDown"
ng-options="campaign.title for campaign in campaigns"
ng-change="ShowConnectionsForCampaign(connectionsCampaignDropDown)">
I initialize the model of the select ...
$http.get(url)
.success(function(data, status, headers, config)
{
if ($scope.connections.length > 0)
$scope.connectionsCampaignDropDown = $scope.connections[0];
When the dropdown shows, it contains the title element of each JSON entry, BUT, it has an initial blank entry.
What am I doing wrongsomely?
[Update] #sheilak gave a good anser :
In order for the dropdown to defaulted to a non-blank value, the value
of the variable passed to ng-model must equal one of the options
passed to ng-options.
In your case where ng-options is populated by values of
campaign.title, it looks like the value passed to ng-model i.e.
connectionsCampaignDropDown should be populated with
$scope.connections[0].title rather than the whole object
$scope.connections[0].
$scope.connectionsCampaignDropDown = $scope.connections[0].title;
However, I would prefer to pass around an complete object, rather than just a field of it.
Can this be done?
(if not, then I will have to pass only the title to the ng-change function ShowConnectionsForCampaign() and it will then have to loop over the data to find a match, which seems inefficient)
<select name="SelectCampaignForConnections"
ng-init="justGiveItAName=getInitialSelection()"
ng-model="justGiveItAName"
ng-options="campaign.title for campaign in campaigns"
ng-change="ShowConnectionsForCampaign(justGiveItAName)">
where getInitialSelection() is a function on your scope that could take a param if you need it to, but I would probably go with something like this in the case you outline above:
function getInitialSelection() {return connections[0]};
or set it directly in the ng-init:
ng-init="$scope.connections[0]"
(you might have to fiddle with the above code - I haven't tested it).
btw - 'justGiveItAName' is then an object available elsewhere.
I have now tested it; see these Fiddles for working examples:
Setting directly in ng-init: http://jsfiddle.net/lukkea/nuo2c3Lk/
Using a function on the $scope: http://jsfiddle.net/lukkea/o6strxjf/
Passing the object instead of properties (as the OP requested): http://jsfiddle.net/lukkea/fsx8s67j/2/
Passing the objects in and object back: http://jsfiddle.net/lukkea/ww9yqsrm/3/
In order for the dropdown to defaulted to a non-blank value, the value of the variable passed to ng-model must equal one of the options passed to ng-options.
In your case where ng-options is populated by values of campaign.title, it looks like the value passed to ng-model i.e. connectionsCampaignDropDown should be populated with $scope.connections[0].title rather than the whole object $scope.connections[0].
$scope.connectionsCampaignDropDown = $scope.connections[0].title;
This should be possible, you just have to make sure that the initial value you set is the exact same object (not just another object with the same values)
You should be able to use campaign as campaign.title for campaign in campaigns in ng-options.
Then it stores the selected campaign object in the model (not just the value of campaign.title) and the label shown in the dropdown will still be campaign.title.
<select name="SelectCampaignForConnections"
ng-model="connectionsCampaignDropDown"
ng-options="campaign as campaign.title for campaign in campaigns"
ng-change="ShowConnectionsForCampaign(connectionsCampaignDropDown)">
The expression used here is: select as label for value in array.
select - the value stored in ng-model
label - the text displayed in the dropdown
The different expression options are listed in the official documentation.

AngularJS, Select and binding to value

I have a scenario that I thought it is logical, but seems angular doesn't support it.
So, I have in the scope/controller a list of user types as array of objects like this:
$scope.userTypes = [{text : "Buyer", value : "1"},
{text : "Vendor", value : "2"},
{text : "Buyer / Vendor", value : "9"}];
$scope.user = new User(globalSelectedUserType);
Where the User is defined like this:
function User(userType) {
this.userType = userType;
this.isAdminUser = false;
this.isActive = true;
this.roleDisabled = true;
};
And I want to have a select element with the list of options coming from the userTypes binding the values to the "value" property and text to the "text" property , and bind the select value to only the "value" property of the array object, allowing the user to set the value from the code.
so when I create the user from the code using this
$scope.user = new User("9");
it should initialize the select with the "Buyer / Vendor" selected.
Reading the documentation of the select element in angular and ng-options, seems it is impossible to do.
I tried this
<select name="usertypedata" id="UsersTypeData" ng-model="user.userType"
ng-disabled="user.roleDisabled" ng-options="usertype.value as usertype.text for usertype in userTypes track by usertype.value">
</select>
and when we select the option, I want only the "value" property to be populated by the ng-model
I have two answers for this.
First answer is what PSL suggested in his comments before.
The second answer is
instead of binding ng-options to an array, I bind it to an object and it worked fine
to elaborate more, it might be that I am not doing fully angular solution from start to end.
I am using angular to populate / and control the behavior of the html page / form.
But later, I am doing normal html/form submit that will submit all the data inside the form.
and usual form/submit, will use the select/option value to submit that value, and not ng-model
but ng-model was important for me to select the option programmatically.

AngularJS: how put correct model to repeated radio buttons

I think I have some sort of special code here as all I could google was "too simple" for my problem and it also didn't helped to come to a solution by myself, sadly.
I got a radio button group of 2 radios. I am iterating over "type" data from the backend to create the radio buttons.
My problem is the data binding: When I want to edit an object its "type" is set correctly, but not registered by the view so it doesn't select the desired option.
Follwing my situation:
Backend providing me this as "typeList":
[
{"text":"cool option","enumm":"COOL"},
{"text":"option maximus","enumm":"MAX"}
]
HTML Code:
<span ng-repeat="type in typeList track by type.enumm">
<input
type="radio"
name="type" required
ng-model="myCtrl.object.type"
ng-value="type">
{{type.text}}
</span>
Some Explanation
I don't want to use "naked" texts, I want to use some sort of identifier - in this case it is an enum. The chosen value shall be the entire "type", not only "type.text" as the backend expects type, and not a simple String.
So all I do with this is always a package thingy, the type.text is for like formatted/internationlized text etc.
A Pre-Selection works by setting this in the controller: this.object.type = typeList[0];
The first radio button is already selected, wonderful.
But why isn't it selected when editing the object. I made a "log" within the HTML with {{myCtrl.object.type}} and the result is {"text":"cool option","enumm":"COOL"}. The very same like when pre selecting. I already work with the same "technique" using select inputs, and it works fine. I also found some google results saying "use $parent because of parent/child scope". But 1) I didn't get that straight and 2) think it is not the problem here, as I use a controllers scope and not the $scope, or is this thinking wrong?
It might be explained badly, sorry if so, but I hope someone 1) get's what I want and 2) knows a solution for it.
Thank you!
If you're trying to bind to elements from an array, I believe you need to assign the actual elements from the array to your model property.
So this creates a new obj and sets it to $scope.selectedType (not what you want):
$scope.selectedType = {"text":"cool option","enumm":"COOL"};
whereas this assigns the first element of the array (which is what you want)
$scope.selectedType = $scope.typeList[0];
So to change the model, you can lookup the entry from the array and assign it to your model with something like this
$scope.selectedType = $scope.typeList.filter(...)
Here's a quick example of this approach http://plnkr.co/edit/wvq8yH7WIj7rH2SBI8qF

AngularJS ngOptions select list behaves inconsistently in IE 9

This jsFiddle hopefully demonstrates the problem I am facing with IE 9. I am roughly doing the same things I am doing in my real app. I have a list of stuff that I loop through using ngRepeat. Each item has a property that can be selected from a dropdown list which is rendered using ngOptions.
The problem in IE seems to be that selecting an item from the list jumps back to the previous item in the list. Chrome does not have this problem, the item selection is perfect. In the example, try selecting the value 'Imported' in the Fiddle - I could simply not do it in IE whereas it works perfectly in Chrome.
The only thing I can think of that could be a problem is the IDs for the dropdown which are currently just plain old string values like '20', '30' and '40'.
The code for ngOptions is
<select class="form-control" id="selProc" data-ng-model="currentPlant.ProcType" data-my-dropdown='Plants' data-ng-options="plant.SpecialProcurement as plant.Description for plant in Plants"></select>
currentPlant here is the looped variable.
The dropdown values are the following array:
var dropDownValues = [{
Plant: '2150',
SpecialProcurement: '20',
Description: 'External'
}, {
Plant: '2150',
SpecialProcurement: '30',
Description: '3rd Party'
}, {
Plant: '2150',
SpecialProcurement: '40',
Description: 'Imported'
}];
These dropdown values get populated in a custom directive's link function. In my real app the list gets fetched using $http.post() and the result is assigned to the dropdown variable using JSON.stringify. Regardless of whether this is good practice or not, the problem still persists. If the problem is because of the JSON.stringify, I would be keen to know. I have tried replacing that with a regular array assignment but the result is the same.
I'm stumped. Please help.

AngularJS inconsistent databinding

I'm learning AngularJS and I have a question regarding the databinding for select elements. The databinding for textboxes works without any kind of event handling code. Once the ng-model attribute is set textbox updates when the model property changes and vice versa. There is no need for ng-change attribute.
However, for select elements we need to write functions that will be called via ng-change atribute.
Why does angularjs handle databinding without an ng-change attribute for textboxes but requires functions that will be called via ng-change attribute for select elements?
UPDATE:
Added the fiddle in the comments section. The example is from AngularJS in Action book. Click on one of the stories, change the textbox value and the model is updated. Change the selection in dropdown model is not updated.
UPDATE:
Added a new fiddle in the comments.
Thanks.
I've created a fiddle that works here - The issue is really just the dummy data here. In the original fiddle, the object created in the statuses array for {name:'Back Log'} and {name:'To Do'} are not the same (not ===) as the {name:'Back Log'} and {name:'To Do'} objects created in the dummy story objects.
To make the example work, I pass the indexed statuses into the getStories function. However I think this is really just a case of demo-induced confusion. (I've been looking at the MEAP for Angular in Action as well, and I think it could be simplified a bit like this one, that uses simple string statuses that will pass the === test
var getStories = function(statusesIndex) {
var tempArray = [
{title:'Story 00',
description:'Description pending.',
status: statusesIndex['To Do']
},
{title:'Story 01',
description:'Description pending.',
status: statusesIndex['Back Log']
}
];
return tempArray;
}
I think your confusion might be a result of the select documentation still being incorrect. (See my Disqus comment.) ng-model can and should be used with select. ng-change is optional and it just gives you a hook should you want to do something each time the selected option changes.
Normally you should use ng-options with select.
If i understood your question correctly then I think your guessing is wrong because for select boxes, you do not have to invoke ng-change event in order to fetch the selected option.
<select ng-model='select'>
<option>....</option>
<option value='one'>One</option>
<option value='Two'>Two</option>
</select>
// Your selected option will print below... without invoking ng-change
<div>You selected: {{select}}</div>
Demo: http://jsfiddle.net/jenxu/1/

Resources