Exclude selected options from multiple dropdowns in angularjs? - angularjs

I have five dropdowns. All five dropdowns have same source for options. An option selected in one dropdown should not appear in options of remaining dropdowns. What is best way to achieve it in angularjs?
And source for ng-options is array of objects.

You'll need to come up with an implementation that suits your specific needs and desired user experience, but this may help you work out some basic principles you can use. This is just the most minimal example I could think of. The main point is the custom filter that you'll filter ng-options down with. It will receive your array of objects (the options), and you'll also pass in the other models so that you can filter them out if they're selected.
Here, I'm looping over each selected object and removing it from the ng-options array, unless it's the object selected on this element.
angular.module('myApp', [])
.filter('customFilter', function(filterFilter) {
return function(input, filterEach, exclude) {
filterEach.forEach(function(item) {
if (angular.equals(item, exclude)) { return; }
input = filterFilter(input, '!'+item);
});
return input;
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.4.0/lodash.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
<div ng-app="myApp" ng-init="foos = [{label:'label 1',id:1},{label:'label 2',id:2},{label:'label 3',id:3}]; selected = []">
<div ng-repeat="foo in foos">
<select ng-model="selected[$index]" ng-options="obj.id as obj.label for obj in foos | customFilter:selected:selected[$index]"></select>
</div>
</div>

Related

ngRepeat doesn't refresh rendered value

I'm having an issue with ngRepeat :
I want to display a list of students in two different ways. In the first one they are filtered by group, and in the second they are not filtered.
The whole display being quite complex, I use a ngInclude with a template to display each student. I can switch between view by changing bClasseVue, each switch being followed by a $scope.$apply().
<div ng-if="currentCours.classesOfGroup !== undefined"
ng-show="bClassesVue">
<div ng-repeat="group in currentCours.classesOfGroup">
<br>
<h2>Classe : [[group.name]]</h2>
<div class="list-view">
<div class="twelve cell"
ng-repeat="eleve in group.eleves | orderBy:'lastName'"
ng-include="'liste_eleves.html'">
</div>
</div>
</div>
</div>
<div class="list-view" ng-show="!bClassesVue">
<div class="twelve cell"
ng-repeat="eleve in currentCours.eleves.all"
ng-include="'liste_eleves.html'">
</div>
</div>
My problem happens when my list of students change (currentCours here). Instead of refreshing the ngRepeat, both lists concatenate, but only in the unfiltered view.
I tried adding some $scope.$apply in strategic places (and I synchronize my list for example) but it doesn't help.
EDIT : the function used to refresh currentCours in the controller. It's called when a "cours" is selected inside a menu.
$scope.selectCours = function (cours) {
$scope.bClassesVue = false;
$scope.currentCours = cours;
$scope.currentCours.eleves.sync().then(() => {
if ($scope.currentCours.classe.type_groupe === 1) {
let _elevesByGroup = _.groupBy($scope.currentCours.eleves.all, function (oEleve) {
return oEleve.className;
});
$scope.currentCours.classesOfGroup = [];
for(let group in _elevesByGroup) {
$scope.currentCours.classesOfGroup.push({
name: group,
eleves: _elevesByGroup[group]
});
}
$scope.bClassesVue = true;
}
});
utils.safeApply($scope);
};
Well, I found a workaround, but I still don't know why it didn't work, so if someone could write an explanation, I would be very thankful.
My solution was simply to open and close the template each time I switch between views.

Angular - how to make a an option field (with ng-repeat) depend on previously chosen value in option field?

I have 3 fields.
1st - option field / ng-repeat over available dates
2nd - option field / based on the date chosen by user, I do ng-repeat over quantity. I have tried all different ways but I can't make it depend on the first option field or other things don't work. Any help would be great! Thanks !!
html:
<div class="containerDiv">
<div>
<select ng-model='date'>
<option ng-repeat="availableDateProduct in product " value='{{i}}'>{{availableDateProduct.dateOfActivity}}
</option>
</select>
</div>
<div ng-repeat="availableDateProduct in product ">
<select>
<option ng-repeat='i in quantityLister(availableDateProduct.quantity)' value='{{i}}'>
{{i}}
</option>
</select>
</div>
<div>
<button>Book</button>
</div>
</div>
js:
app.controller('ProductCtrl', function($scope, ProductsFactory, $stateParams) {
ProductsFactory.fetchByTitle($stateParams.title)
.then(function(product) {
$scope.product = product;
})
$scope.quantityLister = function(num) {
var array = [];
for (var i = 1; i <= num; i++) {
array.push(i)
}
return array;
}
})
data:
var products = [
{
title:'Bowling',
description:'Its fun!',
photoUrl:'https://www.1.jpg',
quantity:12,
price:9,
dateOfActivity: '2017-13-07'
},
...
]
Thanks!!
Angular has a directive built specifically to accomplish this task; ng-options.
First, we define an object in the controller that will hold the values selected from the dropdown:
$scope.reservation = {};
Next, we use ng-options on our dropdowns, and use the ng-model property to accept the value selected. In the first dropdown, we take the array of products, display the dateOfActivity for each product, and save the product object to ng-model when selected. (work from right to left in the ng-options definition).
ng-model="reservation.selectedProduct"
ng-options="product as product.dateOfActivity for product in products"
In our second dropdown, you have defined a function to take a number and spread it into an array. We call this function from reservation.selectedProduct.quantity, and then use this array as the basis for the ng-options:
ng-model="reservation.selectedQuantity"
ng-options="num for num in quantityLister(reservation.selectedProduct.quantity)"
Now we have an object which has the selected values for both dropdowns, we just need to change the quantity in the original array on button press. we also want to clear the selections afterwords, to ensure that the user can't accidentally make a duplicate reservation.
$scope.reserve = function (){
$scope.reservation.selectedProduct.quantity -= $scope.reservation.selectedQuantity;
$scope.reservation = {};
};
Here we use the shorthand -= to subtract the selectedQuantity from the selectedProduct.quantity. since selectedProduct is two way bound, the change to selectedProduct is reflected in the original object in the product array as well. However, The quantityLister function isn't dynamic; if we don't reset $scope.reservation, the second dropdown would hold a now invalid number of available reservations.

Unable to render the values in drop down list

I have a JSON text and three dropdown lists.
First dropdown list will be populating Entities (Applicant, Person, Plan).
Second dropdown list will be populating Association Types of selected Entity from previous selection.
(Here ‘Person’ and ‘Plan’ will be rendered by selecting “Applicant” as Entity)
The third dropdown list should populate the Attribute values. (Based on selection of Association Type from the second dropdown list, it should display the attributes of respective Entity)
Example: If I select Person Association type in 2nd dropdown list then Attributes of Person Entity has to be displayed.
I have tried solving this problem.
I could get the values for the first two dropdown lists. I have tried rendering the values however I am facing some problems.
Can someone help me how to get the values??
http://jsbin.com/toqisibumo/edit?html,js,output
If you dont understand question, Please dont hesitate to ask. Thanks
Hope this solve your issue.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<select ng-model="dd1" ng-options="item.Entity for item in data">
</select>
<select ng-model="dd2" ng-options="ass.associationType for ass in dd1.Associations" ng-change="loadDD3()">
</select>
<select ng-model="dd3" ng-options="atr.name for atr in dataDD3">
</select>
</body>
<script>
var angular = angular.module('myApp', []);
angular.controller('myCtrl', function($scope,$http) {
$scope.data = [
{
"Attributes":[
{"name":"CSRPercent","attributeType":"decimal"},
{"name":"date","attributeType":"date"},
{"name":"hoursPerWeek","attributeType":"long"},
{"name":"householdSize","attributeType":"long"},
{"name":"income","attributeType":"decimal"},
{"name":"incomePeriod","attributeType":"string"},
{"name":"isEligibleForCostSharingReduction","attributeType":"boolean"},
{"name":"isEligibleForIncomeBasedMedi_CAL","attributeType":"boolean"},
{"name":"isEligibleForMedi_Cal_Access","attributeType":"boolean"},
{"name":"isEligibleForPremiumAssistance","attributeType":"boolean"},
{"name":"state","attributeType":"string"},
{"name":"zip","attributeType":"string"}
],
"Associations":[
{"associationType":"Person","name":"familyMembers"},
{"associationType":"Plan","name":"plans"}
],
"Entity":"Applicant"
},
{
"Attributes":[
{"name":"age","attributeType":"long"},
{"name":"isPregnant","attributeType":"boolean"}
],
"Entity":"Person"
},
{
"Attributes":[
{"name":"company","attributeType":"string"},
{"name":"costPerPerson","attributeType":"decimal"},
{"name":"name","attributeType":"string"},
{"name":"premiumAssistance","attributeType":"decimal"},
{"name":"stars","attributeType":"long"},
{"name":"totalMonthlyPremium","attributeType":"decimal"},
{"name":"yourMonthlyPremium","attributeType":"decimal"}
],
"Entity":"Plan"
}
];
$scope.dataDD2 = [];
$scope.loadDD3=function(){
for(var i = 0; i < $scope.data.length; i++) {
var obj = $scope.data[i];
if(obj.Entity == $scope.dd2.associationType)
{
$scope.dataDD3 = obj.Attributes;
}
}
};
});
</script>
</html>
But still I have some issues.
For the dropdown1 you have three values(Applicant, Person, Plan).
If you select Applicant you will load Associations(Person, Plan) to the dropdown2.
What if you select either Person or Plan in dropdown1 ?
For those two you don't have associations ryt.
First of all, we can't test your jsbin, because the get request fails (ERR_NAME_NOT_RESOLVED)
Secondly, I see this line of code:
<select ng-model="z" ng-change="addAttributes()" ng-options="sel.name for sel in z.Attributes">
You've set the ng-model to 'z', and then try to get the options from 'z.Attribute', but your 'z' model is not populated with anything. Maybe you wanted to make y.Attributes ?
UPDATE:
I think you are having troubles with your JSON data.
here's an updated link.
Select Applicant -> Person -> FamilyMembers
is this the behaviour you want?
If so, look how I've modified your select:
<select ng-model="z" ng-change="addAttributes()" ng-options="color.name for color in y.names">
and this is how I've modified your JSON data:
...
"Associations":[{"associationType":"Person","names":[{"name" : "familyMembers"}]}
...
so names is an array containing your values.
I checked your codes.
In the third drop-down your loading the data from
sel.name for sel in z.Attributes
Where it has to be
sel.name for sel in x.Attributes
Then you will get the dropdown

Observing nested properties in Ember controller

I'm in the middle of a small project involving Ember. It's my very first time at working with this framework and it has not been an easy learning so far :(
Right now I'm having troubles dealing with nested arrays. What I want to do is pretty standard (at least it seems that way): I have items, item categories and category types (just a way to organize them).
The idea is that there are checkboxes (categories) that allow me to filter the items that are shown in the webpage. On the other hand, there are checkboxes (types) that allow me to check multiple catgories at a time.
In order to implement this I've defined a route (in which I retrieve all the data from these models) and a controller. Originally, I only had items and categories. In this context, I observe the changes in the filters (categories) like this: categories.#each.isChecked and then show the item selection. Unfortunately, now that the hierarchy is types->categories, is not possible to observe changes in categories in the same manner according to the docs:
Note that #each only works one level deep. You cannot use nested forms like todos.#each.owner.name or todos.#each.owner.#each.name.
I google a little bit but didn't find too much about it, so I right now I was thinking in using a custom view for categories (one that extends the Ember.Checkbox) and send an event to the controller whenever a category is checked or unchecked. Is more of a "manual" work and I guess is far from Ember's way of dealing with this type of things.
Is there a standard way of doing this?
Thanks in advance for any help.
One way of solving this would be to observe the category types and filter categories, the same way that the categories are being observed.
This is an example,
http://emberjs.jsbin.com/naqebijebapa/1/edit
(one to many relationships have been assumed)
hbs
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each catType in catTypes}}
<li>{{input type="checkbox" checked=catType.isSelected }}{{catType.id}} - {{catType.name}}</li>
{{/each}}
</ul>
<hr/>
<ul>
{{#each cat in filteredCats}}
<li>{{input type="checkbox" checked=cat.isSelected }}{{cat.id}} - {{cat.name}}</li>
{{/each}}
</ul>
<hr/>
<ul>
{{#each item in filteredItems}}
<li>{{item.id}} - {{item.name}}</li>
{{/each}}
</ul>
</script>
js
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.CategoryType = Em.Object.extend({
id:null,
name:null,
isSelected:true
});
App.Category = Em.Object.extend({
id:null,
name:null,
type:null,
isSelected:false
});
var catTypeData = [
App.CategoryType.create({id:1,name:"type1"}),
App.CategoryType.create({id:2,name:"type2"}),
App.CategoryType.create({id:3,name:"type3"}),
App.CategoryType.create({id:4,name:"type4"})
];
var catData = [
App.Category.create({id:1,name:"cat1",type:catTypeData[1]}),
App.Category.create({id:2,name:"cat2",type:catTypeData[2]}),
App.Category.create({id:3,name:"cat3",type:catTypeData[0]})
];
var itemsData = [
{id:1,name:"item1",cat:catData[0]},
{id:2,name:"item2",cat:catData[0]},
{id:3,name:"item3",cat:catData[1]}
];
App.IndexRoute = Ember.Route.extend({
model: function() {
return Em.RSVP.hash({catTypes:catTypeData,cats:catData,items:itemsData});
}
});
App.IndexController = Ember.ObjectController.extend({
filteredItems:[],
filterItemsBasedOnCategory:function(){
var selectedCats = this.get("cats").filterBy("isSelected");
if(!Em.isEmpty(selectedCats))
this.set("filteredItems",this.get("items").filter(function(item){
return selectedCats.contains(item.cat);}));
else
this.set("filteredItems",[]);
}.observes("cats.#each.isSelected"),
filterCatsBasedOnCategoryType:function(){
var selectedCatTypes = this.get("catTypes").filterBy("isSelected");
if(!Em.isEmpty(selectedCatTypes))
this.set("filteredCats",this.get("cats").filter(function(cat){
var itContainsIt = selectedCatTypes.contains(cat.type);
if(!itContainsIt){
cat.set("isSelected",false);
}
return itContainsIt;
}));
else
this.set("filteredCats",[]);
}.observes("catTypes.#each.isSelected")
});

Use checkboxes in angularjs to manage array of objects

I had asked a question about
How to associate objects as models using ng-options in angularjs.
And I got an awesome answer very fast. My followup questions is that the response uses <select mutiple> to handle the child object array.
You can see a working example of what I want, working with <select> at http://plnkr.co/edit/FQQxrSE89iY1BnfumK0A?p=preview
How can I use <input type='checkbox'> (instead of <select>) to handle that object array i.e. ng:model="shirt.colors" while repeating the items from colors object array.
The reason, this appears so complicated to me is that I have to manage an array of objects instead of array of values... for example, if you look in the fiddle, there are color objects and shirt object that has multiple colors.
If the color object changes, it should change the corresponding color objects in shirt objects.
Thank you in advance.
You just need some intermediate value in your scope, and bind checkboxes to it. In your controller - watch for it changes, and manually reconstruct shirt.colors, according to it value.
<div ng-repeat='shirt in shirts'>
<h3>Shirt.</h3>
<label>Size: <input ng-model='shirt.size'></label><br/>
<label>Colors:</label>
<label ng-repeat="color in colors">
{{color.label}} <input ng-model="selection[$parent.$index][$index]" type="checkbox"/>
</label>
</label>
</div>
And in your controller:
$scope.selection = [[],[]];
$scope.$watch('selection', function () {
console.log('change', $scope.selection);
angular.forEach($scope.selection, function (shirtSelection, index) {
$scope.shirts[index].colors = [];
angular.forEach(shirtSelection, function (value, index2) {
if (value) $scope.shirts[index].colors.push($scope.colors[index2]);
});
});
}, true);
You can test it here: http://plnkr.co/edit/lh9hTa9wM5fkh3nT09RJ?p=preview

Resources