I couldn't find an elegant way for setting null values with a <select> using AngularJS.
HTML :
<select ng-model="obj.selected">
<option value=null>Unknown</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
{{obj}}
JS :
$scope.obj ={"selected":null};
When the page is loaded, the first option is selected, which is good, and the output is {"selected":null}. When that first option is reselected after having switch to another one, the output becomes {"selected":"null"} (with the quotes), which is not what I would expect.
Running example :
http://plnkr.co/edit/WuJrBBGuHGqbKq6yL4La
I know that the markup <option value=null> is not correct. I also tried with <option value=""> but it corresponds to an empty String and not to null : the first option is therefore not selected and another option which disappears after the first selection is selected by default.
Any idea ?
This should work for you:
Controller:
function MyCntrl($scope) {
$scope.obj ={"selected":null};
$scope.objects = [{id: 1, value: "Yes"}, {id: 0, value: "No"}]
}
Template:
<div ng-controller="MyCntrl">
<select ng-model="obj.selected"
ng-options="value.id as value.value for value in objects">
<option value="">Unknown</option>
</select>
<br/>
{{obj}}
</div>
Working plnkr
You should use ng-options with select.
You can use the ngOptions directive on the select. According to the documentation:
Optionally, a single hard-coded <option> element, with the value set to an empty string, can be nested into the <select> element. This element will then represent the null or "not selected" option. See example below for demonstration.
<select ng-model="obj.selected" ng-options="key as label for (key, label) in ['No', 'Yes']">
<option value="">Unknown</option>
</select>
It's obviously a better idea to define the options list directly in the controller.
Try using ng-options instead of manually creating tags, as in this example, lightly-edited from the Angular docs:
http://plnkr.co/edit/DVXwlFR6MfcfYPNHScO5?p=preview
The operative parts here are lines 17, defining a 'colors' object, and the ng-options attributes iterating over those colors to create options.
If you REALLY want to use null, see below. You need to use ng-options and let Angular handle the mapping:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Color selector</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.8/angular.min.js"></script>
</head>
<body ng-app="">
<script>
function MyCntrl($scope) {
$scope.obj ={"selected":null};
$scope.objStates = [{key:"Unknown", value:null}, {key:"Yes", value:1}, {key:"No", value:0}]
$scope.$watch('obj.selected', function(newVal){
console.log(newVal);
})
}
</script>
<div ng-controller="MyCntrl">
<select ng-model="obj.selected" ng-options="state.value as state.key for state in objStates">
</select>
<br/>
{{obj}}
</div>
</body>
</html>
I ran into the same Problem but could not solve it via 'ng-options'. My solution is:
module.directive('modelToNull', [function () {
return {
scope: {
check: "&modelToNull"
},
require: 'ngModel',
link: function ($scope, element, attrs, ngModelController) {
ngModelController.$parsers.push(function (value) {
return value == null || $scope.check({value: value}) ? null : value;
});
}
};
}]);
You can use it like this:
<select ng-model="obj.selected" model-to-null="value == 'null'">
<option value="null">Unknown</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
Can you try to use parseInt on the value? For example, both "1" and "0" will equal their respective integer values. If you run the empty string through parseInt you can easily get NaN.
> parseInt("") = NaN
> parseInt("0") === 0
> parseInt("1") === 1
Without the possibility of using ng-options I present another fix.
I've been battling this a couple of months now, using solutions presented on this question and I don't know how nobody posted this:
<option value="null"></option>
This should work on Angular 1.6 and above for sure when you are using ng-repeat for options instead of ng-options.
It's not ideal but since we are used to work on legacy code this simple fix could save your day.
the only way you can achieve that is by using a onchange event and restoring the object as initialized any other attempt to set the selected to null will remove the property from the object.
$scope.setValue=function(val){
if($scope.obj.selected=="null")
$scope.obj ={"selected":null};
}
<select ng-change="setValue()" ng-model="obj.selected">
<option value=null ng-click="obj.selected=null">Unknown</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
this is a bad idea, you should always have values in your model instead of playing around with null and undefined
This is much easier on Angular2/Angular where you can just use
<option [value]="null">Unknown</option>
This value is no longer a string, but a real null value.
Related
I'm working on a project, and I need to bind the options value to object key's in such a manner that, on selecting an option, it gives 1, else other variables remain 0.
My HTML Code:-
<select required class="custom-select">
<option disabled>Select an option</option>
<option ng-model="PredictCtrl.detail.building_type_AP">Apartment</option>
<option ng-model="PredictCtrl.detail.building_type_GC">Independent House / Villa</option>
<option ng-model="PredictCtrl.detail.building_type_IF">Independent Floor / Builder's Floor</option>
<option ng-model="PredictCtrl.detail.building_type_IH">Gated Community Villa</option>
</select>
Variable to bind -
PredictCtrl.detail = {
building_type_AP: 0,
building_type_GC: 0,
building_type_IF: 0,
building_type_IH: 0
}
Generally, binding is done with select tag, which gives the value of the selected option, but I want in such a way that, when I click on Apartment option, it's bind variable PredictCtrl.detail.building_type_AP becomes 1, rest remains 0. Similarly, it does with other options.
I want to send the data as the same format through API.
So, please Help me out.
Sorry If I was not very clear with explaining or for any typo.
Thank you in advance.
You should take a look at the NgOptions directive which is the "angularjs" way of working with the select-tag.
It sould be able to fulfill your requirement as you will get the selected option in the SelectedOption object.
Here's an example
angular.module("app",[]).controller("myCtrl",function($scope){
$scope.details =
[
{name:"Apartment", value:1},
{name:"Independent House / Villa", value:2},
{name:"Independent Floor / Builder's Floor", value:3},
{name:"Gated Community Villa", value:4}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
<select required class="custom-select" ng-options="item as item.name for item in details" ng-model="selectedOption">
</select>
<p>SelectedOption: {{selectedOption}}</p>
</div>
None of the answers at my time of writing actually present a functioning solution, so here's one.
Don't scatter ng-model directives across different option elements, it's unnecessary.
You can achieve what you want by using ng-options to enumerate all your choices, a single ng-model to keep track of the selected option, and ng-change to apply the values as you described (i.e. 1 on the selected key, 0 for everything else).
I've assumed you've got a requirement to keep the structure of detail as is. If it can be changed, then I'd recommend associating each labels with each it's respective building_type_ to keep things together.
See below.
angular
.module('app', [])
.controller('PredictCtrl', function () {
this.selectedDetail = null;
this.detail = {
building_type_AP: 0,
building_type_GC: 0,
building_type_IF: 0,
building_type_IH: 0,
};
this.labels = {
building_type_AP: 'Apartment',
building_type_GC: 'Independent House / Villa',
building_type_IF: 'Independent Floor / Builder\'s Floor',
building_type_IH: 'Gated Community Villa',
};
this.changeDetail = function () {
for (var key in this.detail) {
this.detail[key] = +(this.selectedDetail === key);
}
};
});
<script src="https://unpkg.com/angular#1.7.4/angular.min.js"></script>
<div ng-app="app" ng-controller="PredictCtrl as PredictCtrl">
<select
ng-model="PredictCtrl.selectedDetail"
ng-options="key as PredictCtrl.labels[key] for (key, value) in PredictCtrl.detail"
ng-change="PredictCtrl.changeDetail()"></select>
<pre>{{ PredictCtrl.detail | json }}</pre>
</div>
try like this:
in your controller:
$scope.details = [
{
name: "building_type_AP",
value: "Apartment",
state: false
},{
name: "building_type_GC",
value: "Independent House / Villa",
state: false
}/*add more here*/
];
$scope.setActive = function(detail){
detail.state = !detail.state;
}
in html template:
<select required class="custom-select">
<option disabled>Select an option</option>
<option ng-repeat="detail in details" ng-click="setActive(detail)">{{detail.value}}</option>
</select>
in the end just go through $scope.details and parse false to 0 and true to 1 OR just do this inside setActive function
I have an existing select tag with existing option tags, one marked "selected", for example:
<div class="form-group">
<label class="control-label">Test</label>
<select ng-model="vm.testNumber">
<option>Choose...</option>
<option value="1">one</option>
<option value="2">two</option>
<option value="3" selected>three</option>
</select>
<p class="help-block">{{vm.testNumber}}</p>
</div>
Angular is binding to the model correctly and spits out "3" in the help block when the page loads, but option 3 is not selected. It binds and shows 3 as selected if I use text for the value attributes on each option, but not when it's a numerical value. Instead it inserts an option at the top of the select list:
<option value="? number:3 ?"></option>
What am I doing wrong?
From Angular DOC
Note that the value of a select directive used without ngOptions is always a string. When the model needs to be bound to a non-string value, you must either explicitly convert it or use ngOptions to specify the set of options. This is because an option element can only be bound to string values at present.
If you change your code as like as below code snippet, you don't need to do any explicit string conversions.
<select ng-model="vm.testNumber" ng-options="item.value as item.label for item in [{label: 'one', value: 1}, {label: 'two', value: 2}]">
<option value="">Choose...</option>
</select>
Turns out you need to add a directive to make this work correctly. #codeninja.sj's answer ends up replacing 'one' with 1, 'two' with 2, etc. In the documention, the last example has this directive, convert-to-number
angular.module('app').directive('convertToNumber', function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
ngModel.$parsers.push(function (val) {
return parseInt(val, 10);
});
ngModel.$formatters.push(function (val) {
return '' + val;
});
}
};
});
None of the other examples in the documentation worked for me.
Update #codeninja.sj's updated answer does work, however, figure this directive will cut down on magic strings in my code.
My problem is that ng-selected is set as true but the option is not getting selected
This is my controller code
.controller("PendingInvoiceCtrl", function($scope, $location, safeApply, dataService) {
var userData = dataService.data();
var mauth_token = userData.mauthToken;
var mauth_acntId = userData.thisApartment.account;
var apt_id = userData.thisApartment.id;
$scope.house_list = userData.thisApartment.house;
$scope.selectedHouseId = $location.search().houseId;
console.log($scope.selectedHouseId);
});
This is my HTML code
<select ng-model="selectedHouseId">
<option ng-repeat="house in house_list" ng-selected="{{ house.house_id == selectedHouseId }}" value="{{ house.house_id }}">
{{ house.house_display_name }}
</option>
</select>
And below is my data format
{
house:[0]:{
house_display_name: "paras, 101",
house_id: "520755"
}
}
The ng- attributes don't need the extra curly braces. Try:
<option ng-repeat="house in house_list" ng-selected="house.house_id == selectedHouseId" ng-value="house.house_id">
{{house.house_display_name}}
</option>
A better approach would be to use the ng-options possibility of the select tag. This would result in:
<select
ng-model="selectedHouseId"
ng-options="house.house_id as house.house_display_name for house in house_list">
</select>
And then you don't need to manually worry about the selected attribute for the options, as it will select the one depending on the value of the model.
Your option tag should be:
<option ng-repeat="house in house_list" ng-selected="house.house_id == selectedHouseId" ng-value="house.house_id">
if you use ng-Selected and ng-Model together was never really specified. It looks like at some point ng-Model took priority over ng-Selected.
I recommend you to use the Angular Directive ng-options.
<select ng-options="house.house_id as house.house_display_name for house in house_list" ng-model="selectedHouseId"></select>
Associated plunker: http://plnkr.co/8LGz5o0d2gDRoRHYr4pQ
I'm pretty new to Angular and trying the ng-options. In my controller, I have:
$scope.permissionLevels = [
{ value: "ROLE_READ", text: "Read Only" },
{ value: "ROLE_WRITE", text: "Write" }
];
In my template, I have:
<select ng-options="permissionLevel.text for permissionLevel in permissionLevels"
ng-model="selectedValue"></select>
Depending on the view, I want to hide either Read or Write. So in my controller, I have another flag that indicates what view it is. Before I used ng-options, I had a normal select drop down and did something like this:
<select>
<option>Read Only </option>
<option ng-show="shouldShowWrite">Write </option>
</select>
Is there a way to do this with ng-options? Thanks.
You could use a filter in the ngOptions expression:
<select ng-model="selectedValue"
ng-options="permissionLevel.text for permissionLevel in
permissionLevels | filter:shouldShow">
</select>
and define the shouldShow() function to $scope in the controller:
$scope.shouldShow = function (permissionLevel) {
// put your authorization logic here
return $scope.permission.canWrite || permissionLevel.value !== 'ROLE_WRITE';
}
For the full example see: http://plnkr.co/edit/8FkVktDXKGg3MQQawZCH
Consider ngRepeat for that level of control. I don't think it's possible with ngOptions.
<select ng-model="selectedValue">
<option ng-repeat="permissionLevel in permissionLevels" value="{{permissionLevel.text}}" ng-show="shouldShowWrite(permissionLevel)">
{{permissionLevel.text}}
</option>
</select>
Plaese check below code, it may help you to solve the problem!!
<select>
<option>Read Only </option>
<option ng-show="selectedValue.value=='ROLE_WRITE'">Write </option>
</select>
I want to set a boolean value to true or false using a select here is my code:
<select class="span9" ng-model="proposal.formalStoryboard">
<option value="false">Not Included</option>
<option value="true">Included</option>
</select>
The value (proposal.formalStoryboard) is set properly to true or false but the change are not reflected on the select box when the value is already assigned.
I tried ng-value="true" and ng-value="false" instead of just value but it's not working as well.
EDIT: Commentors have pointed out that my original solution did not work as claimed. I have updated the answer to reflect the correct answer given by others below (I cannot delete an accepted answer).
For Angular 1.0.6, consider this HTML:
<div ng-app="">
<div ng-controller="MyCntrl">
<select ng-model="mybool"
ng-options="o.v as o.n for o in [{ n: 'Not included', v: false }, { n: 'Included', v: true }]">
</select>
<p>
Currently selected: <b>{{ mybool }}</b> opposite: {{ !mybool }}
</p>
</div>
</div>
And this JavaScript:
function MyCntrl($scope) {
$scope.mybool = true;
}
Here is a working DEMO for Angular 1.0.6 and here is a working DEMO for Angular 1.3.14, which is slightly different.
Just do like this:
<select ng-model="someModel" ng-options="boolToStr(item) for item in [true, false]">
</select>
and define:
$scope.boolToStr = function(arg) {return arg ? 'Included' : 'Not included'};
Why not just use this?
<select class="form-control" ng-options="(item?'Included':'Not Included') for item in [true, false]"></select>
If you are using angularjs version >= 1.2.0 you will have access to the directive ng-value
You can use the ng-value on an option element. Your htmls would work like this.
<select ng-model="proposal.formalStoryboard">
<option ng-value="true">Included</option>
<option ng-value="false">Not Included</option>
</select>
It also works on radio and checkboxes.
I would recommend using a directive for this. As usual, I try to stay away from timeouts and other async operations in preference of a more authoritative and direct control.
directives.directive('boolean', function() {
return {
priority: '50',
require: 'ngModel',
link: function(_, __, ___, ngModel) {
ngModel.$parsers.push(function(value) {
return value == 'true' || value == true;
});
ngModel.$formatters.push(function(value) {
return value && value != 'false' ? 'true' : 'false';
});
}
};
});
The priority is set specifically so that it is done prior to any other directives (that usually don't have a priority set, and defaults to 0)
For example, I use this directive (for a true/false selection) with my selectpicker directive that wraps my select elements in the selectpicker bootstrap plugin.
Edit:
The caveat here, which I forgot to mention, is that your html values need to be string values. What the directive does is translates between the view and the model, keeping the model value in boolean and your view in string format:
%select.selectpicker{ ng: { model: 'modelForm.listed' }, selectpicker: '{ }', boolean: true }
%option{ value: 'true' } Listed
%option{ value: 'false' } Unlisted
This will work too. Just force the value to be boolean by putting an angular expression in for the value.
<select class="span9" ng-model="proposal.formalStoryboard">
<option value="{{false}}"
ng-selected="proposal.formalStoryboard === false">
Not Included
</option>
<option value="{{true}}"
ng-selected="proposal.formalStoryboard === true">
Included
</option>
</select>
I created sample for you, please check this out.
Is it what you want to use model to drive the ui binding?
<div ng-app ng-controller="Ctrl">
<select class="span9" ng-model="proposal.formalStoryboard">
<option value="false">Not Included</option>
<option value="true">Included</option>
</select>
<button ng-click="changeValue();">Click</button>
<div>
function Ctrl($scope) {
$scope.proposal = {};
$scope.proposal.formalStoryboard = true;
$scope.changeValue = function () {
$scope.proposal.formalStoryboard = !$scope.proposal.formalStoryboard;
console.log($scope.proposal.formalStoryboard);
}
}
I had very little success with this frustrating issue. My solution, while not too elegant since it's an additional line of code, solved it every time. This may not work in every application.
$scope.myObject.myBooleanProperty = $scope.myObject.myBooleanProperty.toString();
Turning it into a "real" string before trying to rebind it to the model displayed on the page allowed it to correctly select the value.
Angular does a strict comparsion between the Value bind to ng-model and the Values in the given Options. The Values given in the initial Question are the Strings "false" and "true".
If the Value of ng-model is of Type bool and defined like {"Value":false}, Angulars strict === comparsion between string and bool does never match so the select-box is empty.
The real Problem is- if you select a Value, the Type changed from bool to string ({"Value":false} --> {"Value":"false"})can cause errors if posted back.
The best Solution for both issues for me was the one of Thiago Passos in this Post.
(https://stackoverflow.com/a/31636437/6319374)
<script type='text/javascript'>
function MyCntrl($scope) {<!--from ww w . j a v a 2s. c om-->
$scope.mybool = true;
}
</script>
</head>
<body>
<div ng-app="">
<div ng-controller="MyCntrl">
<select ng-model="mybool">
<option value="false">Not Included</option>
<option value="true">Included</option>
</select>
<p>
Currently selected: {{ mybool }}
</p>
</div>
</div>
</body>
</html>