ng-model and ng-options not matching up? - angularjs

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

Related

AngularJS select: remove empty option and use data objects not arrays

I've been searching high and low for an explanation as to how to remove the empty option AngularJS always renders in selects. I have found lots of information doing this when the options are derived from a JSON array placed directly in the code, but I can't find anything about removing this empty option when working with data objects.
Say I have object "foo" in my database and "foo" has both a "name" and a "bar_id".
Can someone toss me a clue about doing this with data objects?
References:
Angular JS Remove Blank option from Select Option
http://jsfiddle.net/MTfRD/3/ (This is someone else's fiddle from the SO question linked above; code is not mine but taken from that fiddle.)
JS:
function MyCtrl($scope) {
$scope.typeOptions = [
{ name: 'Feature', value: 'feature' },
{ name: 'Bug', value: 'bug' },
{ name: 'Enhancement', value: 'enhancement' }
];
$scope.form = {type : $scope.typeOptions[0].value};
}
I want to get foo.name and foo.bar_id. I want foo.name to be the option label and foo.bar_id to be the value (e.g. <option value="foo.bar_id">foo.name</option>. For each foo, foo.bar_id will then become an identifying parameter in the record to pull up and display "on change" (immediately upon selection of the option).
I've tried everything I can think of to set this up and nothing seems to work. Worst of all it either fails silently, leaving me with no indication what I am doing wrong, or complains that foo is not an object, which frustrates me to no end. (I assure you, "foo" IS being loaded by an AJAX request in the actual code I'm working on and works fine as an object everywhere else -- NDAs prohibit me from sharing actual code though, sorry.)
Using the above example from the other thread, I understand I would assign something like ng-model="typeOptions" in the select tag within the template, but how would I get "typeOptions" to access foo and make foo.name the option label and foo.bar_id the option value? Also, default angular values for the options (the ones that look like a generated index) are fine with me, but I still need to call upon foo.bar_id to get the task done, so it has to be "in there somewhere".
Any help would be appreciated; currently I'm stuck with the hackish ng-repeat in the option itself, which I understand is not best practice.. Also I'm still fairly new at Angular, and still find it somewhat confusing, so the simpler and more straightforward your answers, the better I will be able to use them successfully. Thanks!
update
Ok, I just now understood what your real question was (at least I hope so ;)). And fortunately this is very easy:
Ctrl:
$scope.options = {
a: 1,
b: 2,
c: 3
};
$scope.selected = 1;
View:
<select
data-ng-model="selected"
data-ng-options="v as k for (k, v) in options"
></select>
Demo: http://jsbin.com/hufehidotuca/1/
for more options, have a look at the manual. Especially under:
ngOptions (optional) ยป for object data sources:
previous answer:
As I wrote in the comments, with ngOptions:
you can't force angular to use your model-data as the actual option-value in the view. Angular handles this in it's own way. It only assures that your scope-model will be set to the correct value on change. If that's not what you want, then you'll have to write your own directive, probably using ngRepeat.
you'll always need two seperate models. One which acts as the list of options (this should probably always be an array of objects) and one to store the selected value (this will then differ in regards to how you set up the ngOptions directive).
Hint: The notorious blank options is always the result when angular can't match the assigned ngModel to the list of options and no real blank option is setup. (Note: if the ngModel is just undefined in the current scope or it's value can't be matched against the list of options, then it will be set or it's value will be overridden, with the first selection of any option. This is why the blank option disappears afterwards.)
Ctrl:
// the "list of options"
$scope.options = [
{ name: 'A', id: 1 },
{ name: 'B', id: 2 },
{ name: 'C', id: 3 },
{ name: 'D', id: 4 }
];
// a pre-selection by value
$scope.asValue = 2;
// a pre-selection by object-identity
$scope.asObject = $scope.options[3];
View:
<!-- select as value -->
<select
data-ng-model="asValue"
data-ng-options="foo.id as foo.name for foo in options"
></select>
<!-- select as object -->
<select
data-ng-model="asObject"
data-ng-options="foo as foo.name for foo in options"
></select>
<!-- the notorious blank option -->
<select
data-ng-model="asBogus"
data-ng-options="foo as foo.name for foo in options"
></select>
<!-- blank option correctly set up -->
<select
data-ng-model="asBogus"
data-ng-options="foo as foo.name for foo in options"
>
<option value="">Please select</option>
</select>
demo:
http://jsbin.com/yasodacomadu/1/
Just posting as a follow up to the whole data object issue ...
To make this work, I had to get the ng-options properly designated first of all. I've mentioned elsewhere that one of my recurring personal issues learning AngularJS is "overthinking" things and confusing myself unnecessarily, and this was no exception. After conferring with a co-worker who is scripting the controller on this app, using my "foo.bar" pseudo-designations, this is how I needed to set up the HTML template:
<select ng-model="selected_item" ng-options="bar.item_name for bar in foo.bars">
I wanted this select to be set to the "bar" of the current display (as opposed to the first indexed "bar" in the complete list of "bars" this "foo" has), so I had to locate the place in the app controller where this data object is being passed in and add for the model (which I will call $selected_item):
$scope.selected_item = _.find($scope.foo.bars, function(bar) {
return bar.item_id == $scope.current_result.foo_item_id;
});
It's a pretty complicated data structure with a lot of stored procedures and concatenated tables, so my bad for not being able to translate it easily into a working pseudo-object/properties example. Yoshi you have been very helpful; the gaps in communication here have all been my baddies. Thank you for sticking with me.
For other confused AngularJS n00bs who might stumble upon this thread: the place I was "overthinking" it and confusing myself was by mistakenly believing that I needed some sort of code in place of the JSON arrays the many examples I studied, to pass in the data object. That is incorrect -- "nothing" needs to go there with regard to passing in a data object and/or its properties. The data object's properties are passed directly into the template, within the select tag itself, using ng-options. So if anyone else finds themselves stumped by this, you're probably making the same two mistakes I made: (1) thinking "something else" should replace the JSON arrays examples online are using and (2) your ng-options are not written correctly.
Angular sure makes me feel stupid sometimes!! It's fun and challenging but sometimes it just leaves me feeling like an idiot. :-)
<select ng-model="form.type" required="required"
ng-options="option.value as option.name for option in typeOptions" >
<option style="display:none" value="">select a type</option>
</select>
While Using Css we removing the Empty Option like
<option value="?undefind"></option>
So While Usiing Css we can remove the emptyy Options.
Shown in this fiddle
http://jsfiddle.net/KN9xx/1060/

AngularJS: Setting selected option in dropdown

I'm assigning an array of 'types' to a dropdown. When a user selects a value in the dropdown, I save it off to a cookie.
The code where I'm updating the ng-model:
$scope.typeItem = $cookieStore.get('typeItem');
This is the dropdown itself:
<select class="transmission-option-width" ng-model="typeItem"
ng-options="t as t.Type for t in transmissionTypes" ng-change="update()"></select>
I set a break point, and $scope.typeItem has a value, but the select is not being set. Any idea what I'm doing wrong here?
The object you're getting back from the cookie store...
$scope.typeItem = $cookieStore.get('typeItem');
while it might have the same properties as one of the items in $scope.transmissionTypes, it's actually an entirely different object. Because angular does the comparison by reference, it can't find a matching object in $scope.transmissionTypes and the dropdown is not set.
Is t.Type a numeric value? That might prevent angular from treating the selected value as equal to the option value.
Might be easiest to convert $scope.typeItem to a numeric value after getting it from the $cookieStore.
i think you misted this part of the docs
Note: ngModel compares by reference, not value. This is important when binding to an array of objects. See an example in this jsfiddle.
try to traverse your array and assign the element in the array holding the same value to your model variable

AngularJS - connect ng-options to an object named in a property

I'm working on a form generator implementation, and I want to have ng-options evaluate a property of the current 'field' and return the select 'options' as listed in something like $scope.options.
This means that I can setup the 'option' containing objects in my controller, and have the form elements use the correct object to populate the dropdown.
My form is built from a relational database, so hard coding the target object isn't going to help (although it does work, it's just not scalable)
I'm stuck figuring out how to get ng-options to evaluate an expression which makes the jump from variable to object name.
Maybe there's just a better way to do this altogether?
<select id="{{field.Name}}" ng-model="field.Value"ng-options="option.ID as option.Value for option in [SOMETHING THAT EVALUATES TO AN OBJECT NAMED IN THE BINDING PROPERTY]" ng-required="field.Required"></select>
I have a fiddle here: JSFiddle
I would make an object containing objects of your options
$scope.optiongroups = {group1: [...], group2: [...]}
and then
ng-options="value as option for option in optiongroups[dynamic var here]"

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