ng-options & ng-repeat: why angular model isn't bind? - javascript

When I bind the select elements to ng-model family, which refers to an element of font.families array, Angular doesn't bind what is selected. The array stays empty, with just the first null element.
But when I bind the select elements to font.families[$index], the binding is effective.
// Javascript
$scope.font.families = [null]; // Used to create an empty first line in the form
$scope.families = [
{ name: "Foo", parent: "Bar" },
{ name: "Doo", parent: "Dar" }
];
$scope.mainFamilies = [
{ name: "Bar", children: ["Foo", "Baz"] },
{ name: "Dar", children: ["Doo", "Hoo"] }
];
<!-- HTML -->
Font families : {{ font.families }}
<div ng-repeat="family in font.families track by $index">
Family : {{ family }}
<ng-form name="familyRow">
<select ng-options="fam as fam.name group by fam.parent for fam in families"
ng-model="font.families[$index]"
name="family">
<option disabled value="">—</option>
</select>
<select ng-options="mainFam.name as mainFam.name for mainFam in mainFamilies"
ng-model="font.families[$index].parent"
name="mainFamily">
<option disabled value="">—</option>
</select>
</div>
Plunker to illustrate the problem.
How comes?

Here's my understanding of the issue:
Case 1 (Working): You're using ng-model="model.families[$index]". In this case when you make a selection here's what happens:
model.families[0] -> Your selection
Case 2 (Not working): You're using ng-model="family". In this case this is what is happening:
family -> model.families[0] -> null
When you make the selection:
family -> new selection
model.families[0] -> null
The problem is that in 2nd case, the assignment causes the family variable to be assigned a new value altogether and the source array remains the same.
You can further verify this by adding these statements in your html:
family: {{family}}
model.families: {{model.families[$index]}}
Are they equal?: {{familly === model.families[$index]}}

Related

Angular 2.0 and TypeScript - Populate <select> options from multi-dimensional array objects

I would like to populate the <select name="selectmodel"> <option> from a nested array of objects based on the selection of the <select name="selectmake"> <option> element.
Here is the multi-dimensional array:
muscleCars = [
{
id: 1, name: "Chevrolet", models: [
{ model: "Camaro" },
{ model: "Corvette" }
]
},
{
id: 2, name: "Dodge", models: [
{ model: "Charger" },
{ model: "Challenger" },
{ model: "Viper" }
]
},
{
id: 3, name: "Ford", models: [
{ model: "GT" },
{ model: "Mustang" }
]
}
];
This is the HTML
//select for Make:
<select name="selectmake" [(ngModel)]="makeListFilter">
<option *ngFor="let muscleCar of muscleCars" [ngValue]="muscleCar.name">{{muscleCar.name}}</option>
</select>
//select for Model:
<select name="selectmodel" [(ngModel)]="modelListFilter">
<option *ngFor="let muscleCar of muscleCars" [ngValue]="muscleCar.models">{{muscleCar.models}}</option>
</select>
So, basically when you select Chevrolet for example, I would like to have the second element populated with Camaro and Corvette.
Currently, the second select element is populated with an array [object Object] for each make, but can't figure out how to dig this deeper.
Here is a plunk:
https://embed.plnkr.co/0eEIJg5uzL6KsI70wWsC/
Any help would be appreciated.
This is how your HTML should look like:
<select name="selectmake" [(ngModel)]="makeListFilter">
<option *ngFor="let muscleCar of muscleCars" [ngValue]="muscleCar">{{muscleCar.name}}</option>
</select>
<select name="selectmodel" [(ngModel)]="modelListFilter">
<option *ngFor="let carModel of makeListFilter?.models" [ngValue]="carModel.model">{{carModel.model}}</option>
</select>
So, what's happening here is that selected value of selectmake dropdown is binded to makeListFilter and second dropdown selectmodel is populated based on selected value of first dropdown. You will notice I binded the whole Object that is selected in first dropdown using ngValue directive so it can be used to populate second dropdown. Another interesting thing you'll notice is Elvis operator (?) I used in second dropdown - it is used to tell second dropdown to populate itself only after value is selected in first dropdown, this is necessary to avoid getting error for iterating through an undefined value. If you don't want to use Elvis operator, you can use *ngIf directive to prevent getting mentioned error, but that means that second dropdown will appear only after you select something in the first dropdown:
<select *ngIf="makeListFilter" name="selectmodel" [(ngModel)]="modelListFilter">
<option *ngFor="let carModel of makeListFilter.models" [ngValue]="carModel.model">{{carModel.model}}</option>
</select>

Handling dropdown in angular js

I have a dropdown with some values :
Apple
Mango
Orange
Grapes
HTML :
<div class="form-group">
<label class="control-label col-sm-20" for="groupz">Role*</label>
<select class="form-control" ng-model="model.selectedRole" name="role" ng-change="GetRole(model.selectedRole)" >
<option value class selected>Select Roles</option>
<option ng-repeat="item in model.roles track by $index" value="{{item}}">{{item}}</option>
</select>
</div>
I want my $scope.selectedRole to be by default as Apple. But later when the user needs to change the value, they can change it from apple to orange or any other fruit name. I have separate service request to fetch the fruits from backend and I write my code in controller as follows.
$scope.GetRole = function() {
$scope.selectedrole = [];
if ($scope.model.selectedRole != null) {
for (var i = 0; i < 1; i++) {
$scope.selectedrole.push($scope.model.selectedRole);
}
}
}
I hope this helps you
JsFiddle
In js
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope) {
$scope.selectedrole = ['Apple', 'Mango', 'Orange', 'Grapes'];
$scope.selectRole= $scope.selectedrole[0];
});
In HTML
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<select ng-model="selectRole" ng-options="role for role in selectedrole">
</select>
</div>
just try : HTML
<select class="form-control select" name="role" id="role" data-ng-model="ctrl.model.selectedRole" data-ng-options="option.name for option in ctrl.model.roles track by option.id"></select>
in your contoller
$scope.model = {
roles: [{
id: '1',
name: 'Apple'
}, {
id: '2',
name: 'Orange'
}, {
id: '3',
name: 'Mango'
}],
selectedRole: {
id: '1',
name: 'Apple'
} //This sets the default value of the select in the ui
};
Then assign the first array element to selectedrole containing the array of values(Apple Mango Orange Grapes).
If you want the default to be apple and the array is ordered:
array = [Apple, Mango, Orange, Grapes]
Your model needs to be set to selectedRole:
data-ng-model="selectedRole"
In your controller, set:
selectedRole = array[0]
angular will take care of the rest of the data manipulation.
I hope this helps. Please provide more information for a clearer answer
Thanks
Handling a select element i.e. a drop down list in AngularJS is pretty simple.
Things you need to know is that you bind it to an array or a collection to generate the set of option tags using the ng-options or the ng-repeat directives which is bound to the data source on your $scope and you have a selected option which you need to retrieve as it is the one the user selects, it can be done using the ng-model directive.
If you want to set the selected option on the page load event, then you have to set the appropriate object or value (here it is the fruit id) which you are retrieving from data binding using the as clause in the ng-options directive as shown in the below embedded code snippet
ng-options="fruit.id as fruit.name for fruit in ctrl.fruits"
or set it to the value of the value attribute when using the ng-repeat directive on the option tag i.e. set data.model to the appropriate option.value
<select size="6" name="ngvalueselect" ng-model="data.model" multiple>
<option ng-repeat="option in data.availableOptions" ng-value="option.value">{{option.name}}</option>
</select>
angular
.module('fruits', [])
.controller('FruitController', FruitController);
function FruitController() {
var vm = this;
var fruitInfo = getFruits();
vm.fruits = fruitInfo.fruits;
vm.selectedFruitId = fruitInfo.selectedFruitId;
vm.onFruitChanged = onFruitChanged;
function getFruits() {
// get fruits and selectedFruitId from api here
var fruits = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Mango' },
{ id: 3, name: 'Banana' },
{ id: 4, name: 'Orange' }
];
var fruitInfo = {
fruits: fruits,
selectedFruitId: 1
};
return fruitInfo;
}
function onFruitChanged(fruitId) {
// do something when fruit changes
console.log(fruitId);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="fruits">
<div ng-controller="FruitController as ctrl">
<select ng-options="fruit.id as fruit.name for fruit in ctrl.fruits"
ng-model="ctrl.selectedFruitId"
ng-change="ctrl.onFruitChanged(ctrl.selectedFruitId)">
<option value="">Select Fruit</option>
</select>
</div>
</div>
Check the Example section here for more information.

Preselected option by ng-init doesn't work in ngOptions | AngularJS

I have object product = {id: "759", name: "someName", category_id: "139", cartridge_type: 2 ...} in my angular controller.
Why preselected option in ngOptions doesn't work? Select renders empty option as if product.cartridge_type would be null.
HTML
<select class="form-control"
id="cartridge_type"
name="cartridge_type"
ng-init="product.cartridge_type=product.cartridge_type||cartridgeTypeScope[0]"
ng-model="product.cartridge_type"
ng-options="cartridge_type.id as cartridge_type.name for cartridge_type in cartridgeTypeScope">
<option value="">Select type</option>
</select>
JS
$http.get('someApiPath').success(function(data) {
$scope.product = data[0];
console.log( $scope.product );
});
$scope.cartridgeTypeScope = [
{
id: 0,
name : '-'
},
{
id: 1,
name : 'cartridgeType1'
},
{
id: 2,
name : 'cartridgeType2'
}
]
Just simple use cartridgeTypeScope[0].id in ng-init
ng-init="product.cartridge_type=product.cartridge_type||cartridgeTypeScope[0].id"
Thing is that you are using cartridge_type.id as cartridge_type.name which is expecting id in the select but in ng-init you are providing it complete object (cartridgeTypeScope[0]).
So it is not selecting your option value.
Alternatively you can do
You can use same ng-init but remove cartridge_type.id as from your ng-options
ng-init="product.cartridge_type=product.cartridge_type||cartridgeTypeScope[0]"
ng-options="cartridge_type.name for cartridge_type in cartridgeTypeScope"
Hope It help :)
UPDATE
For controller default option you need to do
$scope.product={
"cartridge_type":2
}
UPADTE 2:-
For $http:-
Plunker

How to set the selected value for multiple select elements in a single AngularJS context

I try to create multiple select elements on a AngularJS page in a single context. names is a array of available name objects and cores is the array with available cores. For each name, I would like to have a drop down with the available cores. This works fine, but I'm not able to pre-select the currently selected core. That one is stored in name.core.
<ul>
<li ng-repeat='name in names'>
{{ name.name }} - {{ name._url }} <span ng-click='delete_name(name)'>[x]</span>
<select ng-model='?'
ng-options='core.id as core.instance for core in cores'
ng-change='coreChanged(name, ?)'>
<option value=''>No core assigned</option>
</select>
</li>
</ul>
I'm quite new to AngularJS, so if another structure or additional controllers, scopes, ... would be more appropriate, I'm open for suggestions.
Update:
ng-model='name.core' as proposed by haki does not work, probably because name.core is not an instance from the cores array. I tried ng-model='name.core.id' but that results in every drop down having the same (wrong) selection.
This works fine for me:
<ul>
<li ng-repeat='name in names'>
{{ name.name }} - {{ name._url }} <span ng-click='delete_name(name)'>[x]</span>
<select ng-model='name.core'
ng-options='core.id as core.instance for core in cores'
ng-change='coreChanged(name)'>
<option value=''>No core assigned</option>
</select>
</li>
</ul>
Double-check on how your view corresponds to your model. My scope variables look like this:
.controller('MyController', function($scope){
$scope.names = [
{ name: 'Marc', url: 'http://www.example.com/one', core: "1" },
{ name: 'Achim', url: 'http://www.example.com/two', core: "2" },
{ name: 'Jenny', url: 'http://www.example.com/three', core: "3" }
];
$scope.cores = [
{ id: '1', instance: 'First' },
{ id: '2', instance: 'Second' },
{ id: '3', instance: 'Third' }
];
$scope.coreChanged = function(name){
console.log(name);
}
});
Working Plunker

angularjs: cascade dropdown

I'm trying to achieve a cascade dropdown in Angular. I thought it would just work naturally thanks to binding. See below:
<select name="client" ng-model="selectedRequest.client" ng-options="c.name for c in clients track by c.id" required></select>
<select id="department" ng-model="selectedRequest.department" ng-options="d.defaultLabel for d in selectedRequest.client.departments track by d.id"></select>
When the view is loaded, it works, I can see the departments matching those bound to the client. However, whenever the selectedRequest.client changes, the source for the department dropdown should change too, but instead it becomes empty.
EDIT
I've changed the child dropdown to :
<select id="department" ng-model="selectedRequest.department" ng-options="d.defaultLabel for d in departments track by d.id | filter:{clientId: selectedRequest.client.id}"></select>
but this time it loads all the departments in the dropdown, ignoring the filter.
** EDIT 2 **
Changing to :
<select name="client" ng-model="requestService.selectedRequest.client" ng-options="c as c.name for c in clients track by c.id" required></select>
<select id="department" ng-model="requestService.selectedRequest.department" ng-options="d.defaultLabel for d in departments | filter:{clientId: requestService.selectedRequest.client.id}"></select>
Now the source changes correctly when a client is selected. However the initial selection, i.e setting the right department at startup, does not work. That's because I've removed the 'track by id' bit.
the correct way was
<select id="department" ng-model="selectedRequest.department" ng-options="d.defaultLabel for d in departments | filter:{clientId: selectedRequest.client.id} track by d.id "></select>
it's just that I hadn't put the filter at the right place... silly mistake.
It could be that your selectedRequest.client does not refer to the same object in clients array. Try this:
JS:
function testController($scope) {
$scope.clients = [
{ id: 1, name: "client1", departments: [{ id: 1, defaultLabel: 'department1' }, { id: 2, defaultLabel: 'department2'}] },
{ id: 2, name: "client2", departments: [{ id: 3, defaultLabel: 'department3' }, { id: 4, defaultLabel: 'department4'}] }
];
$scope.selectedRequest = {};
$scope.selectedRequest.client = $scope.clients[0];//Assign by object reference.
}
HTML:
<div ng-controller="testController">
<select name="client" ng-model="selectedRequest.client" ng-options="c.name for c in clients" required></select>
<select id="department" ng-model="selectedRequest.department" ng-options="d.defaultLabel for d in selectedRequest.client.departments"></select>
</div>
DEMO
I removed track by to use the default (track by object reference) and ensure that selectedRequest.client refers to objects inside clients

Categories

Resources