AngularJS - radio checked based on object value - angularjs

What I'm trying to do is have a service (lets say: myService) that holds specific data like objects representing printers present and selected:
var localPrinters = [{ id: 12, name: 'HP', type: 'laser' },
{ id: 33, name: 'Lexmark', type: 'laser' }];
var activePrinter = {};
In some view that gets shown occasionally (like app settings), I have a controller that would define variables in the local scope which would point to the objects in the injected myService.
The view would then use ng-repeat to iterate over printer objects in localPrinters and display radio buttons that correspond to each object in the array..
Now i need two things..
1) update the activePrinter upon radiobutton selection change with the corresponding object value
2) in case the activePrinter already contains an object, when the view loads i want the corresponding radio to be checked already (if its value object matches the object in activePrinter, otherwise none should be selected.
I've managed 1) in a couple of ways.. either sticking to the model usage or adding methods to call upon ng-change.
//pseudocode
<container ng-repeat="printer in printers" >
<radio ng-value="printer" ng-model="$scope.activePrinter"/>
</container>
or
//pseudocode
<container ng-repeat="printer in printers" >
<radio ng-value="printer" ng-change="selectPrinter(printer)" "ng-model="$scope.activePrinter"/>
</container>
What i'm having trouble with is 2)
Not sure if there's a way in angular to automatically figure out some of the printer values matches the activePrinter selection and make the radio checked. Also not sure of the way i'm using ng-model for this purpose.
Any pointers?
Thanks!

You can do that in this way:
var app = angular.module('app', []);
app.controller('firstCtrl', function($scope) {
$scope.printers = [{
id: 12,
name: 'HP',
type: 'laser'
}, {
id: 33,
name: 'Lexmark',
type: 'laser'
}];
$scope.activePrinter = {};
//set default printer
$scope.activePrinter.printer = $scope.printers[0]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="firstCtrl">
<div class="container" ng-repeat="printer in printers">
<label>{{printer.name}}</label>
<input type="radio" ng-value="printer" ng-model="activePrinter.printer" />
</div>
Active Printer:{{activePrinter.printer.id}} | {{activePrinter.printer.name}} | {{activePrinter.printer.type}}
</div>
</body>

Related

AngularJs - Select checkboxes by binding list of selected objects to list of all objects

I have searched for a solution to my problem but have not been able to find the right answer.
I have a list of all questions and a list of selected questions. I need to build a list of checkboxes from the list of all questions and check the ones that are in the list of selected questions. When changes are made to the checkboxes, I need the list of selected questions to be updated accordingly. The list of all questions never changes. How can I accomplish this? Here's a very abbreviated version of my situation:
var MyApp = angular.module('MyApp', []);
MyApp.controller('MyController', ['$scope', function ($scope) {
$scope.allQuestions = [
{ q_id: 1, q_txt: 'What time is it?', q_sort: 1},
{ q_id: 2, q_txt: 'What is that?', q_sort: 2},
{ q_id: 3, q_txt: 'Who are you?', q_sort: 4},
{ q_id: 4, q_txt: 'What color is that?', q_sort: 3}
];
$scope.selectedQuestions = [
{ q_id: 1, other_prop: 'yyy' },
{ q_id: 4, other_prop: 'zzz' },
];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyController">
<div ng-repeat="question in allQuestions"><input type="checkbox" ng-model="x" /> {{ question.q_txt }}</div>
</div>
In my application the lists come from a server and I would like to be able to return the selected questions list as an object back to the server when saving the changes.
I can't figure out the binding to accomplish this, or how to check the right checkboxes. Notice that the two lists are different, but bothe have the q_id value as the key to match them. Any help would be greatly appreciated.
It doesn't sound like you need to worry about which questions are selected until the user submits the form, so I'd just loop through the list of all questions at that time and create an array of the selected questions.
Something like this:
function getSelections() {
var selectedQuestions = []
$scope.allQuestions.forEach( function(question) {
if (question.selected) {
var questionCopy = angular.copy(question)
selectedQuestions.push(questionCopy)
}
})
return selectedQuestions
}
Then call this function before you post and pass the results to your API.
You should just be able to use ng-model to handle preselecting the checkboxes that should be preselected with:
<div ng-repeat="question in allQuestions"><input type="checkbox" ng-model="question.selected" /> {{ question.q_txt }}</div>
Something like this should do the trick. Using array.find() might be more efficient. Using a library like _ would make this easier.
$scope.selectedQuestions.map( function(selectedQuestion) {
var match = $scope.allQuestions.some( function(question) {
if (question.q_id == selectedQuestion.q_id) {
question.selected = true
return true
}
return false
}
}

How to run multiple successive select buttons in AngularJS

I facing an issue with having multiple selects in angularJS where each one of them is linked to the previous one and the value depended on the previous item selected which looks like could be done easily by angular but I am having a hard time figuring out how do I make the index of one select be passed to another select and at the same time making it unresponsive until some value is selected.
I also created a fiddle for the same for people to fiddle around with it.
Here is the concerned HTML
<div ng-app="myApp">
<div ng-controller="testController">
<select ng-model="carBrand" name="carBrand" required ng-options=" brand for brand in brands"></select>
<select ng-model="carModel" name="carModel" required ng-options="model.name for model in cars[0]"></select>
<!--I want the car brand(cars[0]) to be dynamic here. It should be prefreberably blacked out or uneditable until a Car brand is selected and once that particular brand is selected all the models pertaining to that brand only should be displayed in the ajoining select button-->
</div>
</div>
and an example app.js. Find the complete one at the fiddle
var app = angular.module('myApp', []);
app.controller("testController", function($scope) {
$scope.brands = ['Ford', 'Honda', 'Hyundai', 'Mahindra',
'Maruti Suzuki', 'Nissan', 'Renault', 'Skoda', 'Tata', 'Toyota', 'Volksvagen'
];
$scope.carBrand = $scope.brands[0];
$scope.cars = [];
/*These cars[0] and cars[1] are static declared but could well be called from a REST API endpoint in angular. For simplicity lets say they are already present. */
$scope.cars[0] = $scope.cars[0] = [{
name: "Figo",
capacity: 45
}, {
name: "Ecosport",
capacity: 52
}, {
name: "Fiesta",
capacity: 45
}, {
name: "Endeavour",
capacity: 71
}];
});
How do I solve the issue of getting an index from one select and passing it to the other to make this work and probably an additional perk would be to make it unresponsive in case no brand is selected.
Try ng-change:
<select ng-model="carBrand" name="carBrand" required ng-options=" brand for brand in brands"
ng-change="selectedCar(carBrand)"></select>
This returns the index of the selected brand:
$scope.selectedCar = function(brand) {
$scope.carIndex = $scope.brands.indexOf(brand);
};
Use it with the other dropdown as:
<select ng-model="carModel" name="carModel" required
ng-options="model.name for model in cars[carIndex]"></select>
Working Fiddle
When you select something from the first select, carBrand goes from undefined to the selected brand. You thus want the second select to be disabled if the carBrand is undefined (falsy):
<select ng-disabled="!carBrand" ...>
Then, you need to second select to contain the models associated to the selected brand (which is carBrand). So you need something like
<select ng-options="model.name for model in getModelsOfBrand(carBrand)" ...>
Now just implement this getModelsOfBrand(carBrand) function in the scope. It would be much easier if you had a better object model, like for example:
$scope.brands = [
{
name: 'Ford',
models: [
{
name: 'Figo',
capacity: 45
},
...
]
},
...
];
Then it would be as easy as
<select ng-options="model.name for model in carBrand.models" ...>

AngularJS filter based on the column displayed

Please refer to this Plunk: http://plnkr.co/edit/5YCCdDSnpHKRU51R9vCL?p=preview
How do I filter based on the field that is displayed? Currently, the filtering is done on the complete user object.
For eg:
$scope.users1 = [
{name: 'Dave', id: 24},
{name: 'Tim', id: 22},
{name: 'Laura', id: 20}
];
<user-info-card users="users" label="name">
</user-info-card>
If I display only the id, the filter works even if I type "Dave". I want it to work only on the fields displayed. Also want it to be generic. It could be a different object but the search term should look at the label field and filter accordingly
Added a function which will sort based on label, hope if suffice.
http://plnkr.co/edit/iCepagIKA1OFDjBnB35I?p=preview
$scope.searchTerm = function(item) {
if(item[$scope.label].toString().
startsWith($scope.searchCondition1)){
return true;
}
else{
return false;
}
}
You can use strict filter to do this stuff.
test.html
<div>
<input placeholder="Search for a name..." ng-model="searchTerm" ng-change="onChange()">
<div ng-repeat="user in users | filter:user:strict">
...
</div>
</div>
During text box key change event, search term is converted as object field value.
script.js
$scope.onChange = function () {
var user = {};
user[$scope.label] = $scope.searchTerm;
$scope.user = user;
};
Check this link

Unselected radio button value in Angularjs

I've got an angularjs application that has a form/controller that look essentially like this (boiled down to the pertinent stuff):
angular.module('testApp', [])
.controller('testCtrl', function ($scope) {
$scope.envelopes = [{
id: 1,
name: 'first',
default_spend: '1'
}, {
id: 2,
name: 'second',
default_spend: '0'
}, {
id: 3,
name: 'third',
default_spend: '0'
}, ];
});
And a form that looks roughly like this:
<div ng-app="testApp">
<div ng-controller="testCtrl">
<div ng-repeat="envelope in envelopes">
<div>{{envelope.name}}
<input type="radio" name="default_spend" ng-model="envelope.default_spend" ng-value="1" />
Default Spend: {{envelope.default_spend}}
</div>
</div>
</div>
</div>
You can see this in action with this fiddle.
As you can see, the first envelope is marked as the default_spend envelope and the other two aren't. When I select a different envelope, that envelope also gets marked as the default_spend, but when the radio button is unselected, the model value stays the same ("1"). I understand that I'm dealing with a child scope here due to ng-repeat, but is there a way for me to set an "unselected" value without having to jump through hoops with ngChange?
Not really. When you use ng-value it is what is going to get bound to the ng-model and in your case all of them are having value 1. Also i really did not get the purpose of toggling 1 and 0, however you could just achieve it this way:-
<input type="radio" name="default_spend"
ng-click="selected.envelope = envelope" /> <!--Register an ng-click and set selected one
Default Spend: {{getDefSpend(envelope)}}</div> <!-- Show the text based on selection-->
And in your controller:-
$scope.selected = {envelope: $scope.envelopes[0]};
$scope.getDefSpend = function(envelope){
return $scope.selected.envelope === envelope ? 1 : 0;
}
//$scope.selected.envelope will be your selected option at any point.
Demo
It's because what you have is actually 3 different model values. To work as you want it, you would have ONE model value for all 3. You can either restructure your data, or use ng-change to modify your model manually.

AngularJS checkbox filter

I would like to filter the results.
There is a list of wines, my wish is when no checkbox is checked, the entire list of wine is displayed.
when only 1 checkbox is checked is displayed the related category
when more than one checkbox are checked the related categories are displayed
I'm a newbie to AngularJS, I tried with ng-model wihout success, here is the code without ng-model associated to the function:
<html ng-app="exampleApp">
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.10/angular.min.js"></script>
<script>
angular.module("exampleApp", [])
.controller("defaultCtrl", function ($scope) {
$scope.wines = [
{ name: "Wine A", category: "red" },
{ name: "Wine B", category: "red" },
{ name: "wine C", category: "white" },
{ name: "Wine D", category: "red" },
{ name: "Wine E", category: "red" },
{ name: "wine F", category: "white" },
{ name: "wine G", category: "champagne"},
{ name: "wine H", category: "champagne" }
];
$scope.selectItems = function (item) {
return item.category == "red";
};
$scope.selectItems = function (item) {
return item.category == "white";
};
$scope.selectItems = function (item) {
return item.category == "champagne";
};
});
</script>
</head>
<body ng-controller="defaultCtrl">
<h4>red: <input type="checkbox"></h4>
<h4>white: <input type="checkbox"></h4>
<h4>champagne: <input type="checkbox"></h4>
<div ng-repeat="w in wines | filter:selectItems">
{{w.name}}
{{w.category}}
</div>
</body>
</html>
How to use ng-model or ng-change to associate a function to each checkbox button to have a real time filtering model??
There are several implementations possible. Here's one:
Have a $scope.filter = {} object to hold the state of each filter. E.g. {red: true, white: false...}.
Associate each checkbox with the corresponding property using ng-model. E.g.: input type="checkbox" ng-model="filter['red']" />.
Have a function (e.g. $scope.filterByCategory(wine)) that decides if a wine should be displayed or not (based on the $scope.filter object).
Use that function to filter the items based on their category. E.g. <div ng-repeat="wine in wines | filter:filterByCategory">
The filterByCategory function could be implemented like this:
function filterByCategory(wine) {
// Display the wine if
var displayWine =
// the wine's category checkbox is checked (`filter[category]` is true)
$scope.filter[wine.category] || // or
// no checkbox is checked (all `filter[...]` are false)
noFilter($scope.filter);
return displayWine;
};
where noFilter() is a function that checks if there is any filter activated (and returns true if there is none):
function noFilter(filterObj) {
return Object.
keys(filterObj).
every(function (key) { return !filterObj[key]; });
}
See, also, this short demo.
UPDATE:
I created a modified version, which supports multiple filters (not just filtering by category).
Basically, it dynamically detects the available properties (based on the first wine element), adds controls (groups of check-boxes) for applying filters based on each property and features a custom filter function that:
Filters each wine item, based on every property.
If a property has no filter applied (i.e. no check-box checked), it is ignored.
If a property has check-boxes checked, it is used for filtering out wine items (see above).
There is code for applying multiple filters using AND (i.e. all properties must match) or OR (at least one property must match).
See, also, this updated demo.
Just to add onto #gkalpak answer, I found this codepen which allows you to provide the total amount left after an option is selected for each category.
Change the ng-repeat from:
<div ng-repeat="wine in (ctrl.wines | filter:ctrl.filterByProperties) as filteredWines">
{{ wine.name }} <i>({{ wine.category }})</i>
</div>
To
<div ng-repeat="wine in filtered = (ctrl.wines | filter:ctrl.filterByProperties) as filteredWines">
{{ wine.name }} <i>({{ wine.category }})</i>
</div>
And with the input labels add:
<label>
<input type="checkbox" ng-model="ctrl.filter[prop][value]" />
{{ value }}({{(filtered | filter:value:true).length}})
</label>
I prefer use filter as $filter
app.filter('someFilter',checkboxFilter)
checkboxFilter() {
return function (arr,filter,key,noOne=false) {
// arr is an array of objects
// filter is checkbox filter. someting like {1:true,2:false}
// key is a property in ech object inside arr
// noOne is a behavior if none of checkbox is activated (default:false)
if (!arr.length) return null;
function noOneCheck(filter) {
return Object.keys(filter).every((key) => {
return !filter[key]
})
}
return arr.filter((i) => {
return filter[i[key]] || (noOne && noOneCheck(filter))
})
}
};
html:
ng-repeat="u in project.projectTeamInvite | checkbox:project.status:'status' track by $index">

Resources