ng-options predefined option Object - angularjs

Have a look at my example
http://plnkr.co/edit/21ewxVIaRm4IHyF3TgoD?p=preview
I need the option object to be stored as ng-model so i use for ng-options ng-options="m as m.name for m in list"
However, if i set ng-model manually as the object it does not select the correct option in the dropdown list by default. It stores the data correctly, but it only pre-selects an option if use this expression ng-options="m.name as m.name for m in list" but this stores a string on the model instead of the options object.
Am I doing something incorrectly?
Goal:
set ng-model to the default option object and have it be automatically selected in the dropdown.

Updated plunker.
For the version you are using (1.0.8), you would have to resolve the object using a loop:
$scope.selected = {
name:"Something"
}
$scope.setSelected = function() {
angular.forEach($scope.list, function(item) {
if (item.name == $scope.selected.name) {
$scope.selected = item;
}
})
}
$scope.setSelected();
More recent versions, have track by syntax supported in the ng-options. So you could write:
ng-options="m.name for m in list track by m.name"
And this would set the object that matches the predicate.

As Davin Tryon has already mentioned, using track by is the correct way of doing it, however, I see some people using track by and 'as' in the same ng-options string, which will not work. using track by should also cater the need of using 'as'

Related

AngularJS ngOptions track by does not set entire object to ngModel

Here my json object :
$scope.todos = [{
name: 'angular',
field: ['a', 'b', 'c', 'd'],
id: 1
}, {
name: 'asd',
field: ['a', 'b', 'c', 'd', 'e'],
id: 2
}];
I give select as :
<select ng-model="dashboard.type" ng-options="item as item.name for item in todos track by item.name"></select>
Now when an option is chosen i want another select which iterates upon ngModel of previous select. The next looks like :
<select ng-model="dashboard.label" ng-options="item as item for item in dashboard.type.field"></select>
It works fine when i choose the option manually but when their is a value in dashboard.type (eg: $scope.dashboard.type.name = 'qwe') in my script, it uses track by to choose the option and save it in dashboard.type, instead of saving the entire object it just saves the value used in track by option.
Value for dashboard.type:
when i choose option manually :
{"name":"qwe","field":["a","b","c","d","f"],"id":3}
When track by is used :
{"name":"qwe"}
NOTE: I cannot use track by item . It has to be a property of the object item. It can either be name or id.
Here's a plnkr.
Edit :
As pointed out by many, i would like to clarify that I can not initialise the object to any value of todos list. This is because dashboard.type.name value would be different every single time and todos list vary from 10 to 100's of objects. Now I have to iterate the list todos check if todos[index].name==someName and assign the relevant object. I am actively trying to avoid this soution (because there has to be a better way).
NOTE : I also tried using ng-change and assign the actual object to dashboard.type.name but that works only when option is selected manually ,which already seems to be working fine. It does not assign the object when track by is used.
IMPORTANT: $scope.dashboard.type.name = 'qwe' is just an example. Value of $scope.dashboard.type.name may change every single time the page is loaded.
PS: Also i should have mentioned this earlier.Sorry, My bad!
This issue is not because of track by. Setting a default value by doing $scope.dashboard.type.name = 'qwe', causes ng-model to look like $scope.dashboard.type = {name : 'qwe'}. Instead of selecting the default value this way, you should use $scope.dashboard.type = $scope.todos[2];. This will make sure the entire object is set to $scope.dashboard.type.
Edited plunker
To initialize the modal by a default value you can use ng-init. For example
<select ng-init="dashboard.type = todos[2]" ng-model="dashboard.type" ng-options="item as item.name for item in todos track by item.name">
</select>
call this function on ng-init
in html
ng-init ="initialize();"
in js
$scope.dashboard={};//initialize object
$scope.dashboard.type ={}//initialize object
$scope.initialize =function(){
//no need to iterate if it is certain that your Id and index is always same suppose your initial id is 3
//then
var currId =3;//suppose initial id is there
$scope.dashboard.type =$scope.todos[currId]
}
here is codepen for it
http://codepen.io/vkvicky-vasudev/pen/MeexmK

Default value with ng-options after filter

I have a <select> box with options driven from a filter, based on the value of another field in the model (another <select> field). If, after filtering the list, there's only one option to display, I want to make it the selected option. ng-init seems like a starting point, but it's also recommended by the Angular team as something to not use.
SELECT:
<select
tabindex="5"
class="form-control"
ng-model="vm.transaction.um"
ng-options="um.name for um in vm.ums | measuresForItem:vm.transaction.item">
</select>
Basically what you want to do is to change some model value (vm.transaction.um) based on some other model value (filtered vm.ums). There is $watch() function that does exactly this thing. Try
$scope.$watch('(vm.ums | measuresForItem:vm.transaction.item).length', function(newVal) {
if (newVal == 1)
$scope.vm.transaction.um = (<get filtered result here>)[0].name;
});
Actually ng-init is not that bad for such sort of tasks, but it executes only once, and it might be not suitable for dynamic filters or any kind of deferring.
Just set vm.transaction.um (the member you assigned to ng-model) in your controller:
if(this.vm.ums.length === 1) {
this.vm.transaction.um = this.vm.ums[0];
}

ng-model and ng-options not matching up?

I have a method in my resources object that comes in as:
resources.type
otherstuff: 'more strings'
type:'specifictype'
morestuff: 'morestuff'
The user can change this type with a dropdown / through another call that gets a list of all possible types which looks like resourceList.types which has a list of objects like this json
types:
[
{name:'resourcetype1'},
{name:'resourcetype2'},
etc...
],
my html looks like:
<select ng-model="resources.type" ng-options="name.name for name in resourceList.types">
</select>
The select/drop down box populates with my resourceList.type stuff but when the page loads the ng-model doesn't set to the already selected resource type. It actually selects a blank entry at the top of the drop down when you click. Is angular picky about this? How can I get the ng-model to behave the way I want it to?
I tried messing around with ng-options with the different ways of getting the repeat but I feel like this is more how angular connects the model. Does it just match the string to the ng-options list?
Here's the plnkr as you can see it's not defaulting to type1
http://plnkr.co/edit/NyWACtFQuyndR6CG8lpN?p=info
In Angular, the model is the single source of truth.
This means that if you want a value selected (and bound to your ngModel) you need to assign it to the model:
<select ng-model="resources.type"
ng-options="type.name as type.name for type in resourceList.types">
</select>
$scope.resources = {...};
$scope.resourceList = {
...
types: [
{name: 'resourcetype1'},
{name: 'resourcetype2'},
...
]
};
// Set the model to the desired value
$scope.resources.type = $scope.resourceList.types[0].name;
See, also, this short demo.
You don't have to set your model's value to the reference object in resourceList. In fact, the accepted answer works fine without this line:
$scope.resources.type = $scope.resourceList.types[0].name;
How is it working? Thanks to the "as" notation in the ngOptions. Without the "as", the match is made on the full type element, which is an object, so the match is made on the reference's object, not the name's value.
With the "as" the match is made on the element's property, name.
I've forked the plunker: http://plnkr.co/edit/kORfxGdsWBUlFWHXp6Ry?p=preview
in my case it didnt work since ngOptions was an array of integers and i was trying to set ngModal to string type (2the year 2014).
the solution is simple: parseInt function

Can't get Angular-xeditable Editable-Select to Select the current item when it is bound to a nested JSON object

I can't get the Angular-xeditable Editable-Select to Select the current item when it is bound to a nested JSON object. Data all saves fine, but it just doesn't show up the currently selected item in the Select box, which is driving me nuts, as I am sure I am missing something obvious. I have created a JSFiddle here:
http://jsfiddle.net/NfPcH/1031/
This is the code:
<span editable-select="data.organisation.OrganisationType"
e-ng-options="type.Name for type in data.types">
{{data.organisation.OrganisationType.Name}}
</span>
These are the 2 data objects:
$scope.data.organisation = {
"Id":1,
"Name":"My Organisation",
"OrganisationType":{"Id":2,"Name":"Internal"}
}
$scope.data.types = [
{"Id":1,"Name":"Client"},
{"Id":2,"Name":"Internal"},
{"Id":3,"Name":"Cold"}
]
It works fine when I bind it to the ID within the Nested Object, but then it only changes the Id in my nested object, and I then have to manually filter and change the Name part of the object, which works, but I am sure there must be a better way.
When the item is selected $data is loaded with the id of the item selected. You can use this to retrieve the value after the selection is made.
<span editable-select="data.organisation.OrganisationType"
e-ng-options="type.Name for type in data.types">
{{ findTypeName($data) || data.organisation.OrganisationType.Name}}
</span>
You can then define findTypeName in your controller
$scope.findTypeName = function(id) {
var found = $scope.data.types.filter(function(t){
return (t.Id === id);
});
return found.length ? found[0].Name : null;
};
The trick resides on using the object on the select's ng-options (e.g. 'obj as obj.title') and not a property of the object, so that when an item gets selected, it will assign the whole object to the model and not the property.
The problem with this approach is, that you cannot just compare objects (because comparing an object always compares their reference, not the "contents", so that obj1 == obj2 will never be true, unless they are exact the same object), so the other trick needed here is to use track by <some-id-property> on the ng-options. So your editable would look like this:
<span editable-select="data.organisation.OrganisationType"
e-ng-options="type as type.Name for type in data.types track by type.Id">
{{data.organisation.OrganisationType.Name}}
</span>
Beware that on your JSFiddle you're using a very old version of angular (where track by is not supported), so I created a new one using 1.2, which is the oldest angular version supporting track by:
http://jsfiddle.net/NBhn4/170/

How to bind a model to an option's value

In Angular 1.2.11, given a controller with:
$scope.produce = { 'apples' : [{ 'variety' : 'granny smith', 'price': 2.00}] }
And a view with
<select ng-model='fruits'>
<option value="0">More Fruits...</option>
<option ng-repeat="p.apple for p in produce" value="p">{{ p.variety }}</option>
</select>
How can I bind 'p' to the value attribute? If I try as above the model is bound as a JSON string, not as the model itself (as it would be if I were able to use ng-options).
I'm working around this now by using an ng-change function that uses the value bound to fruits as a lookup in $scope.produce, but this seems like a workaround rather than a solution.
EDIT: Sorry I wasn't more clear about this: I need to be able to hard code 1+ options into the select list, so using ng-options is not a solution.
Here are some fiddles to help illustrate my problem:
** Using ng-options **
ng-options allows for one element that it uses for creating a default option for use as a label. Two problems with this: if I try to use that <option> for its intended purpose I can't add a second option to the select. On the other hand, if I just try to add one <option> element, it gets placed at the top of the <select> list. No good.
** Using ng-repeat **
I'm unable to bind the model to the value attribute of the <option> element. Instead, the value is stringified JSON, which is not bindable.
You need to use ng-options:
<select ng-model='fruits' ng-options="thisFruit.variety in apples">
</select>
If you need to have an option selected as a default (your "More Fruits" option) then you need to add that to the data before it gets to the repeater, and also set your model $scope.fruits = 0 to make that selection selected by default.

Resources