Chaining Selects with Angular JS - javascript

I am brand new, just experimenting, with AngularJS framework. So I am not sure the approach I have taken is the best/right way.
I'm trying to create a 3 level chained ajax filled select boxes, and it is kind of working, except for a couple of issues.
My actual code uses ajax which is in a factory, but for the sake of the fiddle I just have a controller returning results from an array.
Demo (using arrays, not ajax): http://jsfiddle.net/xxwe1zu8/1/
Issues:
2nd and 3rd level selects don't have a "selected" attribute - ie the one you select doesn't get highlighted.
Ideally I would like the top level categories to be dynamically filled on page load via ajax (or in this example by array) using the same angular function (eg: getSubcategories = function(0, 0) {) rather than hardcoded. Top level categories have a parent of 0.
Bonus: Can the 3rd select box only be shown/visible if there is a sub sub category returned after selecting the sub category? In reality, most sub categories won't have sub sub categories.
var myAppModule = angular.module('myApp',[]);
myAppModule.controller('SearchCtrl', function($scope) {
var self = this;
self.subCategories = [];
self.getSubcategories = function(parent, level) {
theCategories = [];
angular.forEach(sub_category[parent], function(idx, val) {
theCategories.push({id: val, name: idx});
});
self.subCategories[level] = theCategories;
}
});
Thanks

I would suggest to restructure data in an array of objects and get rid of indicies which are seemingly of no use here.
var transport = [
{
name: "Cars",
models: [
{
name: "Hatchback",
variations: ["4 door", "5 door"]
},
{
name: "Sedan",
variations: ["4 door", "5 door"]
},
{
name: "SUV",
variations: ["Medium", "Large"]
}
]
},
...
This allows to make clearer code in template. I am not ControllerAs syntax here since $scope is injected in any way. And it is a better approach if you just start learning AngularJS
<select
ng-model="selectedType"
ng-options="t.name for t in transport"
ng-change = "selectedModel=null;selectedVariation=null">
<option value="">Select type</option>
</select>
<select
ng-model="selectedModel"
ng-options="model.name for model in selectedType.models"
ng-change="selectedVariation=null"
ng-show="selectedType.models">
<option value="">Select model</option>
</select>
<span ng-show="loading">Loading...</span>
<select
ng-model="selectedVariation"
ng-options="variation for variation in variations"
ng-show="variations && !loading">
<option value="">Select variation</option>
</select>
selectedType, selectedModel and selectedVariation are implicitly defined in $scope by ng-model of each select. There is even no mention of them in controller currently. At the same time this properties are used in ng-show to hide select tags which are not relevant in current selection.
The last select (sub subcategory) demonstrates a way to fetch data asynchronously. Let's imagine you don't have a complete tree of data at once and fetch variations from server on model selection. You would place a watch in controller for selectedModel and if it was selected (not emptied) you would launch a request for data and update variations on response
$scope.$watch('selectedModel', function(model){
if(model){
$scope.loading = true;
//setTimeout imitates an ajax request
setTimeout(function(){
$scope.variations = model.variations;
$scope.loading = false;
$scope.$apply();
}, 1000);
}
})
Updated fiddle

Related

Ng-init in the selection for default value with AngularJS doesn't work

In web app I have a list of Expenses, which that if you press above it takes you to a new page to edit the selected Expense. When the user click on an element of list, I save the Object (the selected Expense) in a Global Object common in whole application and I retrieve it when I am in the Editing controller:
$scope.companySelected = GlobalApplicationData.selectedExpenseList;
The GlobalApplicationData.selectedExpenseList is an Object:
$$hashKey: "object:39"
_id: "33aa5549-7802-40d9-bc89-8780705b8c9b"
_rev: "3-eb940cb990524112723f711618e0cf51"
ad_table_id: 1000596
company_name: "Company 1"
data: Object
recordid: 1000005
__proto__: Object
In one expense there is only one company.
So, in this page of editing I have some field (input type text, date, etc). And I also have a selection with the list of the Companies of the logged user. To retrieve that list, I made this function:
$scope.getCompanyList = function() {
EditExpenseListSrv.getCompanyListDetail(user).then(function (companyListDetail) {
$scope.companyList = companyListDetail;
$scope.$apply();
}).catch(function (err) {
console.log(err);
});
};
CompanyListDetail is an array of Obejct:
companyListDetail: Array[5]
0: Object
1: Object
2: Object
3: Object
4: Object
And this is one example of those object:
_id: "44e620dc-e715-453f-882b-3f21aeef48fe"
_rev: "1-c09c9f3350e853e588f3358aaafc0374"
ad_table_id: 1000146
data: {
description: "text"
id_company: 513424
name: "Company 2"
}
recordid: 1000002
So the company of the selected Expense ($scope.companySelected) will definitely part of the list obtained by the query ($scope.companyList). But I want to give the user the possibility to change it.
So I would like to make a selection containing the list ($scope.companyList) and default already selected the Company corresponding to $scope.companySelected.
I've writter this, but it doesnt work:
<select class="selection" ng-model="companySelected"
ng-init="companySelected = globalApplicationData.selectedExpenseList"
ng-options="company
as company.data.name for company in companyList">
But it doesn't work. I can see in the selection all the Companies, but don't select the default value
When you are assigning a default value to the select, you need to assign the object, so it won't work, when assigned with the same data of some other object:
Note:
a) data is the array which is getting looped in the select list
b) selectedcountry is the model which is bind on select list.
Option 1:
Using ng-init:
<select ng-model="selectedcountry" ng-init="selectedcountry = data[0]" ng-options="item.name for item in data">
<option value="">Select</option>
</select>
Demo 1.
Option 2:
Assigning the default from the controller
$scope.selectedcountry = $scope.data[0];
Demo 2
You can try to do the initialisation in the controller:
$scope.companySelected = "the value that you want to set here"
Or you can try to put the ng-init inside the parent of the select. Once I had a problem like this and I solved it putting the ng-init in the parent tag. Example:
<div id="parent" ng-init="companySelected = globalApplicationData.selectedExpenseList">
<select ...>
</div>
Another idea would be to put companySelected inside an object. I have had some problems (I am not sure why) with the forms if I was using $scope.value inside the ng-value instead of using $scope.formData.value

ngOptions weird behavior when trying to use resource response as selected item

I tried to solve this over an hour, haven't found anything online.
This is the template:
<select
ng-model="user.platform_id"
ng-options="platform.id as platform.title for platform in platforms"
id="platform_id" class="form-control">
</select>
and this is the controller :
$scope.platforms = platformsEntity.query();
// Resource object coming from UI Router resolve, already fetched with user info
$scope.user = userEntity;
the platforms resource response return the following format:
[ { id: 4, title: 'platform1'}, { id: 5, title: 'platform2' ]
the user is same, resource object with platform ID
{ id: 3, name: 'foo', platform_id: 5 }
This way I can see all the platforms in the select box but it's not getting selected by the ngModel value (user.platform_id)
The thing is that when I use plain object instead of resource:
$scope.platforms = platformsEntity.query();
$scope.user = { platform_id: 5 } ;
It is getting selected correctly ...
Update
i replicated the behavior i am looking for in ng-repeat:
<select ng-model="user.platform_id" id="platform_id" class="form-control">
<option
value="{{ platform.id }}"
ng-selected="user.platform_id == platform.id"
ng-repeat="platform in platforms"
>{{ platform.title }}</option>
</select>
It's not about using a resource, the problem with programmatic selection of ng-options is that the ng-model value should refer to the same object as in the ng-options. In order to initialize the selection, first make sure that the platforms array is populated (query might be async?), then loop through them, and set the id object of the platform of your choice as the platform_id object of the user.
Try something like this. Tell angular what you want to track the value by.
<select
ng-model="user.platform_id"
ng-options="platform.title for platform in platforms track by platform.id"
id="platform_id" class="form-control">
</select>

Select default value at ng-options

I have this code and information:
$scope.options = [
{ id: 1, label: "My label" },
{ id: 2, label: "My label 2" }
];
$scope.selected = 2;
<select ng-options="opt.label for opt in options" ng-model="selected">
<option value="">Select one</option>
</select>
However, the options are created like this:
<select ng-options="opt.label for opt in options" ng-model="selected">
<option value="">Select one</option>
<option value="0">My label</option>
<option value="1">My label 2</option>
</select>
How can I set the selected option to My label 2? I know it can be done:
$scope.selected = $scope.options[1];
But my problem is that option is created in a directive, and at that moment I don't know 1) how many values has $scope.options, nor 2) what is the index of the selected option in database. The only thing I know is the id (which is in the object).
The html of my directive is something like this:
<select ng-switch-when="select" ng-model="form.model[element.model]" ng-options="{{element.rule}}"></select>
Where element.rule is:
rule: "role.role for role in element.options"
And element.options has the array with options, and form.model[element.model] has the id of the option selected.
How can I get the index of the element with ID X in the array $scope.options? I'm very sure that will give me the solution, but I don't know how to do it...
Just set the correct model value when initiating the controller. You can easily get the correct array value if you know the ID by using a filter:
$scope.selected = $filter('filter')($scope.options, {id: 2})[0];
The issue with your code as I see it is that the 'selected' value coming out of your database is the ID of the object selected and not the object itself. This is fine but because of that difference you can't simply set
$scope.selected = 2 //assuming 2 is the value coming from your database
because the value '2' does not exist in your option array. The Object with an ID of 2 does.
If you can always guarantee the options you have in the option array are from 1-n and in that order, you can accomplish this by simply using this instead:
$scope.options = [
{ id: 1, label: "My label" },
{ id: 2, label: "My label 2" }
];
var selectedIdFromDatabase = 2;
$scope.selected = $scope.options[selectedIdFromDatabase-1];
If you can't make that guarantee (and even if you can for now, it may not be a good idea to make that assumption in your code) you'll need iterate over the array of objects you have to identify the object with an ID of the selectedId from your database.
The answer to this question has a great write-up about the type of data-processing you'll need to do and a lot more information about javascript objects in general.

angularjs - remove option from input select

Can't figure this out for the life of me. Using AngularJS.
I have a dropdown Select field with several options. It is a part of a form that may be completed multiple times (ie "add another" type form). Now, one of the options may only be used once. How can I remove this option from all other select fields after it has been used?
What I'm working with:
html
<select ng-model="item.itemtype">
<option ng-repeat="i in itemtype" value="{{i}}" ng-init="item.itemtype = itemtype[0]">{{i}}</option>
</select>
angularjs
$scope.Items = [
{ 'itemtype': '', 'desc': '', 'color': '' }
];
$scope.itemtype = [
'shirt',
'pants',
'hats',
'shoes',
'special'];
What I've tried (and really doesn't work)
$scope.specialremove = function() {
var exist = Items.indexOf("special")
if (exist !== 0) {
return '';
}
else {
return 'special';
}
}
I'm hoping I don't have to turn to any other framework to solve this. Feel free to point out any other problems/errors/inefficiencies in my code.
The first thing that can help is using ng-options:
ng-options="type for type in itemType".
It would be better to have objects with label and value properties, in order to write it as
ng-options="type.value as type.label for type in itemType"
and separate the displayed label from the actual value of the selection.
In your controller you should have something like:
$scope.itemType= [
...
];
$scope.selectedItem= $scope.itemType[0];
So that you can write the select as:
<select ng-Model="selectedItem" ng-options="item.value as item.label for item in itemType"></select>
To remove a selected item you can watch the value of selectedItem in the controller: when it matches the value you want, remove it from the array and update selectedItem accordingly.
Here is a basic fiddle. I simply remove the third option after selecting it, and reset the selected item to the first.

Conditionally including select options in angular

I'm using an angular select directive.
<select
ng-model="tune.rating"
ng-options="opt.value as opt.label for opt in dropdowns.rating"
ng-change="update()" >
<option ng-if="tune.rating === -1" value="">-- rate tune --</option>
</select>
I want the default option to only be included when a rating isn't already given, but the ng-if isn't getting applied (both when the controller is loaded and in response to selecting a value)
Is there some way I can get this to work. I can't append the default option conditionally to dropdowns.rating as there are many tunes per page, all of which may be rated or unrated and all of which use the dropdowns.rating array
Note: I don't think my question duplicates this one angularjs Select with filter to filter out the option that's already selected, although they are similar
Use $templateCache and a for loop as an alternative:
var app = angular.module('foo', []);
function foo($templateCache)
{
var tmpl, lister,
ProductData =
{ "odata.metadata": "localhost:51811/odata/$metadata#Products",
"value": [
{
"ID": 1,
"Year": 2017,
"Name": "Hat",
"Price": "15.00",
"Category": "Apparel"
}
]
};
lister = function()
{
var index, replacement = "";
for (index in this)
{
/* Avoid adding the callback function itself to the array */
if (/[^\d]/.test(this[index]) === false)
{
replacement = replacement.concat("<option>",this[index],"</option>");
}
}
return replacement;
};
ProductData.value[0].toJSON = lister;
tmpl = JSON.stringify(ProductData.value[0]);
console.log(tmpl);
$templateCache.put('listContent', tmpl);
}
app.run(foo);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="foo">
<select ng-include="'listContent'"></select>
</div>
References
Using a toJSON Method with JSON.stringify
.toJSON() | Sails.js Documentation
What you didn’t know about JSON.Stringify | CodeKraft
TypeScript: Working with JSON · Choly's Blog

Categories

Resources