How do I reverse a checkbox state to make it unchecked (AngularJS)? - angularjs

I basically want that when the user clicks on a checkbox, their decision is immediately reversed and the checkbox is unchecked. The code below is not working. I tried other variations like value = !value instead of value = false and tried initializing another controller variable to be equal to the checkbox variable and changing the other controller variable. None of this works; basically, I cannot set the checkbox state unless the application is first being initialized.
HTML code:
<input type="checkbox" ng-model="avariabledefinedincontroller" ng-click="changemystate(avariabledefinedincontroller)">
Controller code:
$scope.avariabledefinedincontroller = false;
$scope.changemystate = function(value){
if (value == true) {
value = false;
}
else {
value = value;
}
};

The value passed into changemystate will be the value the checkbox has when it is clicked. So if you want it to stay at that value, you can set up a timeout to restore it to that value. No negation is needed.
Also, assigning a value to value will do nothing. You have to modify the scope variable. If you want to identify the scope variable for the item that was clicked, you can pass in a string:
function MyController($scope, $timeout) {
$scope.mybool = false;
$scope.changemystate = function(key) {
var value = $scope[key];
$timeout(function () {
$scope[key] = value;
}, 0);
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<div ng-app="" ng-controller="MyController">
<input type="checkbox" ng-model="mybool" ng-click="changemystate('mybool')" />
{{mybool}}
</div>

Actually, you don't need a function for that :
In the template :
<input type="checkbox" ng-model="avariabledefinedincontroller" />
And in the controller, you'll have just the variable :
// Default state
$scope.avariabledefinedincontroller = false;
Fiddle
Edit : The purpose of this question was actually to prevent the user to check a checkbox, but without disabling the checkbox. For that, #JLRishe's answer addresses well the problem, though I would personally add the state value to set as an argument to setState :
angular.module('demoApp', []).controller('DemoController', function($scope, $timeout) {
$scope.myVar = false;
$scope.setState = function (varName, val) {
$timeout(function() {
$scope[varName] = val;
}, 0);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.js"></script>
<div ng-app="demoApp" ng-controller="DemoController">
<input type="checkbox" ng-model="myVar" ng-click="setState('myVar', false)" />
{{myVar}}
</div>

VYou can also use a short-hand:
<input type="checkbox" ng-model="avariabledefinedincontroller" ng-click="aVariable = !aVariable"/>

There are a number of options available to you, but just in case this can help someone, I did two things.
I passed the $event in so that I could run event.preventDefault() (so it wont get checked)
ng-click="changemystate($event, avariabledefinedincontroller)">
In $scope.changemystate() I then added the event param, and called
event.preventDefault()
Secondly, I then just added a return so that the method terminates, so there is no need to roll back the value, because it prevents it from being updated in the first place.

Related

Passing a parameter in ng-click function

I am trying to pass data to an API for the user e-mail subscription status as "Y"/"N".
In the controller code console.log(a.checked) is undefined. But in regular javascript a onclick event for the input element type="checkbox" has the this.checked to respond correspondingly as true or false. Why is it not happening here in AngularJS?
My html code with angular directive ng-click:
<label for="mail_sub">E-mail subscription</label>
<input id="mail_sub" type="checkbox" name=checkbox ng-click="mailsubscription(this)">
Code of the controller:
.controller("registerCtrl", function($scope, $state, userProcess) {
$scope.mailsubscription = function(a) {
console.log(a);
console.log(a.checked); // console output is: "undefined"
signupinfo = {};
if (a.checked) {
signupinfo.u_mail_subscription = 'Y';
} else {
signupinfo.u_mail_subscription = 'N';
}
console.log(signupinfo);
};
/*$scope.registerProcess = function(signupinfo){
console.log(signupinfo);
userProcess.signup(signupinfo).sucess(function(response){
if(response.status){
}
})
};*/
});
There is no checked defined in your scope. You have do to something like this:
$scope.checked = false
$scope.mailsubscription = function () {
$scope.checked = !$scope.checked;
console.log($scope.checked)
};
Or you can use ngModel directive in your template
<input id="mail_sub" type="checkbox" name=checkbox ng-click="mailsubscription(this)" ngModel="checked">
If you go this way you dont need to toggle the variable checked by your self.

Reset form as pristine if user changed input values to their initial value

Angular has $dirty and $pristine properties on FormController that we can use to detect whether user has interacted with the form or not. Both of these properties are more or less the opposite face of the same coin.
I would like to implement the simplest way to actually detect when form is still in its pristine state despite user interacting with it. The form may have any inputs. If user for instance first changes something but then changes it back to initial value, I would like my form to be $pristine again.
This may not be so apparent with text inputs, but I'm having a list of checkboxes where user can toggle some of those but then changes their mind... I would only like the user to be able to save actual changes. The problem is that whenever user interacts with the list the form becomes dirty, regardless whether user re-toggled the same checkbox making the whole form back to what it initially was.
One possible way would be I could have default values saved with each checkbox and add ngChange to each of them which would check all of them each time and call $setPristine if all of them have initial values.
But I guess there're better, simpler more clever ways of doing the same. Maybe (ab)using validators or even something more ingenious?
Question
What would be the simplest way to detect forms being pristine after being interacted with?
It can be done by using a directive within the ngModel built-in directive and watch the model value and make changes to pristine when needed. It's less expensive than watching the entire form but still seems an overkill and I'm not sure about the performance in a large form.
Note: The following snippet is not the newest version of this solution, check on UPDATE 1 for a newest and optimized solution.
angular.module('app', [])
.directive('ngModel', function() {
return {
restrict: 'A',
require: ['ngModel', '^?form'],
priority: 1000, // just to make sure it will run after the built-in
link: function(scope, elem, attr, ctrls) {
var
rawValue,
ngModelCtrl = ctrls[0],
ngFormCtrl = ctrls[1],
isFormValue = function(value) {
return typeof value === 'object' && value.hasOwnProperty('$modelValue');
};
scope.$watch(attr.ngModel, function(value) {
// store the raw model value
// on initial state
if (rawValue === undefined) {
rawValue = value;
return;
}
if (value == rawValue) {
// set model pristine
ngModelCtrl.$setPristine();
// don't need to check if form is not defined
if (!ngFormCtrl) return;
// check for other named models in case are all pristine
// sets the form to pristine as well
for (key in ngFormCtrl) {
var value = ngFormCtrl[key];
if (isFormValue(value) && !value.$pristine) return;
}
// if haven't returned yet, means that all model are pristine
// so then, sets the form to pristine as well
ngFormCtrl.$setPristine();
}
});
}
};
})
.controller('myController', function($rootScope, $timeout) {
var $ctrl = this;
$ctrl.model = {
name: 'lenny',
age: 23
};
$timeout(function() {
console.log('watchers: ' + $rootScope.$$watchersCount)
}, 1000);
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['app']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.js"></script>
<div ng-controller="myController as $ctrl">
<form name="$ctrl.myForm" novalidate>
<label>Name :
<input name="test1" ng-model="$ctrl.model.name">
</label>
<label>age :
<input name="test2" ng-model="$ctrl.model.age">
</label>
<label>Pristine: {{ $ctrl.myForm.$pristine }}</label>
<div><pre>
</pre>
</div>
</form>
</div>
UPDATE 1
Changed the watching system to watch once and get rid of the extra watchers.Now the changes comes from the change listeners of ngModelController and the watcher is unbinded on the first model set . As can be noticed by a console log, the numbers of watchers on root was always doubling the number of watchers, by doing this the number of watchers remains the same.
angular.module('app', [])
.directive('ngModel', function() {
return {
restrict: 'A',
require: ['ngModel', '^?form'],
priority: 1000,
link: function(scope, elem, attr, ctrls) {
var
rawValue,
ngModelCtrl = ctrls[0],
ngFormCtrl = ctrls[1],
isFormValue = function(value) {
return typeof value === 'object' && value.hasOwnProperty('$modelValue');
};
var unbindWatcher = scope.$watch(attr.ngModel, function(value) {
// set raw value
rawValue = value;
// add a change listenner
ngModelCtrl.$viewChangeListeners.push(function() {
if (rawValue === undefined) {
//rawValue = ngModelCtrl.$lastCommit;
}
if (ngModelCtrl.$modelValue == rawValue) {
// set model pristine
ngModelCtrl.$setPristine();
// check for other named models in case are all pristine
// sets the form to pristine as well
for (key in ngFormCtrl) {
var value = ngFormCtrl[key];
if (isFormValue(value) && !value.$pristine) return;
}
ngFormCtrl.$setPristine();
}
});
// unbind the watcher at the first change
unbindWatcher();
});
}
};
})
.controller('myController', function($rootScope, $timeout) {
var $ctrl = this;
$ctrl.model = {
name: 'lenny',
age: 23
};
$timeout(function() {
console.log('watchers: ' + $rootScope.$$watchersCount)
}, 1000);
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['app']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.js"></script>
<div ng-controller="myController as $ctrl">
<form name="$ctrl.myForm" novalidate>
<label>Name :
<input name="test1" ng-model="$ctrl.model.name">
</label>
<label>age :
<input name="test2" ng-model="$ctrl.model.age">
</label>
<label>Pristine: {{ $ctrl.myForm.$pristine }}</label>
<div><pre>
</pre>
</div>
</form>
</div>
When your controller initialises:
constructor() {
super(arguments);
this._copy = angular.copy(this._formModel);
}
Then you can place a watch on the model.
this._$scope.$watch('this._formModel', (new, old) => {
if (_.eq(this._copy, this._formModel)) {
formObject.$setPristine();
}
});
If the copy is the same as the model, it's still pristine.
Edit: 2nd option is to add ngChange to each input to call a method on your controller, and then do the same procedure as above. This still relies on your copying the original (blank) model in the constructor.
<input ng-change="vm.noticeInputChange(t)" id="some_element" class="some_class" />
Then in the controller:
noticeInputChange() {
if (_.eq(this._copy, this._formModel)) {
formObject.$setPristine();
}
}
That should do the same, but as has been pointed out, the $watch might become quite expensive depending on the size of your form. Also, as someone here pointed out, the _.eq() is a lodash method

How can I monitor the change of a variable in my factory

I have a very simple preloading screen script that i need to get from several controllers. However I can't get it to update automatically the variable and I don't know what I'm doing wrong.
Factory:
myApp.factory("preloader", function(){
var preload = {};
preload.loaded = true;
preload.turnOn = function () {
preload.loaded = true;
console.log('on');
}
preload.turnOff = function () {
preload.loaded = false;
console.log('off');
}
preload.getState = function() {
return preload.loaded;
}
return preload;
});
Controller
mazda.controller('preloadingHome', ['$scope', "preloader", function($scope, preload) {
$scope.users = false;
$scope.showPreload = preload.getState();
console.log(preload.loaded);
$scope.turnOn = function(){
preload.turnOn();
}
$scope.turnOff = function(){
preload.turnOff();
}
//
// $scope.state = preload.state;
// preload.turnOff();
}]);
View
<body data-ng-controller="preloadingHome">
<div>aaa: {{ showPreload }}</div>
<div>b: {{users}} </div>
<input type="checkbox" ng-model="users" />
<input type="checkbox" ng-change="turnOff()" ng-model="pene" />
<input type="checkbox" ng-change="turnOn()" ng-model="pene2" />
<!-- script src="js/scripts.min.js"></script -->
<script src="js/scripts.min.js"></script>
<script src="js/bundle.js"></script>
</body>
My problem is: The {{ showPreload }} variable load on the view always stays true no matter how I change it.
I think it is important to point out that the reason this does not work is because your getState() method returns by value, not by reference.
When your controller instantiates, it sets the showPreloaded variable to the value of the preloaded.loaded object member, which is true. Every time you change the state after that, you are updating the object member in your factory properly, but the $scope.showPreloaded value is equal to true, as it is not referencing the value in the factory.
Here is how to change your code:
In the view
<div>aaa: {{ showPreload.loaded }}</div>
In the factory:
preload.getState = function() {
return preload;
}
The factory will now be returning the reference to the object preload. Javascript always returns references when returning an object, and returns the value when returning a primitive
Your factory's method is not a two-way binding, instead, expose a variable from your factory/service, and bind to that, for example
factory.state = { isTurnedOn : true };
you can either directly bind to that factory variable, or have your controller own variable linked to the factory variable
Return the whole object rather than returning a primitive. There is no inheritance of primitives
In factory
preload.getState = function() {
return preload;
}
In controller
$scope.preloadState = preload.getState();
In view
<div>aaa: {{ preloadState.preload ?'Yes':'No'}}</div>
Now angular view watchers will detect property changes of the object and update view
You should try to write this: scope.preload = preloader;
And then you can manipulate your object scope.preload.

Fire ng-change event for textbox other than keypress or keydown

I want to update a text value when the value is changed by using some method, the method should not call when I am still typing text but when exit from the textbox or change it through script code:
<input type="text" ng-model ="model.value" ng-change = "update()"/>
what should contain update() method,
what I tried is : wrote a directive for this textbox, and
scope.$watch('model', function(nval,oval){
if ((oVal === undefined) && (nVal != undefined)){
update()
}
});
It is calling for the first time, but not whenever the model value changed.
I want to call update method whenever the model.value changed, but not while entering text
You can use ng-blur and you should change your $watch:
JSFiddle
HTML:
<div ng-app="myApp" ng-controller="dummy">
<input type="text" ng-model="model.value" ng-blur="update()"/>
</div>
JS:
angular.module('myApp', [])
.controller('dummy', ['$scope', function ($scope) {
$scope.model = {
value: 'hello'
};
$scope.update = function () {
console.log("changed!");
};
$scope.$watch('model.value', function (nval, oval) {
if (oval !== nval) {
$scope.update();
}
});
}]);
You can use data-ng-model-options directly to achieve your requirement.
Like,
here you can get full reference of data-ng-nmodel-options
https://docs.angularjs.org/api/ng/directive/ngModelOptions
this directive will work for the versions angular 1.4.x ( from 1.4.x version it suppports)

CheckBox not getting checked in Knockoutjs

I am using a checkbox with a function inside the data-bind, but I am unable to check the checkbox.
view:
<input type="checkbox" data-bind="click: function(){ f('hello parameter'); }">Click me
View Model:
var VM = function () {
this.f = function (param) {
alert(param); // here i am getting 'hello parameter'
return true;
}
}
ko.applyBindings(new VM());
Here is my Fiddle
By default, the click binding prevents the default reaction to a click based on the assumption that your JavaScript click event handler will handle everything. You need to return "true" to get the default behavior anyway, which you are doing from your f() function but not the wrapper inside data-bind:
<input type="checkbox" data-bind="click: function() { f('hello parameter'); }">
should be
<input type="checkbox" data-bind="click: function() { return f('hello parameter'); }">
Without context around the code it's not clear how you intend to use this control. With a checkbox you would normally use the checked binding that is bound to a boolean observable:
The checked binding links a checkable form control — i.e., a checkbox () or a radio button () — with a property on your view model.
So another way of writing this using the checked binding would be:
Sample Code:
var VM = function () {
var self = this;
self.myCheck = ko.observable(false);
self.myCheck.subscribe(function () {
alert('checked value = ' + self.myCheck());
});
}
ko.applyBindings(new VM());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div>
<input type="checkbox" data-bind="checked: myCheck" />
Click me
</div>
With this example, there's an observable that tracks the value of the checkbox: self.myCheck. So when the checkbox is checked/unchecked, self.myCheck() will be set to true/false.
In order to provide some output or run some code when the value is changed, I've subscribed to the observable, which basically means that every time the value of the observable is changed, the alert will be fired (or whatever code you place in there).
Demo On JS Fiddle

Resources