AngularJS - Populating HTML Drop-Down with JSON from REST API (without $scope) - angularjs

A lot of solutions on Stack Overflow in relation to populating drop down menus include $scope.
My second drop-down depends on the value of my first drop-down therefore I use ng-changeon the first HTML select to parameter pass the model ID into the 2nd drop-down's function.
1st Drop-Down HTML and Angular JS:
<select data-ng-controller="addAssetController as addAssetCtrl" id="functionalOrg" data-ng-model="addAssetFormCtrl.functionalOrg.id" ng-change="addAssetCtrl.getLocations(addAssetFormCtrl.functionalOrg.id)">
<option data-ng-repeat="functionalOrg in addAssetCtrl.functionalOrgs | orderBy:'id' track by $index" value="{{functionalOrg.id}}">
{{functionalOrg.id}} - {{functionalOrg.account}}
</option>
</select>
Hence ng-change:
ng-change="addAssetCtrl.getLocations(addAssetFormCtrl.functionalOrg.id)"
-
var vm = this;
functionalOrganisationRepository.getFunctionalOrganisation().then(function (results) {
vm.functionalOrgs = results;
}, function (error) {
vm.error = true;
vm.errorMessage = error;
});
The 2nd Drop-Down HTML and Angular:
<select data-ng-controller="addAssetController as addAssetCtrl" id="location" data-ng-model="addAssetFormCtrl.location.id">
<option data-ng-repeat="location in addAssetCtrl.locations | orderBy:'id' track by $index" value="{{location.id}}">
{{location.id}} - {{location.address6}}
</option>
</select>
-
vm.getLocations = function(id) {
console.log("Functional org ID:" + id);
locationRepository.getLocation(id).then(function (results) {
vm.locations = results;
}, function (error) {
vm.error = true;
vm.errorMessage = error;
});
}
Assuming my service layer is fine and brings back a JSON object with everything I require, what could the problem be? The vm.getLocations function is definitely getting called because my console log is being printed. The service layer is also fine because a JSON object to being logged to my command prompt.
My question is how do I populate my second drop-down from whatever JSON is returned by getLocations? Please hence I do not want to make use of $scope in Angular.

The "ng-controller" attribute is repeated on each select. Put the attribute only one time on a parent element!
<div data-ng-controller="addAssetController as addAssetCtrl">
<!-- Drop Down 1 and 2 here -->
</div>

If you dont share scope you can't do what you want to do. Meaning if you dont have a parent vm or pass something to your directive = you can't tell what is selected.
Pull up your controller a level and share it among selected or you are going to have to either watch a shared variable in a service, or rely on $on & $broadcast to communicate.

Pretty sure that this is because they have different scopes and different controllers.
Assuming that the actual HTML looks like the following:
<select data-ng-controller="addAssetController as addAssetCtrl" id="location" data-ng-model="addAssetFormCtrl.location.id">
<option data-ng-repeat="location in addAssetCtrl.locations | orderBy:'id' track by $index" value="{{location.id}}">
{{location.id}} - {{location.address6}}
</option>
</select>
<select data-ng-controller="addAssetController as addAssetCtrl" id="functionalOrg" data-ng-model="addAssetFormCtrl.functionalOrg.id" ng-change="addAssetCtrl.getLocations(addAssetFormCtrl.functionalOrg.id)">
<option data-ng-repeat="functionalOrg in addAssetCtrl.functionalOrgs | orderBy:'id' track by $index" value="{{functionalOrg.id}}">
{{functionalOrg.id}} - {{functionalOrg.account}}
</option>
</select>
Then, to the best of my knowledge, each select will get a different instance of the controller, each with different scopes. So you've effectively got a controller addAssetCtrl1 and addAssetCtrl2, so setting data on 1 does not set it on 2.
The solution would be to set the data on a parent controller, or, probably more simply, to do the following:
<div data-ng-controller="addAssetController as addAssetCtrl">
<select id="location" data-ng-model="addAssetFormCtrl.location.id">
<option data-ng-repeat="location in addAssetCtrl.locations | orderBy:'id' track by $index" value="{{location.id}}">
{{location.id}} - {{location.address6}}
</option>
</select>
<select id="functionalOrg" data-ng-model="addAssetFormCtrl.functionalOrg.id" ng-change="addAssetCtrl.getLocations(addAssetFormCtrl.functionalOrg.id)">
<option data-ng-repeat="functionalOrg in addAssetCtrl.functionalOrgs | orderBy:'id' track by $index" value="{{functionalOrg.id}}">
{{functionalOrg.id}} - {{functionalOrg.account}}
</option>
</select>
</div>

Related

Angular change detection ngFor

I have an array called peoples on my tab view. I need to change the array items based on a change function. While the change function is working successfully and printing the array differently after each change on the console, the view itself is not changing from the initial value assigned from ngInit();
ngOnInit(){
this.someservice.loadallpeople().subscribe(data=>{
this.peoples=data.Data;
});
}
loadpeople(category:any){
this.someservice.getpeoplebycat(category).subscribe(data=>{
this.peoples=data.Data;
});
}
<select [(ngModel)]="category.name"
(ngModelChange)="loadpeople(category.name)">
<option *ngFor="let cat of category">{{category.name}} </option>
</select>
<div *ngFor="let people of peoples">
<span>{{people.name}}</span>
</div>
I have used some methods but none of them seems to work. Any small help will be appreciated.
Some things are not clear on what you show.
First you use [(ngModel)]="category.name" and (ngModelChange)="loadpeople(category.name)" on category.name, but next you iterate over category and you get cat.name, so there cannot be a category.name element if it's an array.
Second I will just use (change) instead of [ngModel] + (ngModelChange) according to what you show, because as said before, category.name cannot return anything.
So with all this updates, I will do something like this
ngOnInit(){
this.someservice.loadallpeople().subscribe(data=>{
this.peoples=data.Data;
});
}
loadpeople(event){
this.someservice.getpeoplebycat(event.target.value).subscribe(data=>{
this.peoples=data.Data;
});
}
<select (change)="loadpeople($event)">
<option *ngFor="let cat of category">{{cat.name}} </option>
</select>
<div *ngFor="let people of peoples">
<span>{{people.name}}</span>
</div>

How to remove item from an array

I have a dropdown where items are populated from $scope.role. Now I need to remove the values from $scope.role once the value is added or selected in dropdown. I did splice(index,1) which actually delete the first element only. Need assistance.
$scope.role = ['Actor', 'Director/ Asst. director', 'Lyricist',
'Music director/ Asst. director', 'Singer', 'Standup Comedian', 'Model',
'Cinematographer', 'Photographer', 'Script Writer', 'Choreographer',
'Editor/ Promo editor', 'Costume designer', 'Art director', 'Stunt artist',
'Voice-over artist', 'Graphic Designer', 'Screenplay', 'Dialogue',
'Back ground music'];
Html:
<div class="row newRow">
<div class="form-group fields col-sm-2 col-xs-4">
<label>ROLE *</label>
<select name="role" class="form-control1 drop2" required ng-model="model.role" placeholder="select">
<option value='' disabled selected>Select</option>
<option ng-repeat="item in role track by $index" value="{{item}}">{{item}}</option>
</select>
</div>
<div class="form-group col-sm-2 col-xs-4">
<button ng-click="AddRole()">Click to Add Role</button>
</div>
</div>
JS:
$scope.multiRoles = [];
$scope.role == $scope.dummy;
$scope.rolesAdded = false;
$scope.AddRole = function(index) {
debugger;
if ($scope.model.role !== undefined) {
$scope.multiRoles.push($scope.model.role);
$scope.role.splice(index, 1);
console.log($scope.role);
}
};
You can do it in two ways 1) As suggested by #nikjohn by sending your $index of dropdown in ng-click = "AddRole($index)" and then splice or else
2) You can find the index of the selected option by using the ng-model binded to the dropdown.
$scope.AddRole = function(){
debugger;
if($scope.model.role !== undefined ){
$scope.multiRoles.push($scope.model.role);
var index = $scope.role.indexOf($scope.model.role); //just find the right index which is the selected option in dropdown.
$scope.role.splice(index,1);
console.log($scope.role);
}
};
In your HTML, pass $index to the AddRole. Otherwise, the function does not know what $index is, and also include the button within the ng-repeat:
<select name="role"
class="form-control1 drop2" required
ng-model="model.role" placeholder="select">
<option value='' disabled selected>Select</option>
<option ng-repeat="item in role track by $index" value="{{item}}"> {{item}}
<button ng-click = "AddRole($index)">Click to Add Role</button>
</option>
</select>
In your Controller, there's an extra = that I've highlighted in my comment:
$scope.multiRoles = [];
$scope.role == $scope.dummy; // Why a `==` here? This should be a `=`
$scope.rolesAdded = false;
$scope.AddRole = function(index){
debugger;
if($scope.model.role.length){ // Cleaner method to verify that the array is non-empty
$scope.multiRoles.push($scope.model.role);
$scope.role.splice(index,1);
console.log($scope.role);
}
};
May I also suggest that you use Angular's select implementation with ng-options because:
Choosing between ngRepeat and ngOptions
In many cases, ngRepeat can be used on elements instead of ngOptions to achieve a similar result. However, ngOptions provides some benefits:
more flexibility in how the <select>'s model is assigned via the select as part of the comprehension expression
reduced memory consumption by not creating a new scope for each repeated instance
increased render speed by creating the options in a documentFragment instead of individually
Specifically, select with repeated options slows down significantly starting at 2000 options in Chrome and Internet Explorer / Edge.

AngularJS: need to split/parse ng-options value

A legacy database I am working with stores lookup values in a semi-colon separated list (within a single field/column in the table) - which worked fine as a data source for an ASP.NET. But for migrating to AngularJS - I've found no way to intercept the value and split it for separate options in the select element.
In the select drop down it simply shows (for example) "1 rep; 2 reps; 3 reps" etc.
Can anyone suggest how to split this value so the select options render vertically - each one in it's own option row?
This is how the select element looks now:
<select
ng-model="exerciseVals[$index].reps1" ng-options="value.REPS as value.REPS for (key,value) in lkps
></select>
ng-options works on an array or an object.
If I understand your situation correctily, you're getting a large string from the server with semicolon-separated entries. You need to split it into an array.
$scope.lkps = stringFromServer.split(";");
Then in HTML:
<select ng-model="selectedVal"
ng-options="item for item in lkps">
</select>
You can try This , i have included in my code too !
Working Code
<div ng-app>
<div ng-controller="tempCtrl">
<select>
<option ng-repeat="temp in tempdata">
{{temp}}
</option>
</select>
</div>
</div>
JS CODE
var myApp = angular.module('App', ["xeditable"]);
myApp.controller('tempCtrl',function ($scope) {
alert();
$scope.tempdata = ["1 rep","2 rep","3 rep"];
console.log($scope);
});
You can also use angular filter :
<select ng-model ='select' ng-options="item for item in 'd;g;f'|split:';'">
<option value="">Nothing selected</option>
</select>
JS:
myApp.filter('split',function(){
return function(str,delimeter)
{
return str.split(delimeter);
}
check working example

How do I get the ng-model of a select tag to get the initially-selected option?

I'm pretty new to Angular, so I may be going about this all wrong...
I have a <select> similar to the following:
<select ng-model="mySelectedValue">
<option value="">--</option>
<option ng-repeat="myValue in someDynamicArrayOfValues" value="{{myValue}}" ng-selected="myFunctionForDeterminingWhetherValueIsSelected(myValue)">{{myValue}}</option>
</select>
This mostly works... The dropdown initially renders with the correct option selected, and if I change the selected option, then mySelectedValue will get the new selection. However, mySelectedValue does NOT get the initially-selected option. mySelectedValue is blank until I change the value in the dropdown.
I looked at ng-init, but that seems to get evaluated before someDynamicArrayOfValues is set...
Is there a way I can get mySelectedValue to receive the value in the initially-selected <option>?
UPDATE:
I forgot to mention that I had also tried using ng-options, but haven't had any luck getting that to work in conjunction with determining which option was selected.
I've tried this:
<div ng-show="someDynamicArrayOfValues">
<select ng-model="mySelectedValue" ng-options="arrayValue for arrayValue in someDynamicArrayOfValues" ng-selected="myFunctionForDeterminingWhetherValueIsSelected(arrayValue)">
<option value="">--</option>
</select>
</div>
and this:
<div ng-show="someDynamicArrayOfValues">
<select ng-model="mySelectedValue" ng-options="arrayValue for arrayValue in someDynamicArrayOfValues" ng-init="myFunctionForSettingSelectedValue()">
<option value="">--</option>
</select>
</div>
but neither of those work because the select is built (and ng-init and ng-selected both get evaluated) before someDynamicArrayOfValues has been set and, therefore, before the <select> is even visible. When using <option ng-repeat="...">, the <select> doesn't get built/initialized until after someDynamicArrayOfValues is set, which is why I had been going that direction.
Is there a way to get the ng-options technique to work while, at the same time, having the select dependent on someDynamicArrayOfValues (if ng-options is the better way to go)?
UPDATE 2:
Here's a Plunker (modified from ababashka's answer) that is a little closer to what I'm ultimately trying to achieve: http://plnkr.co/edit/Kj4xalhI28i5IU0hGBLL?p=preview. It's not quite there yet... I'd like it to have each of the 3 dropdowns set with the closest-matching dynamic value once someDynamicArrayOfValues is set.
I think that it will be good it you will use ng-options attribute of select tag. It's an angular directive which creates options according to Array of options. You can take a look at select documentation
If you use your code - your function myFunctionForDeterminingWhetherValueIsSelected works twice for every option at initialization and once for every option item when you select some another option.
Demo with your code: http://plnkr.co/edit/0IVNLHiw3jpz4zMKcB0P?p=preview
Demo for select you could see at description of select directive.
Update
At first, to see when value is changed - you need to use ng-change attribute of select tag, like this:
<select ng-model="mySelectedValue"
ng-options="myValue for myValue in someDynamicArrayOfValues"
ng-change="myFunctionForDeterminingWhetherValueIsSelected()">
<option value="">--</option>
</select>
Then, i don't know how does myFunctionForSettingSelectedValue look like, but there are 2 variants:
This function returns some value - then you need to use ng-init next way.
Controller:
$scope.someInitFunc = function () {
return 'One';
};
HTML:
<select ng-model="mySelectedValue"
ng-options="myValue for myValue in someDynamicArrayOfValues"
ng-change="myFunctionForDeterminingWhetherValueIsSelected()"
ng-init="mySelectedValue = someInitFunc()">
<option value="">--</option>
</select>
You set value of mySelectedValue in this function - then you do this.
Controller:
$scope.someInitFunc = function () {
$scope.mySelectedValue = 'One';
};
HTML:
<select ng-model="mySelectedValue"
ng-options="myValue for myValue in someDynamicArrayOfValues"
ng-change="myFunctionForDeterminingWhetherValueIsSelected()"
ng-init="someInitFunc()">
<option value="">--</option>
</select>
I have created an example which implements the first version of using ng-init. When new value is selected - it's printed to console.
Also, i moved options to the options.json file. So options are initialized just after ajax request was finished. Everything works great.
Demo: http://plnkr.co/edit/pzjxxTnboKJXJYBGcgNb?p=preview
Update 2
Hello again. I think you don't need to have any ng-init according to your requirements. You can just initiate values of your model when http request is finished. Also i don't understand why do you need ng-change function in this case.
Here is modified code you need from your plunk where values of ng-models are initiated after options are loaded.
JavaScript:
.controller('MainCtrl', function($scope, $http) {
$scope.someStaticArrayOfValues = ['One', 'Two', 'Three'];
$scope.mySelectedValues = {};
$http.get('options.json').then(
function (response) {
$scope.someDynamicArrayOfValues = response.data;
for (var i = 0; i < $scope.someStaticArrayOfValues.length; ++i) {
$scope.someDynamicArrayOfValues.some(function (value) {
if (value.substring(0, $scope.someStaticArrayOfValues[i].length) === $scope.someStaticArrayOfValues[i]) {
$scope.mySelectedValues[$scope.someStaticArrayOfValues[i]] = value;
return true;
}
});
}
},
function (response) {
console.log('ERROR!');
}
);
});
HTML:
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div ng-show="someDynamicArrayOfValues">
<ul>
<li ng-repeat="staticValue in someStaticArrayOfValues">
{{staticValue}} -
<select ng-model="mySelectedValues[staticValue]"
ng-options="myValue for myValue in someDynamicArrayOfValues">
<option value="">--</option>
</select>
<h2>{{mySelectedValues[staticValue]}}</h2>
</li>
</ul>
</div>
</body>
Demo: http://plnkr.co/edit/9Q1MH0esGE1SIJa0m2NV?p=preview
Here is a modified plunker that works as intended: http://plnkr.co/edit/Y8OSvmrG3u0XjnCU3ah5?p=preview.
The main change was using ng-if in place of ng-show. This forces angular to recompile/link the html whenever it is rendered:
<div ng-if="someDynamicArrayOfValues">
...
</div>
Also ng-change, from the original plunker, shouldn't be necessary, and there were a couple of typos fixed.
It works a whole lot better when you use ng-options on your select element instead of nesting option with ng-repeat.
https://docs.angularjs.org/api/ng/directive/select
Then you are capable of setting the ng-model with ng-init.
You can try to set the initial value of mySelectedValue in your Controller like so:
$scope.mySelectedValue = '';
I have created example for your problem in plnkr.
Visit: plnkr.co/edit/rKyjijGWSL1IKy51b8Tv?p=preview
You are going about it the reverse way. ng-model reflects the state of the <select> and is two-way bound.
You just need to set your mySelectedValue to what you want <select> to select first, and no other tricks are required.
So, in the controller, do something like the following:
$scope.mySelectedValue = someDynamicArrayOfValues[0];
And remove the ng-selected and the <option ng-repeat...> from <select>:
<select ng-model="mySelectedValue"
ng-options="value for value in someDynamicArrayOfValues">
<option value="">--</option>
</select>

AngularJS : Custom filters and ng-repeat

I'm an AngularJS newbie and I'm building up a small proof-of-concept car hire listings app that pulls in some JSON and renders out various bits of that data via an ng-repeat, with a couple of filters:
<article data-ng-repeat="result in results | filter:search" class="result">
<header><h3>{{result.carType.name}}, {{result.carDetails.doors}} door, £{{result.price.value}} - {{ result.company.name }}</h3></header>
<ul class="result-features">
<li>{{result.carDetails.hireDuration}} day hire</li>
<li data-ng-show="result.carDetails.airCon">Air conditioning</li>
<li data-ng-show="result.carDetails.unlimitedMileage">Unlimited Mileage</li>
<li data-ng-show="result.carDetails.theftProtection">Theft Protection</li>
</ul>
</article>
<h2>Filters</h2>
<h4>Doors:</h4>
<select data-ng-model="search.carDetails">
<option value="">All</option>
<option value="2">2</option>
<option value="4">4</option>
<option value="9">9</option>
</select>
<h4>Provider:</h4>
Atlas Choice <input type="checkbox" data-ng-model="search.company" ng-true-value="Atlas Choice" ng-false-value="" value="Atlas Choice" /><br>
Holiday Autos <input type="checkbox" data-ng-model="search.company" ng-true-value="Holiday Autos" ng-false-value="" value="Holiday Autos" /><br>
Avis <input type="checkbox" data-ng-model="search.company" ng-true-value="Avis" ng-false-value="" value="Avis" /><br>
Now I want to create a custom filter in my controller, that can iterate over the items in my ng-repeat and return only the items that meet certain criteria - for example, I might create an array of values based on which 'provider' checkboxes are checked, then evaluate each ng-repeat item against that. I just can't work out how I'd do that though, in terms of the syntax - can anyone help?
Here's my Plunker:
http://plnkr.co/edit/lNJNYagMC2rszbSOF95k?p=preview
If you want to run some custom filter logic you can create a function which takes the array element as an argument and returns true or false based on whether it should be in the search results. Then pass it to the filter instruction just like you do with the search object, for example:
JS:
$scope.filterFn = function(car)
{
// Do some tests
if(car.carDetails.doors > 2)
{
return true; // this will be listed in the results
}
return false; // otherwise it won't be within the results
};
HTML:
...
<article data-ng-repeat="result in results | filter:search | filter:filterFn" class="result">
...
As you can see you can chain many filters together, so adding your custom filter function doesn't force you to remove the previous filter using the search object (they will work together seamlessly).
If you still want a custom filter you can pass in the search model to the filter:
<article data-ng-repeat="result in results | cartypefilter:search" class="result">
Where definition for the cartypefilter can look like this:
app.filter('cartypefilter', function() {
return function(items, search) {
if (!search) {
return items;
}
var carType = search.carType;
if (!carType || '' === carType) {
return items;
}
return items.filter(function(element, index, array) {
return element.carType.name === search.carType;
});
};
});
http://plnkr.co/edit/kBcUIayO8tQsTTjTA2vO?p=preview
You can call more of 1 function filters in the same ng-repeat filter
<article data-ng-repeat="result in results | filter:search() | filter:filterFn()" class="result">
One of the easiest ways to fix this is to use the $ which is the search all.
Here is a plunker that shows it working. I have changed the checkboxes to radio ( because I thought they should be complementary )..
http://plnkr.co/edit/dHzvm6hR5P8G4wPuTxoi?p=preview
If you want a very specific way of doing this ( instead of doing a generic search ) you need work with functions in the search.
The documentation is here
http://docs.angularjs.org/api/ng.filter:filter

Resources