AngularJS ng-checked not updating with model - javascript

On my page I have brand A and Brand B. By clicking on one, I display a filtered list of address types, and clicking on one of those then filters locations.
When I click on brand A, I save an object of the checked items and overwrite with brand B's list. When I click back to Brand A, the location list is filtered correctly, but the address types are not being checked, even though they exists in the array.
FYI, md-checkbox is an Angular Material checkbox.
Here is my HTML:
<ul >
<li id="brandA" ng-click="setBrand(0)" ng-class="{'brandSelected': brand == 0}">
</span>Brand A</span>
</li>
<li id="brandB" ng-click="setBrand(1)" ng-class="{'brandSelected': brand == 1}">
</span>Brand B</span>
</li>
</ul>
<div ng-repeat="item in addressTypes | filter : filterAddressTypeByBrand">
<md-checkbox class="md-primary" ng-checked="exists(item)" ng-click="toggle(item)" ng-model="item.selected">
{{ item.desc }}
</md-checkbox>
</div>
Here is an extract of my controller:
$scope.addressTypesSelected = [];
$scope.addressTypesSelectedBrandA = [];
$scope.addressTypesSelectedBrandB = [];
$scope.toggle = function (item) {
var idx = $scope.addressTypesSelected.indexOf(item);
if (idx > -1) $scope.addressTypesSelected.splice(idx, 1);
else $scope.addressTypesSelected.push(item);
};
$scope.exists = function (item) {
return $scope.addressTypesSelected.indexOf(item) > -1;
};
$scope.setBrand = function (val) {
$scope.brand = val;
$scope.addressTypesSelected = [];
if ($scope.brand == 1) {
$scope.addressTypesSelectedBrandB = [];
$scope.addressTypesSelectedBrandB = angular.copy($scope.addressTypesSelected);
$scope.addressTypesSelected = angular.copy($scope.addressTypesSelectedBrandA);
} else {
$scope.addressTypesSelectedBrandA = [];
$scope.addressTypesSelectedBrandA = angular.copy($scope.addressTypesSelected);
$scope.addressTypesSelected = angular.copy($scope.addressTypesSelectedBrandB);
}
$scope.filterDealers($scope.dealers);
}
What needs to be done to get the checkboxes checked when switching between brands?
UPDATE 1.
I've added ng-model="item.selected" to md-checkbox. This seemed to do the trick. However, if I check a box on Brand A, then click on Brand B, then click Brand A, the checkbox remains checked. If I continue and click on Brand B then click again on Brand A, the checkbox is unchecked, yet the value in the model is set to true.

Related

remove function deletes wrong items from array

So i am making my to-do app and i have encountered this weird problem where my removeToDo function targets wrong items in weird pattern apart from the very first deleted item(which always is being removed just fine). Let's say we have items in the array with id's from 0 to 6 :
Clicked to remove item with ID = 3 - removed item with ID = 3
Clicked to remove item with ID = 4 - removed item with ID = 5
Clicked to remove item with ID = 5 - removed item with ID = 6
Clicked to remove item with ID = 0 - removed item with ID = 0
Clicked to remove item with ID = 2 - removed item with ID = 4
Clicked to remove item with ID = 1 - removed item with ID = 2
Clicked to remove item with ID = 6 - removed item with ID = 1
So it's not really following an obvious pattern (thought it may be something like id + 1 or something but it doesn't seem like it). Also i did exactly the same test as above for the second time to see if it randomizes, it doesn't, the results were exactly the same.
Here is some code
HTML
<body>
<div class='app'>
<ul id='list'>
</ul>
<div class="footer">
<i class="fas fa-plus-circle" aria-hidden="true" id='addButton'></i>
<input type="text" id='itemInput' placeholder="Add a to-do" />
</div>
</div>
<script src="./app.js"></script>
</body>
JS
const list = document.getElementById("list");
const input = document.getElementById("itemInput");
let id;
//get the item from the local storage
let data = localStorage.getItem('TODO');
//check if data is not empty
if(data) {
LIST = JSON.parse(data)
id = LIST.length; // set the list id to the last in the array
loadList(LIST); // load all the items in the array to the UI
} else {
//if data is empty
LIST = [];
id = 0;
}
function loadList(array) {
array.forEach(item => {
addToDo(item.name, item.id, item.done, item.trash);
})
}
function addToDo(toDo, id, done, trash) {
// if trash is true do not execute the code below
if (trash) {return ;}
const DONE = done ? check : uncheck;
const LINE = done ? lineThrough : "";
const text =`
<li class="item">
<i class="far ${DONE}" id='${id}'></i>
<div class="description ${LINE} wrap">${toDo}</div>
<i class="fas fa-trash-alt" id='${id}'></i>
</li>`;
const position = "beforeend";
list.insertAdjacentHTML(position, text);
}
// remove to-do
function removeToDo(element, i) {
let newList = [...LIST]
element.parentNode.parentNode.removeChild(element.parentNode);
i = newList.indexOf(newList[event.target.id]) //<-- i think that is the problem, the indexing is not working as it should, as a result app gets confused ?
alert(i)
//newList[event.target.id].trash = true;
newList.splice(i, 1);
LIST = newList;
console.log(LIST);
return LIST;
}
// click listener for job complete and job delete
list.addEventListener('click', e => {
const element = e.target;
if(e.target.className == "fas fa-trash-alt" ){
removeToDo(element);
}else if(e.target.className == "far fa-circle") {
jobComplete(element);
}else if(e.target.className == "far fa-check-circle"){
jobComplete(element);
}
}
)
//add a task with "enter" key
document.addEventListener("keyup", (event) => {
if(event.keyCode == 13){
const toDo = input.value;
if(toDo) {
addToDo(toDo, id, false, false);
LIST.push(
{
name: toDo,
id: id,
done: false,
trash: false
}
);
localStorage.setItem('TODO', JSON.stringify(LIST));
id++;
input.value = '';
}
}
})
EDIT :
The items deleted in weird pattern are in the LIST array, actual buttons i click are being deleted just fine. I think i didn't explained that well enough
I think it will be best not to consider the id for removing items.
You can consider the value as well.
Instead of splice(i,1), please try using
newList = newList.filter(function( obj ) {
return obj.name !== element.previousElementSibling.innerHTML;
});
The problem is this:
id = LIST.length; // set the list id to the last in the array
The .length property of an array returns the number of items in the array, but array indexes are zero-based. An array with 5 items in it (length === 5), will have its last item index be 4.
The index of the last item in an array is .length -1.

AngularJs ngclass checkall boxes conditions

I'm trying to find a way through ngclass conditions to check all boxes automatically when the user deselect them both .
to make it clear , there're two item in cards as it shown in the image , when the user deselect first one , then the other card will be checked , then when the user deselect the second one , It should automatically select both of them again .
I tried the code below but it didn't give the desired result
HTML
<ion-list ng-show="transactionsCtrl.showCardBox">
<ion-item class="bg-blue p-0">
<div class="dotted-1"></div>
<div ng-repeat="singleCard in transactionsCtrl.cards">
<div class="row p-tb0 m-t5" ng-click="transactionsCtrl.ToggleCardFilter(singleCard.g_id)">
<div class="col col-20">
<div ng-class="{'image-size-1-50 white-checked-image': singleCard.selected || singleCard [0].selected, 'image-size-1-50 white-unchecked-image': !singleCard.selected}"></div>
</div>
<div class="col col-80">
<div class="sub-title-white oslight">{{(singleCard.cardNumberMasked).slice(-5)}} <span class="m-l10 right {{transactionsCtrl.PlaceCardImageClassForFilter(singleCard.g_productSubCategory)}}"> </span></div>
</div>
self.ToggleCardFilter = function(cardId) {
// toggle on-screen indicator
for (var c = 0; c < self.cards.length; c++)
if (self.cards[c].g_id == cardId)
self.cards[c].selected = !self.cards[c].selected;
// store card status to filter
var idx = $scope.transactionFilter.cards.indexOf(cardId);
if (idx == -1)
$scope.transactionFilter.cards.push(cardId);
else
$scope.transactionFilter.cards.splice(idx, 1);
self.applyFilterChange();
};
here's the function which display the result for the cards even if both of them aren't selected
DatabaseFactory.GetAllFromDB("Card").then(function(result) {
self.cards.length = 0;
if (result.rows.length > 0) {
var addAllCardsToFilter = false;
// first time and when all cards are deselected
if ($scope.transactionFilter.cards.length === 0)
addAllCardsToFilter = true;
for (var r = 0; r < result.rows.length; r++) {
self.cards.push(result.rows.item(r));
// initial fill and when all card were deselected - avoid empty display
if (addAllCardsToFilter)
$scope.transactionFilter.cards.push(self.cards[r].g_id);
if ($scope.transactionFilter.cards.indexOf(self.cards[r].g_id) > -1)
self.cards[r].selected = true;
}
}
});
Checked is a state so you cannot manage the state of the checkbox using ng-class. Please check my solution below:
I think this might help
HTML part
<div ng-app="myApp" ng-controller="CheckBoxController">
<input type="checkbox" ng-model="stateOption1" ng-change="changeTracker(stateOption1)">Option1
<input type="checkbox" ng-model="stateOption2" ng-change="changeTracker(stateOption2)">Option2
</div>
JavaScript Part
var myApp = angular.module('myApp',[]);
myApp.controller('CheckBoxController', ['$scope', function($scope) {
$scope.stateOption1 = true;
$scope.stateOption2 = true;
$scope.changeTracker = function(value)
{
if(!($scope.stateOption1 || $scope.stateOption2))
{
$scope.stateOption1 = true;
$scope.stateOption2 = true;
}
return value? false: true;
}
}]);

Angular JS filter an array with search input

So, my problem is that I want to create an search input field, which I have already done and I am using a search filter in an ng-repeat. What I want now to do is to select all the items I searched for using the select all checkbox.In this moment when I click the select all checkbox it checks all of my items from my array not from what I searched.
This is my html:
<div class="modal-body">
<div>
<md-input-container flex> <label>Search</label>
<input ng-model="search.name"> </md-input-container>
<md-button class="md-primary" ng-click="saveValues()">Update</md-button>
</div>
<div class="md-list">
<md-checkbox ng-model="modelItemsList.allItemsSelected"
ng-change="selectAll()">
Select all
</md-checkbox>
<md-list> <md-list-item class="md-3-line"
ng-repeat="mod
elItem in modelItemsList | filter:search">
<div class="md-list-item-text">
<md-checkbox ng-model="modelItem.isChecked " aria-label="Checkbox 1" ng-change="selectModelItem(modelItem)">
<h3>{{ $eval('modelItem.'+propertyName) }}</h3>
<p>{{ $eval('modelItem.'+propertyDesc) }}</p>
</md-checkbox>
</div>
</md-list-item> </md-list>
</div>
</div>
This is my select all function:
$scope.selectAll = function(){
console.debug("searchText", $scope.search);
//filteredArray = filterFilter($rootScope.modelItemsList, $scope.search);
//console.log(filteredArray);
console.log($rootScope.modelItemsList.allItemsSelected);
$rootScope.temp = [];
console.log($scope.modelItemsList);
$rootScope.modelItemsList.allItemsSelected = !$rootScope.modelItemsList.allItemsSelected;
console.log($rootScope.modelItemsList.allItemsSelected);
if($rootScope.modelItemsList.allItemsSelected){
for (var i = 0; i < $scope.modelItemsList.length; i++) {
$rootScope.temp.push($scope.modelItemsList[i].name);
console.log($scope.modelItemsList[i].name);
$scope.modelItemsList[i].isChecked = $rootScope.modelItemsList.allItemsSelected;
console.log($scope.modelItemsList[i].isChecked);
console.log($rootScope.modelItemsList.allItemsSelected);
}
}
else if (!$rootScope.modelItemsList.allItemsSelected){
for (var i = 0; i < $scope.modelItemsList.length; i++) {
$scope.modelItemsList[i].isChecked = $rootScope.modelItemsList.allItemsSelected;
$rootScope.temp = [];
console.log($scope.modelItemsList[i].isChecked);
}
}
}
I think I should make some kind of filter, but I am not so sure. I mean in my JS file in the select all function. Does anybody have any idea how should I do this ?
The easiest way to do this is to name the filtered list:
ie
<md-list-item class="md-3-line" ng-repeat="modelItem in filtered = ( modelItemsList | filter:search )">
...
</md-list-item>
Then in the controller you can access $scope.filtered
FYI
you can change {{ $eval('modelItem.'+propertyDesc) }} to
{{ modelItem[propertyDesc] }}
I'm sorry, but you weren't very clear in your question. From what I understand, this is what you want.
What I want now to do is to select all the items I searched for using the select all checkbox
Check out this plunkr where it's implemented.
https://plnkr.co/edit/vjzJEm8Wck8b5iUD4qz8?p=preview
The way you could do this is by setting up ng-change on your select all checkbox
<input type="checkbox" ng-change="selectAllFiltered()" ng-model="checkbox.value2" ng-true-value="true" ng-false-value="false"/>
Next, you would define the selectAllFiltered method in your controller. The objective is to set isSelected on $scope.filtered array when the array is filtered.
And when the array is NOT filtered, i.e. there is no string in the search.name field, then set isSelected on $scope.modelItemsList array.
Here's what your controller method should look like...
$scope.selectAllFiltered = function() {
if ($scope.checkbox.selectAll) {
$scope.checkbox.selectAll = false;
} else {
$scope.checkbox.selectAll = true;
}
if (!$scope.search.name) {
console.log('when no name is entered')
for (var i = 0; i < $scope.modelItemsList.length; i++) {
if (angular.isUndefined($scope.modelItemsList[i].isSelected)) {
$scope.modelItemsList[i].isSelected = $scope.checkbox.selectAll;
} else {
$scope.modelItemsList[i].isSelected = !$scope.modelItemsList[i].isSelected;
}
}
} else {
console.log('when some name is entered');
for (var i = 0; i < $scope.filtered.length; i++) {
if (angular.isUndefined($scope.filtered[i].isSelected)) {
$scope.filtered[i].isSelected = $scope.checkbox.selectAll;
} else {
$scope.filtered[i].isSelected = !$scope.filtered[i].isSelected;
}
}
}
}

Angular 1: Last checkbox doesn't stay checked after page refresh

I am saving all data into localStorage. When a checkbox is checked function is called to change items state. It works fine. However after page refresh, last checked item gets unchecked (or if it was unchecked, it gets checked) while others are working just fine. Why does that 1 last action gets ignored after page is refreshed?
Here is codepen: http://codepen.io/kunokdev/pen/vGeEoY?editors=1010
(add few items and click on "click me" for all of them and then refresh page, last action will be ignored)
The view:
<div ng-app="TaskApp" ng-controller="ToDoCtrl">
<form>
<input type="text" ng-model="toDoItem">
<input type="submit" ng-click="addToDoItem()">
</form>
<div>
<ul>
<div
ng-repeat="item in toDoItems |
orderBy: 'createdAt'
track by item.createdAt">
<b>Content:</b> {{item.content}} <br>
<b>Completed?</b> {{item.completed}}
<md-checkbox ng-model="item.completed" ng-click="toggleToDoItem(item.completed)" aria-label="todo-checkbox">
CLICK ME
</md-checkbox>
</div>
</ul>
</div>
</div>
And JS:
var ls = {};
ls.get = function(key) {
return JSON.parse(localStorage.getItem(key));
};
// sets or updates a value for a key
ls.set = function(key, val) {
localStorage.setItem(key, JSON.stringify(val));
};
// returns true if value is set, else false
ls.isSet = function(key) {
var val = ls.get(key);
return ( null === val || 'undefined' === typeof val) ? false : true;
};
// removes a set item
ls.remove = function(key) {
localStorage.removeItem(key)
};
var TaskApp = angular.module('TaskApp', [
'ngMaterial',
'taskAppControllers'
]);
var taskAppControllers = angular.module('taskAppControllers',[]);
taskAppControllers.controller('ToDoCtrl', ['$scope',
function($scope){
//
loadToDoItems = function(){
var data = ls.get("toDoData");
if (data == null) data = [];
return data;
};
//
$scope.toDoItems = loadToDoItems();
//
$scope.addToDoItem = function(){
var toDoItems = $scope.toDoItems;
var newToDoItem = {
"content" : $scope.toDoItem,
"createdAt" : Date.now(),
"completed" : false
}
toDoItems.push(newToDoItem);
ls.set("toDoData", toDoItems);
$scope.toDoItem = "";
};
//
$scope.toggleToDoItem = function(item){
console.log('test');
var toDoItems = $scope.toDoItems;
for (var i = 0; i < toDoItems.length; i++)
if (toDoItems[i].createdAt === item){
if (toDoItems[i].completed == true)
toDoItems[i].completed = false;
else
toDoItems[i].completed = true;
}
ls.set('toDoData', toDoItems);
};
//
}]);
md-checkbox is designed to toggle whatever you put in ng-model so with your code, md-checkbox was toggling the completed property and then you were changing it back again in your $scope.toggleToDoItem function. Why this worked for all the items except the last clicked I am unsure.
So I changed the ng-click to only save the items to local storage and still got the same problem which leads to me believe the problem is caused by using ng-click on an md-checkbox.
<md-checkbox ng-model="item.completed" ng-click="saveToLocalStorage()" aria-label="todo-checkbox">
CLICK ME
</md-checkbox>
$scope.saveToLocalStorage = function() {
ls.set('toDoData', $scope.toDoItems);
};
So I removed the ng-click and set up a watch on $scope.toDoItems.
<md-checkbox ng-model="item.completed" aria-label="todo-checkbox">
$scope.$watch("toDoItems", function() {
ls.set("toDoData", $scope.toDoItems);
}, true);
Codepen
-- EDIT --
Just read the documentation and feel like an idiot, you should use ng-change instead of ng-click. From the docs regarding ng-change:
Angular expression to be executed when input changes due to user interaction with the input element.
That being said, the above about not needing to toggle the completed property yourself still stands.
You are passing item.completed (in the HTML) to your toggleToDoItem(item) method. In your array loop, you then compare the item.Created field to the item.completed parameter. This is comparing a Date type to a Bool. How is that supposed to work?

AngularJS ng-repeat list built dynamically,$index is always zero

I'm using AngularJS ng-repeat to create a list of cities. The list is being built dynamically client-side by the user. The user selects city from a list on the left side of the page, clicks the "Add City" button, and the city is added to a list on the right. The ng-repeat is in both lists, but when a city is added to the list on the right, the $index value for the list items does not update. The $index values are all zero.
Here's the ng-repeat list where the cities are added dynamically:
<div ng-repeat="(key, value) in filter.selectedCities | groupBy:'country'" class="text-left">
<div ng-repeat="(key2, value2) in value | groupBy:'state'" class="text-left">
<span class="label label-default">{{key}} </span> <span class="label label-danger" ng-show="key2.length">{{key2}} </span>
<ul class="nav nav-pills">
<li class="selectedpill" ng-repeat="city in value2">
<div class="pull-left">
<span class="indent8">{{ city.label | characters:24 :true}}</span>
</div>
<div class="pull-right">
<i class="fa fa-heart pointer" ng-click="addToCityFavs($index);" ng-class="{'maroonzheimer' : checkFavCity(city)}" ng-hide="savingCityFav" title="Add To Favorites"></i>
<i class="fa fa-spinner fa-spin" ng-show="savingCityFav"></i>
<i class="fa fa-times fa-lg pointer" ng-click="removeCity(city)" title="Remove City"></i>
</div>
</li>
</ul>
</div>
</div>
Here are the JavaScript functions used to add a city dynamically to the list:
$scope.addSelectedCity = function () {
if ($scope.selectedCity && $scope.selectedCity.value) {
$scope.addCity($scope.selectedCity);
$scope.cityValidationError = false;
} else { $scope.cityValidationError = true; }
$scope.tempSelectedCity = null;
$scope.selectedCity = null;
}
$scope.addCity = function (city) {
if (city && city.city && !$scope.checkCityExists(city)) {
$scope.filter.selectedCities.push(city);
}
}
$scope.addToCityFavs = function (index) {
var city = $scope.filter.selectedCities[index];
var exists = false;
for (var i = 0; i < $scope.filter.favs.CityList.length; i++) {
if ($scope.filter.favs.CityList[i].city.value == city.value) {
exists = true;
}
}
if (!exists) {
$scope.savingCityFav = true;
var fav = { id: 0, active: true, ruser_id: 0, city: city };
Do I need to add something to the HTML or JavaScript functions to get the ng-repeat list to update the $index value after a city has been added to the list?
I'll appreciate any help anyone can give.
Try to add track by $index to ng-repeat.
See this link for an example: https://egghead.io/lessons/angularjs-index-event-log
So it seems that the index is only needed to get the city from the array filter.selectedCities. But you don't need the index, because you can use the city directly:
ng-click="addToCityFavs(city);"
$scope.addToCityFavs = function (city) {
//no longer needed -> var city = $scope.filter.selectedCities[index];
var exists = false;

Categories