Update ngmodel from Select - angularjs

We're attempting to display a customer's information based upon the contact they select. When we use ng-options it works as expected. We are, however, using Ui.Select2 and "ui-select2 is incompatible with ." So we are forced to use ng-repeat and it isn't working as expected.
JS:
var app = angular.module('App', []);
app.controller('Ctrl', function($scope){
$scope.contacts =
[ {
'CntctKey': '331518',
'Name': 'Cheryl',
'Phone': '55555115',
'PrimaryCntct': '0'
} ,
{
'CntctKey': '118431',
'EmailAddr': 'pizzamaybe#gmail.com',
'Name': "Stephanie Pizza",
'Phone': '555552255',
'PrimaryCntct': '1'
} ];
});
HTML
<h3>With ng-options</h3>
<select ng-model="contactedPerson" ng-options="contact.Name for contact in contacts"></select>
<input ng-model="contactedPerson.Phone"/>
<input ng-model="contactedPerson.CntctKey"/>
<br>
<h3>With ng-repeat</h3>
<select ng-model="contactPerson1" >
<option ng-repeat="contact in contacts" value="{{contact}}">{{contact.Name}}</option>
</select>
<input ng-model="contactPerson1"/>
<input ng-model="contactPerson1.Cntctkey"/>
http://plnkr.co/edit/9D9LTCnBOGAo2ftA1jbU
You can see that with ng-repeat it shows the object but we can't drill it into it any further.
Is there a good way to do this besides using ng-options? Thanks in advance!

The problem is that you are using value="{{contact}}"
This causes angular to print the object as a JSON string.
So when the value gets assigned from the ng-model in the select list, the variable is assigned a string value i.e.
"{"CntctKey":"331518","Name":"Cheryl","Phone":"55555115","PrimaryCntct":"0"}", rather than the object's itself.
This is why in your plnkr, your second field which has ng-model="contactPerson1.CntctKey" has no value, because for a string, this property is undefined.
to fix this you can just use the index of the array element to reference the object.
You can access this index by using ng-repeat's key mapping (contactKey, contact) in contacts.
Or if you are just storing everything in a flat, contiguous array (like in the example), just use ng-repeat's $index variable.
i.e.
<select ng-model="contactPerson1Index" >
<option ng-repeat="contact in contacts" value="{{$index}}">{{contact.Name}}</option>
</select>
<input ng-model="contacts[contactPerson1Index].Phone"/>
<input ng-model="contacts[contactPerson1Index].CntctKey"/>
See updated plnkr: http://plnkr.co/edit/4thQmsZamN3tt0xpscj9
for more info on key mapping and ng-repeats internal variables, see: https://docs.angularjs.org/api/ng/directive/ngRepeat

Related

AngularJS <select> ng-options selected value is not properly set

I have this pseudo code angularjs app. You can select a hobby for each person, save it and load it again.
However when I load the data, the select boxes dont have the correct option selected. person.hobby exists and is the correct object, but it looks like ng-model isn't set correctly.
Am I missing something?
<div ng-repeat="person in people">
<p>{{ person.fullname }}</p>
<select ng-model="person.hobby"
ng-options="h.hobby_name group by h.category for h in hobbies">
</select>
</div>
<script>
//... controller...
$http.get('/gethobbies/').succes(data) {
hobbies = data;
};
$http.get('/getpeople/').succes(data) {
people = data;
// looks like this: [{'fullname': 'bill gates', 'hobby': {'hobby_name': 'programming', 'category': 'computers'}, ...]
};
</script>
ng-model needs to be set to the exact same object as the one in the ng-options array that you want to be selected. Angular uses object references to figure out which one should be active so having a "hobby-object" with the same "hobby_name" as one of the objects in "hobbies" is not enough. It needs to be a reference to the same object.
See documentation for select for details

How to trigger binding of select based on the changed value in another select?

Let's say that I have list of countries and each country has a list of states/regions. So there are two selects. First to select country, and when country changes I want to trigger binding of the states select. How do you link these two controls to trigger binding of the states select when country changes?
<select id="countries"
data-ng-model="vm.permanentAddress.countryCode"
data-ng-options="country.code for country in vm.form.countries">
</select>
<select data-ng-model="vm.permanentAddress.stateCode"
data-ng-options="state.value for state in vm.getStatesForCountry(vm.permamentAddress.countryCode)">
</select>
UPDATE:
I was probably not explicit in my question as to what I want to do. I do not want to create any new properties that are then watched by angular for a change. I just want to tell anuglar, hey something has changed, go ahead and re-evaluate the binding for this control.
Is it not possible?
In your controller have something like this:
$scope.setStateOptions = function(country){
$scope.stateOptions = /* whatever code you use to get the states */
}
Then your html can be:
<select id="countries"
data-ng-model="vm.permanentAddress.countryCode"
data-ng-options="country.code for country in vm.form.countries"
data-ng-change="setStateOptions(country)">
</select>
<select
data-ng-model="vm.permanentAddress.stateCode"
data-ng-options="state.value for state in stateOptions">
</select>
May be you should use the jquery chanied select plugin:
http://jquery-plugins.net/chained-selects-jquery-plugin
I have used it for 4 select list chained and it worked fine.
Thx
Here is a working example. You just need to use the ng-change to change the model you have set for the states
You can take advantage of the dynamic nature of JavaScript to bind the key from the first list to the second list. Then you only have to set a default value on the change. If you remove the $watch it will still work, the second select will just default to empty when you switch the category.
Here's my data set-up and watch:
app.controller("myController", ['$scope', function($scope) {
$scope.data = ['shapes', 'colors', 'sizes'];
$scope.data.shapes = ['square', 'circle', 'ellipse'];
$scope.data.colors = ['red', 'green', 'blue'];
$scope.data.sizes = ['small', 'medium', 'large'];
$scope.category = 'colors';
$scope.$watch('category', function () {
$scope.item = $scope.data[$scope.category][0];
});
And here's the HTML:
<div ng-app="myApp" ng-controller="myController">
<select id="categories"
data-ng-model="category"
data-ng-options="category for category in data"></select>
<select id="item"
data-ng-model="item"
data-ng-options="item for item in data[category]"></select>
{{category}}: {{item}}</div>
You can, of course, change this to host complex objects and use keys or other identifiers to switch between the lists. The full fiddle is here: http://jsfiddle.net/jeremylikness/8QDNv/

AngularJS ng-repeat default value does not work

I am working with AngularJS and I am so new in that.
My aim is fill a tag SELECT with OPTION element from a datasource.
That is what I do basically:
<script>
function LocalizationController($scope, $http) {
$scope.Regions = [
{ ID: "001", DESC: "DESC_1" },
{ ID: "002", DESC: "DESC_2" },
{ ID: "003", DESC: "DESC_3" },
{ ID: "004", DESC: "DESC_4" },
];
$scope.Region = "003";
$scope.init = function () {
$scope.Region = "003";
}
}
</script>
<div ng-controller="LocalizationController" ng-init="init();">
<input type="button" ng-click="Region='002'" value="test">
<select id="region" name="RegionCode" ng-model="Region">
<option ng-repeat="item in Regions" value="{{item.ID}}">{{item.DESC}}</option>
</select>
</div>
Everything works good, the Select is fill of my items,
but I would like to set default value.
How you can see
- I set my object model which is ng-model="Region"
- I set its default value into init() function by $scope.Region = "003";
when the SELECT is loaded I do not why but dafault value is the first one "001"
I also tried to change value manually by
in that case the SELECT gets the right value selection.
Anyone can explain why it does not work?
I know that is a common problem, I am already looked for that,
any solution suggestion to use NG-OPTION, that directive works good,
it can fill my SELECT with my array of object, it can select default value,
very nice but it does not solve my problem because the value into the
the option element id an integer autoincrement which is not what I want.
so in summary:
NG-REPEAT can render the SELECT how I want but default value does not work
NG-OPTIONS can render the SELECT how I want, default value works but the value into option
item cannot be set how I want.
any suggestions?
thanks in advance
EDIT
I found a "solution"
<div ng-controller="LocalizationController" EXCLUDE-ng-init="init();">
<select id="region" onload="alert();angular.element(this).scope().Region='002'" name="RegionCode" ng-model="Region">
<option ng-repeat="item in Regions" value="{{item.ID}}">{{item.DESC}}</option>
{{init();}}
</select>
</div>
I do not like so much but works pretty good:
- ng-init="init();" does not work good
- If we exclude the initialize of default value into ng-init so EXCLUDE-ng-init="init();" and put the initilize when option are loded it works
Have a look at the code blow, I put {{init();}} after ng-repeat. Everithing works good
{{item.DESC}}
{{init();}}
I do not think is the best solution but Works,
1) I have a droopdown fill of my elements
2) The value of option into my list is CORRECT, "001", "002" and not a stupid autoincremental value which is useless.
I hope that can help someone...
THANKS TO EVERYONE TRIED TO HELP ME
Just use ng-options
<select id="region" name="RegionCode" ng-model="Region" ng-options="option.ID as option.DESC for option in Regions">
And do
$scope.init = function () {
$scope.Region = $scope.Regions[2];
}
When you inspect the select box while using ng-options the values for the options will be like 0,1,2,3.., that is auto increment integer as you said. But don't worry about that. Its the value just for showing up there, but once you post your form you will get the right value as you possess, in your case ID.
use ng-options
and in this case if you play with object (not single values) it should be so
$scope.init = function () {
$scope.Region = $scope.Regions[2];
}

how to set the default 'select' ng-model to an object?

I am trying to do a search engine interface with angular. The user selects some parameters in a form, click "search" and then it fills the url with the parameters using $location.search()
the search interface parameters which are used to build the form :
params = {
milestones: [ "a", "b", "c", "d", etc. ],
properties: [
{ "name": "name A", type: "text" },
{ "name": "name B", type: "checkbox" },
{ etc. }
]
}
inside the controller :
$scope.query = $location.search(); // get the parameters from the url
$scope.search = function (query) { // set the parameters to the url
$location.search(query);
};
and the html of the form
<select ng-model="query.milestone_name" ng-options="ms for ms in params.milestones">
<option value="">-select milestone-</option>
</select>
<select ng-model="property" ng-options="prop.name for prop in params.properties" ng-change="query.property_name=property.name">
<!-- if the object 'property' was passed in the url, it would look like this `%5Bobject%20Object%5D`, so its 'name' parameter is converted to a string -->
<option value="">-select property-</option>
</select>
<span ng-switch="property.type">
<label ng-switch-when="text">{{query.property_name}}: <input type="text" ng-model="query.property_value"></label>
<label ng-switch-when="checkbox">{{query.property_name}}: <input type="checkbox" ng-model="query.property_value"></label>
</span>
<button ng-click="search(query)">search</button>
and somewhere else in the page is the list of results.
the user can also access to a search result page with an url like this:
http://myapp.com/search?milestone_name=a&property_name=name%20A
almost everything works fine : the list of results is displayed, the "milestone" parameter is pre-selected with the correct value in the select component, but not the "property" parameter because it's not a string, it's an object.
how can i set the default value (ng-model) of the select component to an object ?
or any other idea on how i should do this ?
When using an array of objects for the select to iterate over, the ng-options directive needs to have an attribute of the object to match against (and differentiate between arrays)
Use the track by section of the directive declaration eg
<select ng-model="property" ng-options="prop.name for prop in params.properties track by prop.name" ng-change="query.property_name=property.name">
<!-- if the object 'property' was passed in the url, it would look like this `%5Bobject%20Object%5D`, so its 'name' parameter is converted to a string -->
<option value="">-select property-</option>
</select>
You can use this form of comprehension expression in ngOptions: label group by group for value in array. Html drop down list will display only name of selected object. Model will contain whole selected object. You can set selected object from controller.
<select ng-model="property"
ng-options="prop as prop.name for prop in params.properties">
</select>
Check this plnkr example.
ng-options is generating some options to be used with ng-model. In your syntax (prop.name for prop in params.properties) you've told it to bind to the object found in the array (as oppose to a property on it - which is what you want to do) and use its name property as the value to display. So when you try and set the ng-model to be an object that's not in the ng-options array nothing happens - I'm guessing because it's using reference/shallow equality and not deep equality. So what you should do is either:
Convert the ng-options object to be an array of strings.
Use a syntax that involves keys, such as:
prop.name as prop.name for prop in params.properties
http://jsfiddle.net/C5ENK/
If that doesn't suit your needs let me know why and I'll see if I can help further.
i found a kind of solution…
when selecting a property, it saves the index of this object and then when the page loads, it sets the ng-model of the select to the value of this index. it uses this : example for setting the index and this example for getting the value of this index in the array of objects.

Use checkboxes in angularjs to manage array of objects

I had asked a question about
How to associate objects as models using ng-options in angularjs.
And I got an awesome answer very fast. My followup questions is that the response uses <select mutiple> to handle the child object array.
You can see a working example of what I want, working with <select> at http://plnkr.co/edit/FQQxrSE89iY1BnfumK0A?p=preview
How can I use <input type='checkbox'> (instead of <select>) to handle that object array i.e. ng:model="shirt.colors" while repeating the items from colors object array.
The reason, this appears so complicated to me is that I have to manage an array of objects instead of array of values... for example, if you look in the fiddle, there are color objects and shirt object that has multiple colors.
If the color object changes, it should change the corresponding color objects in shirt objects.
Thank you in advance.
You just need some intermediate value in your scope, and bind checkboxes to it. In your controller - watch for it changes, and manually reconstruct shirt.colors, according to it value.
<div ng-repeat='shirt in shirts'>
<h3>Shirt.</h3>
<label>Size: <input ng-model='shirt.size'></label><br/>
<label>Colors:</label>
<label ng-repeat="color in colors">
{{color.label}} <input ng-model="selection[$parent.$index][$index]" type="checkbox"/>
</label>
</label>
</div>
And in your controller:
$scope.selection = [[],[]];
$scope.$watch('selection', function () {
console.log('change', $scope.selection);
angular.forEach($scope.selection, function (shirtSelection, index) {
$scope.shirts[index].colors = [];
angular.forEach(shirtSelection, function (value, index2) {
if (value) $scope.shirts[index].colors.push($scope.colors[index2]);
});
});
}, true);
You can test it here: http://plnkr.co/edit/lh9hTa9wM5fkh3nT09RJ?p=preview

Resources