Clicking a checkbox with ng-click does not update the model - angularjs

Clicking on a checkbox and calling ng-click: the model is not updated before ng-click kicks in so the checkbox value is wrongly presented in the UI:
This works in AngularJS 1.0.7 and seems broken in Angualar 1.2-RCx.
<div ng-app="myApp" ng-controller="Ctrl">
<li ng-repeat="todo in todos">
<input type='checkbox' ng-click='onCompleteTodo(todo)' ng-model="todo.done">
{{todo.text}}
</li>
<hr>
task: {{todoText}}
<hr><h2>Wrong value</h2>
done: {{doneAfterClick}}
and controller:
angular.module('myApp', [])
.controller('Ctrl', ['$scope', function($scope) {
$scope.todos=[
{'text': "get milk",
'done': true
},
{'text': "get milk2",
'done': false
}
];
$scope.onCompleteTodo = function(todo) {
console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
$scope.doneAfterClick=todo.done;
$scope.todoText = todo.text;
};
}]);
Broken Fiddle w/ Angular 1.2 RCx
- http://jsfiddle.net/supercobra/ekD3r/
Working fidddle w/ Angular 1.0.0
- http://jsfiddle.net/supercobra/8FQNw/

How about changing
<input type='checkbox' ng-click='onCompleteTodo(todo)' ng-model="todo.done">
to
<input type='checkbox' ng-change='onCompleteTodo(todo)' ng-model="todo.done">
From docs:
Evaluate given expression when user changes the input. The expression is not evaluated when the value change is coming from the model.
Note, this directive requires ngModel to be present.

As reported in https://github.com/angular/angular.js/issues/4765, switching from ng-click to ng-change seems to fix this (I am using Angular 1.2.14)

The order in which ng-click and ng-model will be executed is ambiguous (since neither explicitly set their priority). The most stable solution to this would be to avoid using them on the same element.
Also, you probably do not want the behavior that the examples show; you want the checkbox to respond to clicks on the complete label text, not only the checkbox. Hence, the cleanest solution would be to wrap the input (with ng-model) inside a label (with ng-click):
<label ng-click="onCompleteTodo(todo)">
<input type='checkbox' ng-model="todo.done">
{{todo.text}}
</label>
Working example: http://jsfiddle.net/b3NLH/1/

Why dont you use
$watch('todo',function(.....
Or another solution would be to set the todo.done inside the ng-click callback and only use ng-click
<div ng-app="myApp" ng-controller="Ctrl">
<li ng-repeat="todo in todos">
<input type='checkbox' ng-click='onCompleteTodo(todo)'>
{{todo.text}} {{todo.done}}
and
$scope.onCompleteTodo = function(todo) {
todo.done = !todo.done; //toggle value
console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
$scope.current = todo;
}

Replacing ng-model with ng-checked works for me.

It is kind of a hack but wrapping it in a timeout seems to accomplish what you are looking for:
angular.module('myApp', [])
.controller('Ctrl', ['$scope', '$timeout', function ($scope, $timeout) {
$scope.todos = [{
'text': "get milk",
'done': true
}, {
'text': "get milk2",
'done': false
}];
$scope.onCompleteTodo = function (todo) {
$timeout(function(){
console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
$scope.doneAfterClick = todo.done;
$scope.todoText = todo.text;
});
};
}]);

The ordering between ng-model and ng-click seems to be different and it's something you probably shouldn't rely on. Instead you could do something like this:
<div ng-app="myApp" ng-controller="Ctrl">
<li ng-repeat="todo in todos">
<input type='checkbox' ng-model="todo.done" ng-click='onCompleteTodo(todo)'>
{{todo.text}} {{todo.done}}
</li>
<hr>
task: {{current.text}}
<hr>
<h2>Wrong value</h2>
done: {{current.done}}
</div>
And your script:
angular.module('myApp', [])
.controller('Ctrl', ['$scope', function($scope) {
$scope.todos=[
{'text': "get milk",
'done': true
},
{'text': "get milk2",
'done': false
}
];
$scope.current = $scope.todos[0];
$scope.onCompleteTodo = function(todo) {
console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
//$scope.doneAfterClick=todo.done;
//$scope.todoText = todo.text;
$scope.current = todo;
};
}]);
What's different here is whenever you click a box, it sets that box as what's "current" and then display those values in the view. http://jsfiddle.net/QeR7y/

Usually this is due to another directive in-between your ng-controller
and your input that is creating a new scope. When the select writes
out it value, it will write it up to the most recent scope, so it
would write it to this scope rather than the parent that is further
away.
The best practice is to never bind directly to a variable on the scope
in an ng-model, this is also known as always including a "dot" in
your ngmodel. For a better explanation of this, check out this video
from John:
http://www.youtube.com/watch?v=DTx23w4z6Kc
Solution from: https://groups.google.com/forum/#!topic/angular/7Nd_me5YrHU

I just replaced ng-model with ng-checked and it worked for me.
This issue was when I updated my angular version from 1.2.28 to 1.4.9
Also check if your ng-change is causing any issue here. I had to remove my ng-change as-well to make it working.

.task{ng:{repeat:'task in model.tasks'}}
%input{type:'checkbox',ng:{model:'$parent.model.tasks[$index].enabled'}}

Related

AngularJS, ng-repeat on radio and $scope.watch

I'm using this example: https://jsfiddle.net/qnw8ogrk/1/ to create my radio-buttons.
I would like to be able to use $scope.watch on the radio-buttons, but I'm not able to use:
$scope.watch('selected', ...
What should I assign to .watch to be able to detect whenever a user clicks on a radio button?
Actual mistake was $scope.watch should be $scope.$watch Having watcher over a scope variable would not be the good idea. Instead of placing watcher over ng-model I'd suggest you to use ng-change which will only fires up when radio button ng-model value gets changed.
<label ng-repeat="option in options">
<input type="radio" ng-change="test()" ng-model="$parent.selected" ng-value="option" />{{option}}
</label>
And then don't use get rid of $parent notation for accessing outer scope of ng-repeat you could switch to use controllerAs pattern, and then change ng-controller directive to use alias of controller. Also that will change the controller implementation, bind value to this(context) instead of $scope
Markup
<div ng-controller="mainController as vm">
<label ng-repeat="option in vm.options">
<input type="radio" ng-change="vm.test()" ng-model="vm.selected" ng-value="option" />{{option}}
</label>
</div>
Controller
appModule.controller('mainController', function($scope) {
var vm = this;
vm.selected = 'red';
vm.options = ['red', 'blue', 'yellow', 'green'];
vm.test = function() {
alert('Changed');
}
});
Forked Plunkr
I agree with #PankajParker, but it is possible from you controller as well. You just got the syntax wrong, it is $scope.$watch, not $scope.watch
Have a look at this: https://jsfiddle.net/qnw8ogrk/32/
Fiddle: https://jsfiddle.net/stevenng/qnw8ogrk/33/
Markup
<div ng-app="app">
<div ng-controller="mainController">
<label ng-repeat="option in options">
<input type="radio" ng-change="picked(this)" ng-model="$parent.selected" ng-value="option"/>{{option}}
</label>
</div>
</div>
Script
var appModule = angular.module('app', []);
appModule.controller('mainController', function($scope) {
$scope.selected = 'red';
$scope.options = ['red', 'blue', 'yellow', 'green'];
$scope.picked = function($scope) {
alert('you picked: ' + $scope.selected);
}
});

angularjs manual bootstrapping does not update property value when input changes through method call

The problem, I have is AngularJS application is not updating the result when input changes in the input HTML field. If I turn this to auto bootstrapping it does work as expected. I do not know what am i doing wrong?
This is JS file.
angular.module('doublevalue', [])
.controller('DoubleController', ['$scope', function($scope){
$scope.value = 0;
$scope.double = function(value) { $scope.value = value * 2; }
}]);
angular.element(document).ready(function() {
var div3 = document.getElementById('App3');
angular.bootstrap(div3, ['doublevalue']);
});
JSFIDDLE version:
https://jsfiddle.net/as0nyre3/48/
HTML file:
<div id ='App3' ng-controller='DoubleController'>
Two controller equals
<input ng-model='num' ng-change='double(num)'>
<span> {{ value }}</span> </div>
Auto bootstrapping one link:
https://jsfiddle.net/as0nyre3/40/
Please help me!
This is working, with a problem like that you should always check your selectors
<div id="App3" ng-controller="myCtrl">
<input type='text' ng-model='name' ng-change='change()'>
<br/> <span>changed {{counter}} times </span>
</div>
<script>
angular.element(document).ready(function() {
angular.bootstrap(document.getElementById('App3'), ['myApp']);
})
</script>

angularJS radio buttons not functioning

I'm having trouble communicating with my angularJS radio buttons. I'm using the material design framework. I'm fairly new to angular.
HTML
<div ng-controller="MainCtrl as ctrl">
<md-radio-group class="user-type">
<div layout="row" layout-sm="column" layout-align="space-between" layout-align-sm="space-around center">
<md-radio-button ng-model="userType" value="prospective" name="user_type" ng-change='newValue(value)'>Prospective Patient</md-radio-button>
<md-radio-button ng-model="userType" value="patient" name="user_type" ng-change='newValue(value)'>Patient</md-radio-button>
<md-radio-button ng-model="userType" value="caregiver" name="user_type" ng-change='newValue(value)'> Caregiver </md-radio-button>
<md-radio-button ng-model="userType" value="doctor" name="user_type" ng-change='newValue(value)'>Doctor</md-radio-button>
</div>
</md-radio-group>
</div>
JS
.controller('MainCtrl', ['$scope', function($scope) {
var self = this;
$scope.newValue = function(value) {
console.log(value);
};
$scope.$watch('userType', function(value){
if(value == "patient"){
console.log(value);
self.showPatientStepTwo = true;
}else{
console.log(value);
self.showPatientStepTwo = false;
}
});
}])
My ng-change isn't firing and my $watch isn't working either.
Can anyone find where I'm going wrong? I can't communicate between my controller and view!
When you use the controller as syntax, you should bind to that instead of the scope. I think the md-radio-button directive was creating a child scope that was messing things up but hard to reproduce without that directive.
Here's a plunker with the model and click bound to ctrl instead of $scope: http://plnkr.co/edit/fSTBDAMZLFKJgRD4br9K?p=preview
Radios changed to input, but referencing the ctrl:
<input type="radio" ng-model="ctrl.userType" value="prospective" name="user_type" class="user-type-rdo md-warn md-hue-2" ng-change='ctrl.newValue(value)'>Prospective Patient
And the controller updated to move the newValue function off $scope:
.controller('MainCtrl', ['$scope', function($scope) {
var self = this;
this.newValue = function(value) {
console.log(value);
};
$scope.$watch(function(){return self.userType;}, function(value){
if(value == "patient"){
console.log(value);
self.showPatientStepTwo = true;
}else{
console.log(value);
self.showPatientStepTwo = false;
}
});
}])
The newValue function logs undefined - not sure what you were trying to do there, but you can use self.userType in the newValue function if you want the value.
First thing: You don't need to declare an ng-model on ea. angular-material radio button when using radio groups, as per the angular-material docs for radio buttons.
The second thing is, the standard $scope events behave a bit differently when you build your controllers using controllerAs syntax See controllerAs Reference.
function MainController($scope, $log) {
var vm = this;
vm.title = 'Some Title';
vm.showPatientStepTwo = false;
// does not work
$scope.$watch('userType', function(newVal, oldVal){
// do work
});
// works
$scope.$watch('vm.userType', function(newValue, oldValue) {
// do work with newValue
});
// also works
$scope.$watch(function() {
return vm.userType;
}, function(newValue, oldValue) {
vm.showPatientStepTwo = newValue === 'patient';
});
}
Working plunkr: http://plnkr.co/edit/Dth67cQJKarwt3NiE9yp
<div class="form-group">
<label>Type of ad <b class="text-danger">*</b></label><br>
<input type="radio" name="typeofAd" value="sell" ng-model="product.typeofAd"> I want to sell
<input type="radio" name="typeofAd" value="buy" ng-model="product.typeofAd"> I want to buy
</div>
radio button this way works fine for me
refer link
https://scotch.io/tutorials/handling-checkboxes-and-radio-buttons-in-angular-forms
you need to add ng-model and ng-change event like below.
<md-radio-group ng-model="selectedVal" ng-change="showSelected()">
and in your controller you can defined function as following.
$scope.showSelected= function(){
console.log($scope.selectedVal);
}

How do I choose which <input> gets focus in ng-repeat

I'm just starting to get to grips with angular and I am trying to do something that I think should be pretty simple, but I can't find anyone who has posted with exactly the same scenario. I have a collection which is initiated with three objects and I am using ng-repeat to generate a set of input fields for each of these objects. When the SPA is initialised I want the first input field to have focus: I can do with with autofocus if necessary. When the user TABs off the last input field I add another object to the collection using ng-blur="addRecord($index)". When the DOM is refreshed I want the first field in the new object to have focus. The difference between my effort and all the examples I can find online is that all the examples initiate the addition using a button and an ng-click event.
Because the DOM element is going to be created on the fly, I think I need a custom directive with a $timeout but this seems like a lot of work for what should be a fairly standard requirement. I am using 1.3.x Can anyone show me the basics of how to write the directive or point me at a library that already exists that will do what I want. My current code is set out below.
HTML
<body ng-app="myApp">
<div ng-controller="playerController">
<ul>
<li ng-repeat="player in players">
<input type="text" placeholder="FirstName" ng-model="player.firstName"></input>
<input type="text" placeholder="NicktName" ng-model="player.nickName"></input>
<input type="text" placeholder="SurnameName" ng-model="player.lastName" ng-blur="addNew($index)"></input>
{{player.firstName}} "{{player.nickName}}" {{player.lastName}}
</li>
</ul>
</div>
<script type="text/javascript" src="angular.js"></script>
<script type="text/javascript" src="myApp.js"></script>
</body>
myApp.js
var myApp = angular.module('myApp',[]);
myApp.controller('playerController',function($scope){
$scope.players = [
{
"firstName":"Aaron",
"lastName":"Reese",
"nickName":"Star Wars",
"givemefocus": "true"
},
{
"firstName":"Ian",
"lastName":"Soinne",
"nickName":"Dominian",
"givemefocus": "false"
},
{
"firstName":"Aaron",
"lastName":"Bailey",
"nickName":"Fernando",
"givemefocus": "false"
}
];
$scope.addNew = function($index){
if($index == (players.length -1 )){
$scope.newPlayer = {
"firstName":"",
"lastName":"",
"nickName":"",
"givemefocus": "true"
};
$scope.players.push($scope.newPlayer);
}
}
});
app.directive('takefocus', function($timeout) {
return function(scope, element, attrs) {
scope.$watch(attrs.takefocus, function(value) {
if (value) {
$timeout(function() { element.focus(); });
}
});
};
});
In html:
<li ng-repeat="player in players">
<input type="text" placeholder="FirstName" ng-model="player.firstName" takefocus="player.givemefocus"></input>
Add Id to first input <input id=input{{$index}}../> to find this input later in onBlur function.
<li ng-repeat="player in players">
<input id="input{{$index}}" type="text" placeholder="FirstName" ng-model="player.firstName"></input>
<input type="text" placeholder="NicktName" ng-model="player.nickName"></input>
<input type="text" placeholder="SurnameName" ng-model="player.lastName" ng-blur="addNew($index)"></input>
{{player.firstName}} "{{player.nickName}}" {{player.lastName}}
</li>
Add $timeout to controller. In function addNew use $timeout with zero delay to wait to the end of DOM rendering. Then input can be found by getElementById in $timeout function.
myApp.controller('playerController',function($scope, $timeout)
{
$scope.addNew = function($index){
if($index == (players.length -1 )){
$scope.newPlayer = {
"firstName":"",
"lastName":"",
"nickName":"",
"givemefocus": "true"
};
$scope.players.push($scope.newPlayer);
$timeout(function ()
{
document.getElementById("input" + ($index + 1)).focus();
});
}
}
});

ng-show doesn't work in scope

I'm trying to show/hide something using angular but when it's called via $scope it doesn't work. If I change the show variable with ng-model it works fine.
<div id="editClient"
class="accordeon panel-group"
role="tablist"
aria-multiselectable="true"
ng-show="show">
....
</div>
And in my script this doesn't work.
client.controller('clientController', function($scope) {
$scope.show = true;
$scope.test = function() {
alert('clicked');
$scope.show = true;
}
The "clicked" is shown and I tried to use $apply, too, but result is the same.
But when I use ng-model it works
<input type="checkbox" value="true" ng-model="show">
Can someone explain me why it doesn't work with $scope.show ?
Debug.
Following works just fine using checkbox with ng-bind and button firing function in controller.
<input type="checkbox" ng-model="show">Toggle visibility
<br>
<button type="button" ng-click="toggle()">Toggle visibility</button>
<hr>
<div ng-show="show">Visible</div>
<div ng-show="!show">Hidden</div>
app.controller('Ctrl', function($scope, $http) {
$scope.show = true;
$scope.toggle = function() {
$scope.show = !$scope.show;
};
});
Following works fine using $scope. See jsfiddle
http://jsfiddle.net/ffKTy/312/

Resources