I have a dropdown list:
<select
ng-model="filter.country"
ng-options="country.code as country.name for country in countries"
ng-change="broadcast()">
<option value="">All Countries</option>
</select>
$scope.countries is initially populated by a service, and then another dropdown change event would limit the values of $scope.countries by calling the service again, passing by the other dropdown's selected item.
The issue here is when $scope.filter.country is already bound to a value (other than the default value) and $scope.countries gets updated to a new list that doesn't include $scope.filter.country's value. I can see countries dropdown reverting back to its default option "All Countries", however $scope.filter.country remains as it was.
Any ideas about this scenario? Shouldn't $scope.filter.country get updated back to the default value?
Update: Here is a fiddle
Update:
Just to illustrate this, here is a screenshot from the fiddle:
This does look like a bug to me, I have opened an issue for it.
Update: This has been addressed and fixed by angularjs team; demo here.
The behavior you see is exactly how double-binding should work:
Most developers have a "feel" for double-binding more than an understanding. The "feeling" is that any visual change to a model-bound input results in a model change.
But any developers who have tried mode-binding a JQuery date picker will attest to this phenomenon: The JQuery Date-Picker will not update an angular model bound to same input. In other words, picking a date through JQuery does not register with any AngularJS events.
Similarly, pasting values into model-bound inputs usually does not result in the model being updated.
Analyzing your case:
You have changed the list of options for a model bound drop-down. As a result, the displayed option reverts back to default --> because the model-bound value is not a relevant choice.
Angular's ng-model doesn't care about the "options list" for your drop-down. The framework is waiting for some event that will cause it to run through the digest cycle and absorb a change of value picked. Even though something has "appeared" to change, this is an illusion or loop-hole depending on how you see it.
For a drop-down (select element), a change event is a "click" on an option.
I had issues with that in the past. It took me a good while to fix.
Try replacing your select by:
<select ng-model="changeMe" ng-options="d as d for d in dummyValues" ng-change="changeMeChange(); filter.country = undefined">
<option value="">Change Me</option>
</select>
adding the "filter.country = undefined" to your ng-change should reset the value of your model.
Hopefully that will sove your problem.
If the contents of the countries select is independent of the value of changeMe, then two-way binding won't help us here. Instead, we have to make the change manually as follows:
$scope.changeMeChange = function () {
// this or a $scope.$watch for independent values in the changeMe drop down menu
if ($scope.filter) {
$scope.filter.country = null;
}
$scope.countries = [
{
"code": "MX",
"name": "Mexico"
}
];
};
If the the values in the changeMe select can be used to filter the country select and thus make the country select dependent upon the change me select, then we can use a filter as follows:
'use strict';
var myApp = angular.module('myApp', []);
function FilterCtrl($scope) {
$scope.countries = [
{
"code": "KW",
"name": "Kuwait"
},
{
"code": "US",
"name": "USA"
},
{
"code": "MX",
"name": "Mexico"
}
];
$scope.dummyValues = ['MX'];
$scope.changeMeChange = function() {
$scope.countries = [
{
"code": "MX",
"name": "Mexico"
}
];
};
};
<select ng-model="changeMe" ng-options="d as d for d in dummyValues" ng-change="changeMeChange()">
<option value="">Change Me</option>
</select>
<select ng-model="filter.country" ng-options="country.code as country.name for country in countries | filter: { code: changeMe} ">
<option value="">All Countries</option>
</select>
Updated Fiddle using the filter approach as it seems the more likely use case. If you want your users to have access to the complete list of countries if no change me option has been selected, then a Custom Filter is the way to go.
Hope this helps.
The ng-model is binded to the scope variable called filter.country. When you update the ng-options values, it doesn't trigger the change that would update filter.country. It's a separate and unrelated variable at this point. So, there is no implicit bound connection. Only on selection of the list, the selection field is updated.
Though you can easily reset the select model from where you call the service or include a $watch on the options to reset the ngmodel value.
$scope.$watch('countries', function() {
//reset the model here
});
<table>
<tr ng-init="states()">
<td>State</td>
<td><select ng-model="selectedState" data-ng-change="cities1()" data-ng-options="state.Id as state.Name for state in states" /></td>
</tr>
<tr>
<td>City</td>
<td><select ng-model="selectedCity" data-ng-options="city as city for city in cities" /></td>
</tr>
</table>
.JS file contents -
$scope.cities = function () {
$scope.working = true;
$scope.title = "Loading Cities...";
$scope.cities = [];
$scope.queryDisabled = true;
var stateId = $scope.selectedState;
if (stateId) {
$http.get("/api/city", { params: { "stateId": $scope.selectedState } }).success(function (data, status, header, config) {
$scope.cities = data;
$scope.title = "Displaying Cities...";
$scope.working = false;
$scope.queryDisabled = false;
}).error(function (data, status, headers, config) {
$scope.title = data + status;
$scope.working = false;
$scope.queryDisabled = false;
});
}
else {
$scope.cities = [];
}
};
$scope.$watch($scope.selectedState, function () {
$scope.cities1 = $scope.cities;
});
$scope.cities1 = $scope.cities;
I don't know the reason why it is so and how it works, but I have to create a copy of the cities variable as cities1. Assign cities1 from cities iself, then use cities1 on State combo ng-change event and cities on City combo ng-model directive.
ng-click doesn't seems the right replace of ng-change. Child combobox contents should change when selected item is changed in Parent (State) combo, and not when someone just click on Parent combo.
Related
My markup looks something like this
<select ng-model="search.parameters.selectedOptionId" ng-options="lookup.id as lookup.lookupValue for lookup in lookups.options" custom-attribute-directive>
<option value="" selected>All</option>
</select>
From the customAttributeDirective, I need to access the selected option value that is displayed in the dropdown (i.e. lookup.lookupValue). I've tried accessing the $viewValue on the ngModel, but it is set to the lookup.id (which I assume is the fault of the way ng-options is set up). I cannot modify the implementation of the markup due to the circumstances that I am implementing the directive against, so the problem must be solved there.
Bind the lookup.lookupValue for display and lookup.lookupValue for backend Id too. In that case you will get display text with ng-model
<select ng-model="search.parameters.selectedOptionId" ng-options="lookup.lookupValue as lookup.lookupValue for lookup in lookups.options" custom-attribute-directive>
<option value="" selected>All</option>
Found my answer. It turns out directives can access the dom directly.
link: function (scope, element, attrs, controller) {
scope.getViewValue = function () {
if (angular.isDefined(scope.overrideTagDisplay)) {
return scope.overrideTagDisplay;
}
else if (element[0].nodeName == "SELECT") {
return element.children()[element[0].selectedIndex].text;
}
else {
return ngModel.$viewValue;
}
}
scope.initialize()
}
EDIT: I found later that this method has the issue that if javascript modifies the model value somewhere else in the code and then this piece immediately runs, it does not pick up the new value because the DOM hasn't updated (this includes on page load and initial setting). I ended up passing the list of lookups into the directive as well and got around the lack of standardization in those lookups by having an optional "comparator key" that would defaultly look something like
var comparitorKey = { id: "id", lookupValue: "lookupValue"};
which is then utilized like this
var id = ngModel.$modelValue;
var filtered = scope.lookupList.filter(function (lookup) { return lookup[scope.comparitorKey.id] == id });
return filtered.length > 0 ? filtered[0][scope.comparitorKey.lookupValue] : null;
the idea being that someone could pass in an alternative like
var comparitorKey = {id: "userId", lookupValue: "userName" };
How to update new value choosing from select box on click save button.
I am using ng-click function like this in my JS function for update button:
$scope.updateDealDetail = function updateDealDetail (){
$scope.showEditView = !$scope.showEditView;
$scope.dealDetail.decisionMakerDetail.email = $scope.selectedid;
}
My function for edit button:
$scope.editUserDetail = function editUserDetail(){
$scope.showEditView = !$scope.showEditView;
$scope.showSubmitView = !$scope.showSubmitView;
deal.getIdData($scope.accountDetail. accountUsers[0].role,$scope.accountDetail.id).then(function successCb(data){
$scope.editIdOptionsData=data;
$scope.selectedid = $scope.editIdOptionsData[0].email;
});
};
and my HTML for bitton click is like this :
<select ng-model="selectedid" class="form-control">
<option ng-selected="selectedid" ng-repeat="eiod in editIdOptionsData" value="{{eiod.email}}">{{eiod.email}}
<button ng-click="updateDealDetail(eoid.email)" ng-disabled="dealDataSaveButtonDisabled">Update</button>
I am trying to this through ng-repeat because of by using ng-options my data through API is now showing in the box. But My data which is on first index is only getting set. What to do to set the a default value for the selection box and by selection any value, onclick need to update that value.
Don't use ngRepeat to render options, this is your problem. Correct code would be:
<select class="form-control"
ng-model="selectedid"
ng-options="eiod.email as eiod.email for eiod in editIdOptionsData">
</select>
Best practice to use ng-options, ng-model and ng-change on the <select> element
Online demo - https://plnkr.co/edit/Gaa8sMgerRv6chio4iYa?p=preview
html
<select
ng-model="selectedItem"
ng-change="onSelectedItemChanged()"
ng-options="item as item.email for item in items"></select>
js
app.controller('MainCtrl', function($scope) {
$scope.items = [{
email: 'asd1#asd.com'
}, {
email: 'asd2#asd.com'
}];
$scope.selectedItem = $scope.items[0];
$scope.onSelectedItemChanged = function() {
alert('you changed it to ' + $scope.selectedItem.email);
}
});
I have the following in my view
<div>
<select ng-model="obj.arr[otherObj.variable]" ng-change="otherObj.variable=SOMETHING">
<option ng-repeat="label in obj.arrs">{{label}}</option>
</select>
</div>
Without the ng-change attribute, this code does what I want when otherObj.variable is one of the indexes of the obj.arr - it selects the correct item in the list.
What I want in addition to this is to set otherObj.variable to the index of the array item that is picked when the dropdown variable is changed. So, if the second value in the dropdown is picked then otherObj.variable should be set to 1. I tried to do this with a
ng-change="otherObj.variable=SOMETHING"
Problem is., I don't know what that SOMETHING should be. Am I doing this right?
EDIT
My requirements are
Select the top option in the dropdown by default
select the appropriate item in the array depending on the value of otherObj.variable (this gets set by some external code so if I come to the page with this value set then I want the correct option selected)
Make sure otherObj.variable is updated if I change the value in the dropdown.
angular.module('selects.demo', [])
.controller('SelectCtrl', function($scope){
$scope.values = [{
id: 1,
label: 'aLabel',
}, {
id: 2,
label: 'bLabel',
}];
$scope.selectedval = $scope.values[0];
});
<script src="https://code.angularjs.org/1.3.15/angular.js"></script>
<div ng-app="selects.demo">
<div ng-controller="SelectCtrl">
<p>Using ngOptions without select as:</p>
<select ng-model="selectedval" ng-options="value.label for value in values"></select>
<p>{{selectedval}}</p>
<p>Using ngOptions with select as statement: (this will return just the id in the model)</p>
<select ng-model="selectedval2" ng-options="value.id as value.label for value in values"></select>
<p>{{selectedval2}}</p>
</div>
</div>
Sorry if my comment was a little cryptic. Select elements like other form elements are actually directives in AngularJS, so they do a lot of stuff for you automatically. You don't need to use an ngChange to populate the ngModel associated with your select element. AngularJS will handle that for you.
Also, you can use ngOptions instead of ngRepeat on select elements to generate the values automatically on options.
Assuming that you have an object with values:
$scope.values = [{
id: 1,
label: 'aLabel',
}, {
id: 2,
label: 'bLabel',
}];
You would write:
<select ng-model="selectedval" ng-options="value.label for value in values"></select>
Now your ngModel is going to be bound to the selected element. It will be set with the value of the object that was chosen. If you add {{selectedval.id}} to your view, it will display the id of the selected element.
If you want to set the value to the first item, in your controller, you would add:
$scope.selectedval = $scope.values[0];
If you want to update some property on $scope.values based on the selected value, you could use something like:
$scope.addActiveProp = function() {
var selected = $scope.values.filter(function(e) { return e == $scope.selectedval; });
selected.active = true;
}
And then run the addActiveProp fn in ngChange on the select.
Please give a try with below code
<select ng-model="obj.arr[otherObj.variable]" ng-change="otherObj.variable=key" ng-options="key as value for (key , value) in obj.arrs"></select>
I have an angular app that contains a form, which has a select. I can choose one of the selects, and its updated in the model perfectly. But the next time i go in the form its suppose to be "pre filled" as the data already exists. And all the data is filled ok, but angular doesn't seem to recognise that the value of the model corresponds to the value of the select.
html/angular:
<select ng-model="userData.country"
ng-options="country as country.name for country in countries">
</select>
data
[
{"iso":"DK","name":"Danmark"},
{"iso":"DE","name":"Germany"}
]
The wanted behaviour is that once I set this data to userData.country, that the form will come pre-selected with the correct country that the user chose in the previous form.
In Your HTML you need to add country.iso instead of country
<select ng-model="userData.country"
ng-options="country.iso as country.name for country in countries">
</select>
And in your controller
$scope.userData = {"country":"DK"}; // this can be filled with prefilled data. which can be fetched from db or stored in a service
In case you want to save the whole object instead of just the iso code here is an example:
http://jsfiddle.net/2ES4t/
JS File:
$scope.countries = [{
iso: "DK",
name: "Danmark"
}, {
iso: "DE",
name: "Germany"
}];
$scope.userData = {};
$scope.userData.country = $scope.countries[0];
HTML File:
<select ng-model="userData.country" ng-options="country as country.name for country in countries"></select>
I would suggest storing the whole object, that you wouldn't have to search the country array later if you needed the name and only had the ISO code.
You probably are not filling the form in the Angular Way.
To do this, you should set the model (and the form will be pre-filled):
In your controller:
$scope.countries = [
{"iso":"DK","name":"Danmark"},
{"iso":"DE","name":"Germany"},
{"iso":"PT","name":"Portugal"},
{"iso":"CN","name":"Canada"}
];
$scope.userData = {};
$scope.userData.country = $scope.countries[3];
Fiddle
Try to change your html/angular to
<select ng-model="country"
ng-options="country.name for country in countries" ng-change="countryChanged(country)">
</select>
and your controller:
$scope.countryChanged = function(country){
$scope.userData.country = country
}
I am unable to update value of select from AngularJs.
Here is my code
<select ng-model="family.grade" >
<option ng-repeat="option in options" value='{{option.id}}'>{{option.text}}</option>
</select>
Here are the options which i am using to populate my select
var options = [{text:'Pre-K',id:'Pre-K'},
{text:'K',id:'K'},
{text:'1',id:'1'},
{text:'2',id:'2'},
{text:'3',id:'3'},
{text:'4',id:'4'},
{text:'5',id:'5'},
{text:'6',id:'6'},
{text:'7',id:'7'},
{text:'8',id:'8'},
{text:'+',id:'+'}];
Here is mu js code.
$scope.$watch("family_member.date_of_birth" ,function(newValue, oldValue){
$scope.family.grade = "1"
})
When ever value of family_member.date_of_birth changes it should set they value of select to 1. But this change is not visible on UI.
You should use ngSelected to select the option.
it could be something like this:
<select ng-model="family.grade" >
<option ng-repeat="option in options"
value='{{option.id}}' ng-selected="family.grade==option.id">
{{option.text}}</option>
</select>
Hope this helps.
I think you are looking for the track by clause of ng-options:
<option ng-repeat="option in options track by option.id">{{option.text}}</option>
However, you will still need to supply an object with an id property to set:
$scope.$watch("family_member.date_of_birth" ,function(newValue, oldValue){
$scope.family.grade = { id: "1" }
})
The options array indiviual elements are objects. So the respective ng-model also need to be an object. So even when it is being changed in js, the respective object has to be provided rather than a string.
Sample demo: http://plnkr.co/edit/naYcnID29SPa90co0leB?p=preview
HTML:
JS:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.options = [{text:'Pre-K',id:'Pre-K'},
{text:'K',id:'K'},
{text:'1',id:'1'},
{text:'2',id:'2'},
{text:'3',id:'3'},
{text:'4',id:'4'},
{text:'5',id:'5'},
{text:'6',id:'6'},
{text:'7',id:'7'},
{text:'8',id:'8'},
{text:'+',id:'+'}];
$scope.family = {};
$scope.family_member = {
date_of_birth: '10-Jan-1986'
};
$scope.$watch("family_member.date_of_birth", function(newValue, oldValue) {
$scope.family.grade = $scope.options[2];
});
});