ngChecked doesn't seem to want to bind to the model. Why? - angularjs

I'm using Ionic and thus angularjs, and I'm trying to store a setting in localStorage.
I've a checkbox that I want to use to set analytics on or off. The html looks like this.
Analytics on/off
<label class="toggle toggle-balanced">
<input type="checkbox" ng-click="toggleAnalytics()" ng-checked="analytics">
<div class="track">
<div class="handle"></div>
</div>
</label>
and in the related controller I have:
$scope.analytics = $localstorage.get('analyticsOnOff');
$log.log("Analytics initialised at", $scope.analytics);
$scope.toggleAnalytics = function(){
if($scope.analytics===true){
$scope.analytics = false;
$localstorage.set('analyticsOnOff', false);
}else{
$scope.analytics = true;
$localstorage.set('analyticsOnOff', true);
}
$log.log("Analytics is ", $scope.analytics);
}
So as you can see, when you change the checkbox the toggleAnalytics() function toggles $scope.analytics between true and false and updates the localstorage to reflect that.
Note that I am using $localstorage methods. $localstorage is defined in another service. It's allows you to set and get localStorage objects easily.
Anyway, my issue is a really simple, but baffling one. I know I can alter the analytics value from the console logs, but no matter what I do the checkbox initialises as on.
Any ideas?

You should be using ng-model on your checkbox, this will handle setting the actual property - and you can use a change event then:
<input type="checkbox" ng-change="toggleAnalytics(analytics)" ng-model="analytics" />
And the controller:
$scope.toggleAnalytics = function(isChecked) {
$localstorage.set('analyticsOnOff', isChecked);
$log.log("Analytics is ", isChecked);
}

Thank you very much for the feedback guys.
#nickgraef for altering me to the real problem - that .get() returns a string.
#tymeJV for supplying me with a much more elegant version of my own code.
This is what I did in the end, it's a mash up of both tips. I convert the analytics variable into a boolean straight after I .get() it from localstorage.
$scope.analytics = $localstorage.get('analyticsOnOff');
$scope.analytics = ($scope.analytics == 'true' ? true : false);
$log.log("Analytics initialised at", $scope.analytics);
$scope.toggleAnalytics = function(isChecked) {
$localstorage.set('analyticsOnOff', isChecked);
$log.log("Analytics is ", isChecked);
}

Related

using AND operator in Angular

I have the following HTML
<input type="checkbox" id="cbxSelectAllPreventive"/> <label>Select All</label>
<ul style="list-style: none;">
<li ng-repeat="test in lists.Tests">
</li>
</ul>
Test is an array of complex objects having isSelected property.
I want to use the checkbox as SelectAll functionality.
To do this , I need to supply ng-model to the checkbox, I can supply it as a method which checks in each of the tests and returns true/false. Is there any way to do this inline, without writing a method ?
I only see a way to do it using a function on ng-change
<input type="checkbox" id="cbxSelectAllPreventive"
ng-model="checked" ng-change="setAll(checked)"/>
...
$scope.setAll = function(isSelected){
$scope.lists.Tests.map(function(el){
el.isSelected = isSelected;
})
}
Working fiddle
EDIT:
For complete two-way connection between items' selection and check box the code will be a bit more complicated.
We will use an extra variable in $scope to reflect the label of check box. Don't forget to init the variable at controller creation
<label for="cbxSelectAllPreventive">{{selectLabel}}</label>
...
$scope.selectLabel = 'Select All';
setAll function should take care of setting this variable.
$scope.setAll = function(isSelected){
$scope.selectLabel = isSelected ? 'Deselect All' : 'Select All';
$scope.lists.Tests.map(function(el){
el.isSelected = isSelected;
})
}
And finally you definitely will have an option to select/deselect individual items. For this case you have to $watch your list. Mind the third parameter true which does deep comparison otherwise it won't "notice" changes inside objects.
$scope.$watch('lists.Tests', function(){
var text = $scope.lists.Tests.map(function(el){ return el.isSelected; }).join();
console.log(text);
var allSelected = $scope.lists.Tests.every(function(el){ return el.isSelected;});
var noneSelected = $scope.lists.Tests.every(function(el){ return !el.isSelected;});
if(noneSelected){
$scope.selectLabel = 'Select All';
$scope.checked = false;
}else if(allSelected){
$scope.selectLabel = 'Deselect All';
$scope.checked = true;
}
if(!noneSelected && !allSelected) {
$scope.selectLabel = 'Undetermined';
// here set the check box to undetermined (third) state
}
}, true);
Updated fiddle
Did you try ng-checkbox is should do exaclty what

using angularjs checkbox is not updating after ajax call

I have this checkbox
<input type="checkbox"
ng-change="defaultMsgsHandler(vuser.use_managed_default_msgs)"
ng-model="vuser.use_managed_default_msgs"
ng-true-value=1 ng-false-value=0>
When i make this ajax call, the checkbox does not reflect the ajax retured has vuser.use_managed_default_msgs set to 1, but the checkbox is not checked. I know the model is correct. vuser.use_managed_default_msgs is a 1 if i dump vuser to the console.
messageServices.getMessageSettingsInfo(user.dir)
.then(function(data) {
if (!data.success) {
$scope.errormessage = data.errors;
} else {
$scope.vuser = data.vuser;
$scope.messages = data.messages;
}
}, function(error) {
alert(error);
});
If i click on the checkbox defaultMsgsHandler changed the model correctly, and the checkbox checks and unchecks.
$scope.defaultMsgsHandler = function( use_managed_default_msgs ){
$scope.vuser.use_managed_default_msgs = use_managed_default_msgs;
};
Thanks for any help
Please, try to use: ng-checked="condition"
There is a similar question here,
How to set checkbox selected on the basis of model value
Not sure if it will help, but I think your
ng-true-value=1 ng-false-value=0
should be
ng-true-value="1" ng-false-value="0"

How can I make angularjs ngChange handler be called only when user finishes typing

I have an input field, where I want to apply the variant of ngChange.
The input field is sort of binding with an ajax call, when user changes the input, the server side will process the data, however, I don't wanna make the call too often.
Say the user wanna input a really string, I want the call be made only after user finishes the word he is about to type.
Nevertheless, I don't wanna use event such as blur. What would be a better way to implement this, rather than setTimeout?
Use ng-model-options in Angular > 1.3
<input type="text"
ng-model="vm.searchTerm"
ng-change="vm.search(vm.searchTerm)"
ng-model-options="{debounce: 750}" />
Without ng-model-options -- In markup:
<input ng-change="inputChanged()">
In your backing controller/scope
var inputChangedPromise;
$scope.inputChanged = function(){
if(inputChangedPromise){
$timeout.cancel(inputChangedPromise);
}
inputChangedPromise = $timeout(taskToDo,1000);
}
Then your taskToDo will only run after 1000ms of no changes.
As of Angular 1.3, you could use Angular ng-model-options directive
<input ng-change="inputChanged()" ng-model-options="{debounce:1000}">
Source: https://stackoverflow.com/a/26356084/1397994
Write your own directive- this will only run the commands on myText based on the conditions you set
<input my-change-directive type="text ng-model="myText" />
.directive('myChangeDirective',function() {
return {
require : 'ngModel',
link : function($scope,$element,$attrs) {
var stringTest = function(_string) {
//test string here, return true
//if you want to process it
}
$element.bind('change',function(e) {
if(stringTest($attrs.ngModel) === true) {
//make ajax call here
//run $scope.$apply() in ajax callback if scope is changed
}
});
}
}
})

Check all boxes toggle method not working like expected

I'm wiriting a method to toggle a checklist as checked / unchecked, as follow:
$scope.checkAllToggle = function(dtProvider, destiny, ev) {
$(ev.currentTarget).parent().find('span').html());
data[destiny] = [];
if ($(ev.currentTarget).parent().find('span').html()=='Check all') {
for (var val in data[dtProvider]) data[destiny].push(data[dtProvider][val].id);
$(ev.currentTarget).parent().find('span').html('Uncheck all');
}
else $(ev.currentTarget).parent().find('span').html('Check all');
}
The label of the toggle button changes every time, but the state of the checkboxs only becomes checked after 2 click, from this point change in each 3 clicks.
What's wrong?
Append
if(!$scope.$$phase) $scope.$apply();
to the method make no difference, as prepending $scope to data
Code in plunker: http://plnkr.co/edit/Zf6UzLbC4osRov7IR5Na?p=preview
Rather than do it that way, this would be the easier way to do it.
Make the master input and assign it to a model.
<input type="checkbox" ng-model="master">
Then have your other inputs be tied to the master like this.
<input type="checkbox" ng-model="box1" ng-checked="master">
<input type="checkbox" ng-model="box2" ng-checked="master">
<input type="checkbox" ng-model="box3" ng-checked="master">
Here is your plunker with these changes.
Master Checkbox Plunker
I got your plunker to work by doing this.
$scope.checkToggle = function(dtProvider, destiny, ev) {
$scope.data[destiny] = [];
if ($(ev.currentTarget).parent().find('span').html() == 'Check all') {
for (var val in $scope.data[dtProvider]){
$scope.data[destiny].push($scope.data[dtProvider][val].id);
$(ev.currentTarget).parent().find('span').html('Uncheck all');
}
} else {
$(ev.currentTarget).parent().find('span').html('Check all');
}
$scope.$apply();
};
I could not get it to work if calling safely like if(!$scope.$$phase){$scope.$apply();} but it is working with straight $scope.$apply(). This might have something to do with rootScope and need to be safetly called in a different manner.
See this link: https://coderwall.com/p/ngisma
You also had some missing {} in your code that could have been causing some issues.

Dynamically change source of ng-model

I have shipping and billing fields. If a customer checks a box for "use shipping for billing", I want the billing fields to match the shipping fields. I could copy over the values, but I would rather re-bind the billing fields to match the shipping ones so if the box is checked and a change is made to shipping, the billing will also update. So how would I re-bind the source of ng-model or what idiom could I use to bind the billing address fields to achieve this?
Maintain two objects
$scope.shipping = { .... };
$scope.billing = { .... };
If someone selects that they want to match, just do
$scope.billing = $scope.shipping.
Since objects are by reference, as they update one the other will also update. You can remove the reference by changing $scope.billing back to whatever you initialized it with:
$scope.billing = { .... };
If you have a checkbox that you want to bind this too, wire up a data-ng-change on it
<input type="checkbox" data-ng-model="MY_MODEL" data-ng-change="myFunction() "/>
Then in your controller have
$scope.MY_MODEL = false;
$scope.myFunction(){
console.log($scope.MY_MODEL);
}
Or don't bind data-ng-change and just $watch the MY_MODEL:
$scope.MY_MODEL = false;
$scope.$watch("MY_MODEL", function(){
console.log($scope.MY_MODEL);
}, true);
You can use ng-checked instead of using scope watch to make it light weighted. Don't reinvent the wheel.
<input type="checkbox" ng-model="flip" ng-checked="checked();">
$scope.checked = function(){
if($scope.flip){
$scope.data = ... //update the data source
}else{
$scope.data = ... //update the data source
}
}

Resources