Angular ng-change not working for checkboxes - javascript

Here is my code below. The ng-change of individual checkboxes is not getting triggered when it is changed by clicking Select All button but it is getting triggered when it's selected individually. I need the itemChecked method to be triggered when the Select All button is clicked.
Here is a Codepen Link for the same
HTML
<div ng-app="Test">
<div ng-controller="TestController">
<form>
<div ng-repeat="item in list track by $index">
<input type="checkbox" ng-model="item" ng-change="itemChecked($index)">{{$index + 1}}
</div>
</form>
<button ng-click="toggleSelection()">Select all</button>
</div>
</div>
JavaScript
var app = angular.module("Test", []);
app.controller("TestController", [
"$scope",
"$http",
function($scope, $http) {
$scope.list = [false, false, false, false, false];
$scope.itemChecked = function(i) {
console.log(i);
};
$scope.toggleSelection = function() {
for (var i in $scope.list) {
$scope.list[i] = true;
}
};
}
]);
Please let me know what I need to change or what I am doing wrong to fix this.

You have set wrong variable in ng-model. The ng-model section should be:
ng-model="list[$index]"
To listen the collection, you have to use the following:
$scope.$watchCollection
It is working perfectly in the following code, check the code snippet:
var app = angular.module("Test", []);
app.controller("TestController", [
"$scope",
"$http",
function($scope, $http) {
$scope.list = [false, false, false, false, false];
$scope.itemChecked = function(i) {
console.log(i);
console.log($scope.list[i]);
};
$scope.$watchCollection('list', function (oldValue, newValue) {
//console.log(oldValue);
//console.log(newValue);
//console.log($scope.list);
for(var i = 0; i < oldValue.length;i++){
if (oldValue[i]!==newValue[i]) {
$scope.itemChecked(i);
}
}
});
$scope.toggleSelection = function() {
for (var i in $scope.list) {
$scope.list[i] = true;
}
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="Test">
<div ng-controller="TestController">
<form>
<div ng-repeat="item in list track by $index">
<input type="checkbox" ng-model="list[$index]" ng-change="itemChecked($index)">{{$index + 1}}
</div>
</form>
<button ng-click="toggleSelection()">Select all</button>
</div>
</div>

What you need is watchCollection method. ngChange works only if the value is changed from HTML. It is not triggered when the value is changed from controller.
app.controller("TestController", [
"$scope",
"$http",
function($scope, $http) {
$scope.list = [false, false, false, false, false];
$scope.itemChecked = function(i) {
console.log(i);
};
$scope.toggleSelection = function() {
for (var i in $scope.list) {
$scope.list[i] = true;
}
};
/*********************************************************/
$scope.$watchCollection('list', function (newVal, oldVal) {
console.log('collection changed') });
}
/*********************************************************/
]);
Or If you just want itemChecked method to be called whenever the selectAll button is clicked, Then just call itemChecked inside toggleSelection method.
$scope.toggleSelection = function() {
for (var i in $scope.list) {
$scope.list[i] = true;
$scope.itemChecked(i);
}
};

Related

disable button if none of the options are selected angularJS

I would like to disable the add button if none of the options in the list are selected or if the selected class is not there in the list.
HTML:
<div ng-repeat="json in myJson" ng-class="{'selected': json.selected}" ng-model="selectionOptions" ng-click="selectItem($index)">
{{json.title}}
</div>
<button ng-disabled="hideAdd">Add</button>
I have tried:
$scope.$watch('selectionOptions', function(val) {
if(angular.element('selectionOptions').hasClass('selected')) {
$scope.hideAdd = false;
} else {
$scope.hideAdd = true;
}
});
JSFiddle Demo
No need for a directive or a $watch.
Remove the ng-model as it's doing nothing. Even if it did work every item would be bound to the same variable.
Use ng-click to call a function on $scope which toggles the selected value and adds/removes it from the list of selected options.
You can use ng-show="selectedOptions.length" to hide/show the add button.
angular.module('app', []).controller('mainCtrl', function($scope) {
$scope.myJson = [{
title: 'test1',
selected: false
}, {
title: 'test2',
selected: true
}, {
title: 'test3',
selected: false
}, {
title: 'test4',
selected: false
}];
// Initialize array with already selected options
$scope.selectedOptions = $scope.myJson.filter(function(item) {
return item.selected;
});
$scope.toggle = function(item) {
// Toggle selected
item.selected = !item.selected;
// Add it to list
if (item.selected) {
$scope.selectedOptions.push(item);
} else {
// Remove it from list
$scope.selectedOptions.splice($scope.selectedOptions.indexOf(item), 1);
}
};
});
.selected {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller='mainCtrl'>
<div app-click="">
<div ng-repeat="json in myJson" ng-click="toggle(json)" ng-class="{'selected': json.selected}">
{{json.title}}
</div>
<button ng-show="selectedOptions.length">Add</button>
</div>
<br/>Selected options: <pre>{{selectedOptions | json}}</pre>
</div>
I don't think you can use ng-model here, since there is no input.
You can watch the json or you can use a filter to determine if there are any selected elements. Example of the latter:
.directive('appClick', function() {
return {
restrict: 'A',
scope: true,
controller: function($scope, $element) {
optionIsSelected();
function optionIsSelected() {
$scope.hideAdd = $scope.myJson.filter(function(el) {
return el.selected
}).length === 0;
}
$scope.selectItem = function(index) {
$scope.myJson[index].selected = !($scope.myJson[index].selected);
optionIsSelected();
};
}
}
}
Working fiddle

How to remove checkbox from array in angular js

Trying to create a function that will remove the selected checkbox items from the array but can't seem to figure it out. I have tried using splice and .pop() but it just won't remove the items I select. I think the best way would be to use an if statement but I do not know how to write it so that it shows true or false. Please help!!!
JS:
.controller('toDoCtrl', function($scope, toDoFactory){
//set $scope variables
$scope.tasks = toDoFactory.tasks;
$scope.removeTasks = toDoFactory.removeTasks;
})
.factory('toDoFactory', ['$http', function($http){
var toDo = {
tasks: [],
removeTasks: function(selectedTask){
angular.forEach(toDo.tasks, function(value, selectedTask){
var i = toDo.tasks.indexOf(value);
toDo.tasks.splice(toDo.tasks.indexOf(i), 1);
});
}
};
return toDo;
}])
HTML:
<button ng-click="removeTasks()">Remove</button>
I did not really understood if you wanted to delete all selected tasks or just one.
Anyway, you can do this for example :
JS:
app
.controller('toDoCtrl', function($scope, toDoFactory){
//set $scope variables
$scope.data = {};
$scope.data = toDoFactory.data;
$scope.removeTasks = toDoFactory.removeTasks;
$scope.removeTask = toDoFactory.removeTask;
})
.factory('toDoFactory', ['$http', function($http){
var toDo = {
data : {
tasks: [
{text: "hello world", done: false},
{text: "hello world2", done: false},
{text: "hello world3", done: false}
]
},
removeTasks: function(){
toDo.data.tasks = toDo.data.tasks.filter(function(task){
return !task.done;
});
},
removeTask: function(index){
toDo.data.tasks.splice(index, 1);
},
};
return toDo;
}]);
HTML:
<body ng-controller="toDoCtrl">
<div ng-repeat='task in data.tasks'>
<input type='checkbox' ng-model='task.done' />
<span>{{task.text}}</span>
</div>
<br/>
<button ng-click="removeTasks()">Remove</button>
</body>
Plunkr :
https://plnkr.co/edit/bTG0fEUZl1uoTIFT1NhC?p=preview

How pass variables to directive from controller?

HTML:
<div ng-repeat="item in productArr">
{{ item.title }}
</div>
<div category-page-navigation current-page='currentPage' category-products-count='productsCount'></div>
JS:
.controller('categoryController', ['$scope', '$location', '$http', '$q', '$window', '$stateParams', function($scope, $location, $http, $q, $window, $stateParams) {
$scope.currentPage = 1;
$scope.productsCount = 0;
var GET = {
getProductData: function() {
var defer = $q.defer();
$http.post('services/loadProduct.php', {
'id' :1,
}).then(function(response) {
defer.resolve(response);
}, function(response) {
defer.resolve([]);
});
return defer.promise;
}
};
var getData = {
getProduct: function() {
var productData = GET.getProductData();
$q.all([productData]).then(
function(response) {
$scope.productArr = response[0].data.products;
$scope.productsCount = response[0].data.products.length;
});
}
};
getData.getProduct();
}])
.directive('categoryPageNavigation', function($compile, $parse) {
return {
scope: {
currentPage: '=currentPage',
categoryProductsCount: '=categoryProductsCount'
},
link: function (scope, element, attrs) {
debugger;
// Here scope.categoryProductsCount = undefined
// ...
$scope.$watch(scope.currentPage, function(value) {
// ...
});
}
};
});
I try to form new HTML for navigation to manipulate with HTML I get from ng-repeat.
In directive I need currentPage(from start =1) and total count of items from ng-repeat(length of array) witch I get from service. How I can pass variables to directive? First I need to get variables from service(ajax request or something else) then pass variables(some ather data) to directive.
If I understood correctly what you mean. Here is a code pen example on how to shared data between you controller and your directive.
A good read to understand the code below:https://docs.angularjs.org/guide/providers
http://codepen.io/chocobowings/full/Xmzxmo/
var app = angular.module('app', []);
//-------------------------------------------------------//
app.factory('Shared', function() {
return {
sharedValue: {
value: '',
}
};
});
//-------------------------------------------------------//
app.controller('ctrl', function($scope, Shared) {
$scope.model = Shared.sharedValue;
});
//-------------------------------------------------------//
app.directive('directive', ['Shared',
function(Shared) {
return {
restrict: 'E',
link: function(scope) {
scope.model = Shared.sharedValue;
},
template: '<div><input type="text" ng-model="model.value"/></div>'
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
Ctrl:
<div ng-controller="ctrl">
<input type="text" ng-model="model.value" />
<br/>
</div>
Directive:
<directive value="model.value"></directive>
</div>

Call function or pass data to another controller AngularJS

I already have seem other topics with this kind of issue, but no one could help me... So here is my issue:
I have a navbar with a button for search, this buttons makes and get request from a webservice and returns a json object which must be apply to fill an table list. The problem is, my button and my table are in separated controllers, and it does work like I expected.
var app = angular.module('clientRest', []).controller('lista', ['$scope', 'loadLista', function($scope, loadLista) {
$scope.contatos = loadLista.getContatos();
}]).controller('pesquisa', ['$scope', '$http', 'loadLista', function($scope, $http, loadLista) {
$scope.listar = function() {
$http.get("http://localhost/wsRest/index.php/contato").success(function(response) {
loadLista.setContatos(response);
});
};
}]).service('loadLista', function() {
var contatos = [];
return {
getContatos: function() {
return contatos;
},
setContatos: function(c) {
contatos = c;
}
};
});
My code...
When I call listar() from pesquisa controller I need to send received data to $scope.contatos from lista controller to make my ng-repeat work, everything with a single click.
How can I do it?
Thanks everyone
Better to use a service to share data between two controllers / modules as this might be the best approach. You can refer the code segment given below to understand the concept.
angular.module('app.A', [])
.service('ServiceA', function() {
this.getValue = function() {
return this.myValue;
};
this.setValue = function(newValue) {
this.myValue = newValue;
}
});
angular.module('app.B', ['app.A'])
.service('ServiceB', function(ServiceA) {
this.getValue = function() {
return ServiceA.getValue();
};
this.setValue = function() {
ServiceA.setValue('New value');
}
});
In order to trigger the data receipt event, you may use
Broadcast / emit messages - with #broadcast / #emit
An angular promise with a call back
Controller initiation function to reload the previously read information from a service
.controller('MyController', function($scope, ServiceA) {
$scope.init = function() {
$scope.myValue = ServiceA.getValue();
};
// Call the function to initialize during Controller instantiation
$scope.init();
});
Use $rootScope.$emit to emit a change event when setting the variable and use $on to get the value in the lista controller. I used customListAr here just to demostrate a button click. Does this help?
var app = angular.module('clientRest', [])
.controller('lista', ['$scope', 'loadLista', '$rootScope',
function($scope, loadLista, $rootScope) {
console.log(loadLista);
$scope.contatos = loadLista.getContatos();
$rootScope.$on('change', function() {
$scope.contatos = loadLista.getContatos();
});
}
])
.controller('pesquisa', ['$scope', '$http', 'loadLista',
function($scope, $http, loadLista) {
$scope.listar = function() {
$http.get("http://localhost/wsRest/index.php/contato").success(function(response) {
loadLista.setContatos(response);
});
};
$scope.customListAr = function() {
loadLista.setContatos(["item 1" , "item 2", "item 3"]);
}
}
])
.service('loadLista', ['$rootScope',
function($rootScope) {
var contatos = [];
return {
getContatos: function() {
return contatos;
},
setContatos: function(c) {
contatos = c;
$rootScope.$emit('change');
}
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="clientRest">
<div ng-controller="lista">
<ul>
<li ng-repeat="a in contatos">{{a}}</li>
</ul>
</div>
<div ng-controller="pesquisa">
<button ng-click="customListAr()">Click Me</button>
</div>
</div>
Your problem is that when you do $scope.contatos = loadLista.getContatos(); you are setting a static value, and angular is unable to effectively create a watcher for that object because your setContatos method is creating a new object each time. To get around this, have the controller's scope hold a reference to the parent object and then it will automatically have a watcher on that object.
var app = angular.module('clientRest', [])
.controller('lista', ['$scope', 'loadLista', function($scope, loadLista) {
$scope.contatos = loadLista.contatos;
}])
.controller('pesquisa', ['$scope', '$http', 'loadLista', function($scope, $http, loadLista) {
$scope.listar = function() {
$http.get("http://localhost/wsRest/index.php/contato"
).success(function (response) {
loadLista.contatos.data = response;
});
};
}])
.service('loadLista', function() {
var lista = {
contatos: {},
};
return lista;
});
// view:
<ul>
<li ng-repeat="contato in contatos.data">
{{ contato }}
</li>
</ul>

Bind a service variable to a directive?

I have a controller which contains a function that gets some data from the server. I store that data in a service variable. This service is then injected into a directive. I want the directive to be auto updated, whenever this function is called and the data is renewed.
My controller:
angular
.module('myApp')
.controller('myCtrl', ['$scope', 'SomeService', function($scope, SomeService) {
$scope.update = function() {
SomeService.myValue = 100;
}
}]);
The directive:
angular.module('myApp')
.directive('myDirective', ['SomeService', function(SomeService) {
return {
templateUrl : 'views/myDirective.html',
restrict : 'E',
scope : false,
controller : function($scope) {
this.myValue = SomeService.myValue;
}
};
}]);
The template:
<div>
{{ myValue }}
</div>
The update function is called when a button is clicked and it updates myValue to a new value. I want it to be automatically reflected in the directive.
Plunk: http://plnkr.co/edit/OUPzT4MFS32OenRIO9q4?p=preview
Please see working demo below
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, SomeService) {
$scope.name = SomeService.data;
$scope.update = function() {
$scope.name.myValue += 1;
}
});
app.factory('SomeService', function() {
var data = {
myValue: 0
};
return {
data: data
}
});
app.directive('myDirective', ['SomeService',
function(SomeService) {
return {
templateUrl: 'myDirective.html',
restrict: 'EA',
scope: false,
link: function(scope, elem, attr) {
scope.data = SomeService.data
}
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="MainCtrl">
<p>My Value: {{name.myValue}}</p>
<button ng-click="update()">Click</button>
<hr/>
<div my-directive></div>
<script type="text/ng-template" id="myDirective.html">
<h3>My Directive</h3>
<p>Value: {{data.myValue}}</p>
</script>
</div>
</div>
You can try by adding the reference of the service to the directive itself..
The directive:
angular.module('myApp')
.directive('myDirective', ['SomeService', function(SomeService) {
return {
templateUrl : 'views/myDirective.html',
restrict : 'E',
scope : false,
controller : function($scope) {
this.SomeService = SomeService;
}
};
}]);
The template:
<div>
{{ SomeService.myValue }}
</div>
Edit : I went through your plunker, and have finally got it working.
You can check the updated code here
#RutwickGangurde and others who were having issues, if you're trying to set a scope variable that is not an object it won't work. I'm guessing that's what you're currently doing in your service:
...
this.myVar = true;
...
and then trying to set it in the directive/controller:
...
scope.myVar = myService.myVar;
...
That will NOT work for getting the updated variable in the service when it changes.
Try this instead in your service:
...
this.myObj = {};
this.myObj.myVar = true;
...
and in your directive/controller:
...
scope.myValue = myService.myObj;
...
and in your html:
...
{{ myValue.myVar }}
...
I would have made this as a comment, but I don't have sufficient privileges yet so decided to post as a response with a very brief example.

Categories

Resources