Angular: having a function as ng-false-value - javascript

In my Angular app, I have the following list of checkboxes, generated within a nested ng-repeat:
<div ng-repeat="partner in type.partners">
<label class="checkbox-inline">
<input type="checkbox"
ng-model="partners[$parent.$index][$index]"
ng-true-value="{{partner}}"
ng-change="handleCheckboxChanged({{type.id}})"
ng-checked="getChecked({{partner.id}})"
>
<p><span ></span>{{partner.name}}<p>
</label>
</div>
This will end up looking like the picture below:
When a user selects a partner with a chekcbox, this partner gets added to the nested list of selected partners, making use of ng-true-value and ng-model.
However, when a user deselects a checkbox, my object will still keep that key:value pair, except the value will now simply be false, as seen here in the console:
My intention would be that this key:value pair would disappear when a user unticks a checkbox, instead of it just becoming false.
Is it possible to do so by triggering a function as the ng-false-value?

What you described - this is a default behaviour. You can adjust your ngChange function.
Just a simple example, how you can make desired.
<div ng-repeat="partner in partners">
<label>
<input type="checkbox"
value="partner.id"
ng-model="partner.selected"
ng-change="changeValue(partner)"
/>
{{partner.name}}
</label>
</div>
var partnersList = [], idsArray = [];
$scope.changeValue = function(partner){
if(partner.selected)
addPartner(partner);
else
removePartner(partner);
};
var addPartner= function(partner){
if(!existInList(partner))
partnersList.push(partner);
};
var removePartner= function(partner){
idsArray = getIdsArray();
var indexToRemove = idsArray.indexOf(partner.id);
if(indexToRemove == -1)
return;
partnersList.splice(indexToRemove, 1);
};
var existInList = function(partner){
idsArray = getIdsArray();
return idsArray.indexOf(partner.id) != -1;
};
var getIdsArray = function(){
return partnersList.map(function(partner){ return partner.id });
};
Link to JSFiddle example.

Related

Checkbox is not working properly with ng-repeat, AngularJs

I want to make multiselect checkbox using ng-repeat. It is working fine if I don't have pre-selected checkbox. But when I have preselected checkbox then its behaviour is totally unusual.
<div ng-repeat="account in accounts">
<input ng-model="selectedAccounts[account.id]"
ng-checked="{{account.lastchecked}}" type="checkbox">
</div>
In Controller I got selected id as:
$scope.selectedAccounts = [];
angular.forEach($scope.selectedAccounts, function(value, key) {
if(value == true) {
selectedIds.push(key);
}
});
The problem here is that I have to initialise selectedAccounts with initial array. If I don't do this then it gives me undefined. When I have 'lastchecked' true for some accounts then it shows pre-checked values according to lastchecked but when I try to retreive $scope.selectedAccounts it give me empty array. But when I manually check/uncheck each option then $scope.selectedAccounts give me correct result.
Here is how i would do it :
$scope.context = {accounts : :[]};// init is important to place the variable in that scope on not in a child
<div ng-repeat="account in context.accounts">
<input ng-model="account.lastchecked" ng-checked="account.lastchecked"
ng-true-value="true" ng-false-value="false" type="checkbox">
</div>
Ng-true-value and ng-false-value ensure that the value will be the boolean true/false instead of strings.
After to get the selectedAccount you just search.filter account with lastchecked==true
The indermediary object context is to avoid scope issues, scope inheritance fails on simple fields. This is a limit of javascript. In angularJS this is called DOT notation.
Go through this
var jimApp = angular.module("mainApp", []);
jimApp.controller('mainCtrl', function($scope){
$scope.selectedAccounts = {"3":true};
$scope.accounts = [{id:1, name:"account1", checked: true}, {id:2, name:"account2"}, {id:3, name:"account3"}]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mainApp" ng-controller="mainCtrl">
<div ng-repeat="account in accounts">
<input ng-model="selectedAccounts[account.id]"
ng-checked="selectedAccounts[account.id]" type="checkbox">{{account.name}}
</div>
<div>{{selectedAccounts}}</div>
</div>

Populate/clear array when checkbox checked/unchecked

I'm currently using angular-google-maps to build out a map with several different marker types. Below my map I have a simple set of checkboxes like so:
<div class="mapOptions">
<form action="" class="form-inline" style="text-align:center">
<label for="">General:</label>
<input type="checkbox" checked value="Games">
<label for="" style="color:#000033">Games</label>
<input type="checkbox" checked value="Practices">
<label for="" style="color:#ffd900">Practices</label>
</form>
</div>
Within my controller I initialize empty arrays then populate them with 'markers' through calls to my API endpoints:
vm.markersGames = [];
vm.markersPractices = [];
What I want to do is clear each respective array when its checkbox is unchecked (ex: User unchecks 'Games', method within my controller sets vm.markersGames = []), and re-populate each array when checkbox clicked (ex: User checks 'Practices', method within my controller calls API endpoint and populates vm.markersPractices).
The issue I've run into is not knowing how to properly add 'check/uncheck handlers' to my inputs.
Attempt to reload markers when checked:
vm.checkToggle = function(isChecked, value) {
if (!isChecked) {
vm[value] = [];
} else {
getPlayerAddress();
$scope.$apply();
}
};
getPlayerAddress() calls API to populate markers array.
You can use the ngChange directive in order to listen change on your input.
Here is an example :
Controller
(function(){
function Controller($scope, Service) {
//Object to know if the input is checked or not
$scope.form = {};
$scope.markersGames = [];
$scope.markersPractices = [];
//isChecked : input checked or not
//value : keyname of your array, for example markersGames
$scope.change = function(isChecked, value){
!isChecked
//If input is not checked, set our $scope[value] to empty array
? $scope[value] = []
//If the input is checked, call Service
: Service.get(value).then(function(data){
//Retrieve and set data
$scope[value] = data;
});
}
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Then you can use a Service with promise to make your requests
Service
(function(){
function Service($q){
function get(url){
var defer = $q.defer();
var promise = defer.promise;
if (url === 'markersGames') {
defer.resolve([1,2,3,4]);
} else {
defer.resolve([4,6,8,1,5,2]);
}
return promise;
}
var factory = {
get: get
};
return factory;
}
angular
.module('app')
.factory('Service', Service);
})();
Then you can add ngChange directive in your html :
HTML
<body ng-app="app" ng-controller="ctrl">
<form action="" class="form-inline" style="text-align:center">
<label for="">General:</label>
<input type="checkbox" checked value="Games" ng-model="form.game" ng-change="change(form.game, 'markersGames')">
<label for="" style="color:#000033">Games</label>
<input type="checkbox" checked value="Practices" ng-model="form.practice" ng-change="change(form.practice, 'markersPractices')">
<label for="" style="color:#ffd900">Practices</label>
</form>
{{markersGames}}
{{markersPractices}}
</body>

How to filter through a table using ng-repeat checkboxes with Angularjs

Once upon a time this was working but somehow it's broken. I want to be able to produce checkboxes using ng-repeat to get as many checkboxes as required based on stored data and use these to filter through a table produced.
Additionally I don't want identical values for the checkboxes to be repeated.
I have made a plnkr with the code.
<div class="row">
<label data-ng-repeat="x in projects">
<input
type="checkbox"
data-ng-true-value="{{x.b}}"
data-ng-false-value=''
ng-model="quer[queryBy]" />
{{x.b}}
</label>
</div>
http://plnkr.co/edit/RBjSNweUskAtLUH3Ss6r?p=preview
So in summary.
Checkboxes to filter Ref.
Checkboxes to be unique.
Checkboxes to be made based off ng-repeat using Ref.
Okay, here's how to do it.
First, let's add a couple of lines of CSS in your to make sure all the checkboxes are visible:
<style>
.row { margin-left: 0px }
input[type=checkbox] { margin-left: 30px; }
</style>
Next, add the following lines to your controller:
app.filter('unique', function() {
return function (arr, field) {
var o = {}, i, l = arr.length, r = [];
for(i=0; i<l;i+=1) {
o[arr[i][field]] = arr[i];
}
for(i in o) {
r.push(o[i]);
}
return r;
};
})
app.controller("maincontroller",function($scope){
$scope.query = {};
$scope.quer = {};
$scope.queryBy = '$';
$scope.isCollapsed = true;
$scope.selectedRefs = [];
$scope.myFilter = function (item) {
var idx = $scope.selectedRefs.indexOf(item.b);
return idx != -1;
};
$scope.toggleSelection = function toggleSelection(id) {
var idx = $scope.selectedRefs.indexOf(id);
if (idx > -1) {
$scope.selectedRefs.splice(idx, 1);
}
else {
$scope.selectedRefs.push(id);
}
};
Phew.
For some reason, your Plunkr's version of AngularJS didn't recognise the unique attribute, so I added one to your controller.
Finally, change your html to this:
<div class="row">
<label data-ng-repeat="x in projects | unique:'b' | orderBy:'b'" >
<input
id="x.b"
type="checkbox"
ng-click="toggleSelection(x.b)"
ng-init="selectedRefs.push(x.b)"
ng-checked="selectedRefs.indexOf(x.b) > -1" />
{{x.b}}
</label>
</div>
... and your ng-repeat to this...
<tr ng-click="isCollapsed = !isCollapsed" ng-repeat-start="x in projects | filter:myFilter | orderBy:orderProp">
If you're interested in knowing how this works, add these lines:
<div style="margin:10px 10px 30px 10px">
<pre>{{ selectedRefs }} </pre>
</div>
I love this trick: you can see the exact contents of our "selectedRefs" array, and see it change as we tick/untick our checkboxes. This really helps when developing/testing our bindings!
As you can see, these changes use the new unique function to get your list of distinct values from your project array, and when the page first loads, we push all of the values into our new "selectedRefs" array.
["123","321","456","654","789","987"]
Then, as you tick/untick the checkboxes, we add/remove that item from this list.
Finally, we use that filter in the ng-repeat.
ng-repeat-start="x in projects | filter:myFilter | orderBy:orderProp"
Job done !
Update
If you wanted to start off with all checkboxes unticked, then it's a simple change. Just remove this line...
ng-init="selectedRefs.push(x.b)"
..and change the myFilter function to show all items initially..
$scope.myFilter = function (item) {
if ($scope.selectedRefs.length == 0)
return true;
var idx = $scope.selectedRefs.indexOf(item.b);
return idx != -1;
};
And to add a "Clear all" button, simply add a button to your form which calls a function in your AngularJS controller like this..
$scope.clearAll = function () {
$scope.selectedRefs = [];
};
(I haven't tested these suggestions though.)
ng-false-value directive needs a value set. Try ng-false-value='false' or ng-false-value='null' (in fact you can skip this one entirely if it has to just be a falsy value and not something concrete, like a string or certain number).
As you've pointed out in the comments, after selecting and then clearing the checkboxes, all rows are filtered out. It happens because unchecking the checkbox will set its value to false, and this does not agree with your entities' values (as you probably know, just stating it for others).
Therefore you do need to set this value to empty string in the end. That'd be the way:
$scope.$watch('quer.$', function () {
if ($scope.quer.$ === false) {
$scope.quer.$ = '';
}
});

angularJs - Is it possible for 2 different models of different structures to sync or share states?

I have a list of checkboxes and values I"m loading from a list which comes back from the database.
Controller
listA = ['item1','item2'...'itemn']; //Master list of items
$scope.selectedItems = ["item1",... "item5"]; //selected items
$scope.attributesModel = [ //new model based on selected items
{"index":5,"attribute":"item1"},
{"index":10, "attribute":"item2"},
{"index":13, "attribute":"item3"},
{"index":21, "attribute":"item4"},
{"index":24, "attribute":"item5"}
];
View part 1
<td>
<div class="checkbox checkbox-notext">
<input checklist-model="selectedItems" checklist-value="key" type="checkbox" id="{{key}}" ng-disabled="exceededLimit && !checked" />
</div>
</td>
<td>
<label for="{{key}}">{{key}}{{$index}}</label>
</td>
view part 2
<div ng-repeat="(index, row) in attributesModel" >
<div class="margin10">
<div>Index<input ng-model="row.index" value="row.index" type="number" class="indexInputs"></input>{{row.attribute}}</div>
</div>
</div>
Now I would like to sync $scope.selectedItems and $scope.attributesModel. When a checkbox is deselected, both selectedItems and attributesModel models remove that item, and vice versa. So every time someone checks a new checkbox they are presented a attributesModel with an empty text field to type the index value.
catch The index key is null initially for every newly selected item that is added to attributesModel. The user must enter a new index # once the new item is created.
I've tried using watch but the problem I run into is when a new item is selected, I don't have access to the item itself. I only have access to the list without any idea whether the new item is X or if the item removed is Y in order to push/delete the right item.
So this might be a watch solution that I'm missing.
Let me know if I can clarify anything.
I am not sure what the problem is, but you could use ngChange on the checkboxes:
<input type="checkbox" ... ng-change="..." />
I asdume you have a checklist directive or something, so should do something there, but (since you don't share it with us) I can't tell what exactly :)
UPDATE:
Since the checklist directive is an external dependency, you could handle the ng-chage in your code:
<input type="checkbox" ... ng-change="changed(key)" />
/* In the controller: */
...
$scope.changed = function (key) {
if ($scope.selectedItems.indexOf(key) === -1) {
// The checkbox for `key` was unchecked...
} else {
// The checkbox for `key` was checked...
}
};

How to get selected checkboxes on button click in angularjs

I want to do something like this
<input type="checkbox" ng-model="first" ng-click="chkSelect()"/><label>First</label>
<input type="checkbox" ng-model="second" ng-click="chkSelect()"/><label>Second</label>
<input type="checkbox" ng-model="third" ng-click="chkSelect()"/><label>Third</label>
<input type="checkbox" ng-model="forth" ng-click="chkSelect()"/><label>Forth</label>
<button>Selected</button>
On button click I want to display selected checkbox labelname.
$scope.chkSelect = function (value) {
console.log(value);
};
Because the checkboxes are mapped, you can reference $scope.first, $scope.second, etc in your chkSelect() function. It's also possible to have a set of checkboxes mapped as a single array of data instead of having to give each checkbox a name. This is handy if you are generating the checkboxes, perhaps from a set of data.
I agree with Bublebee Mans solution. You've left out a lot of detail on why you're trying to get the label. In any case if you REALLY want to get it you can do this:
$scope.chkSelect = function (value) {
for(var key in $scope){
var inputs = document.querySelectorAll("input[ng-model='" + key + "']");
if(inputs.length){
var selectedInput = inputs[0];
var label = selectedInput.nextSibling;
console.log(label.innerHTML);
}
};
};
You can mess around with it to see if it's indeed selected.
fiddle: http://jsfiddle.net/pzz6s/
Side note, for anybody who knows angular please forgive me.
If you are dealing with server data, you might need isolated html block and deal with data in controller only.
You can do it by creating array in controller, maybe your data from response, and use ngRepeat directive to deal independently in html code.
Here is what I am telling you:
HTML:
<form ng-controller="MyCtrl">
<label ng-repeat="name in names" for="{{name}}">
{{name}}
<input type="checkbox"
ng-model="my[name]"
id="{{name}}"
name="favorite" />
</label>
<div>You chose <label ng-repeat="(key, value) in my">
<span ng-show="value == true">{{key}}<span>
</label>
</div>
</form>
Javascript
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.names = ['pizza', 'unicorns', 'robots'];
$scope.my = { };
}
You want to have something like the following in your controller (untested, working from memory):
$scope.checkBoxModels = [ { name: 'first', checked: false }, { name: 'second', checked: false }, { name: 'third', checked: false }, { name: 'fourth', checked: false } ];
Then in your view:
<input ng-repeat"checkboxModel in CheckBoxModels" ng-model="checkBoxModel.checked" ng-click="chkSelect(checkBoxModel)" /><label>{{checkBoxModel.name}}</label>
Then update your function:
$scope.chkSelect = function (checkBoxModel) {
console.log(checkBoxModel.name);
};

Categories

Resources