Angular - trouble with properties updating onchange from checkbox - javascript

I am attempting to use a checkbox in Angular in order to toggle the contents of an array. I am using property booleans to determine if 3 different boxes are checked and adjust the contents of the array accordingly. The array defaults to containing all 3 subarrays via declaring this.allNouns in ngOnInit(). When I click the first checkbox, I can see in the console that it is running the associated onchange function and setting the associated boolean to its opposite, but the interpolated string in the view does not change. When I click it again, it will change the view but now everything is one step behind. Also when it hits the console logs, it appears to load the prior length and not the length of the newly established this.allNouns. Below are the html and ts snippets associated with it. This is the first of the three checkboxes, but I plan to apply it to all when smoothed out.
TS
preloadedNouns: Boolean = true;
toggleData() {
if(this.preloadedNouns==false){
this.allNouns=this.nouns.concat(this.onlyUserNouns);
console.log('hitting first condition in toggledata', this.allNouns.length);
}
else {
this.allNouns=this.nouns.concat(this.stockNouns).concat(this.onlyUserNouns);
console.log('hitting second condition in toggledata', this.allNouns.length);
}
}
HTML
<div name="allNouns" [(ngModel)]="allNouns" [ngModelOptions]="{standalone: true}" ngDefaultControl>{{allNouns.length}}</div>
<div class="toggleDiv">
<label>Preloaded Nouns</label>
<input id="preloadedNounsCheck"
type="checkbox"
[checked]="preloadedNouns"
(change)="toggleData(); preloadedNouns = !preloadedNouns">
</div>

i don't clear about your nouns logic.
<div class="toggleDiv">
<label>Preloaded Nouns</label>
<input id="preloadedNounsCheck"
type="checkbox"
[checked]="preloadedNouns"
(change)="preloadedNouns = !preloadedNouns;toggleData()">
</div>
but i hope based on my understanding above code will help to you.
just reorder the function call in (change).

Related

How to two-way bind a checkbox using Angular?

I currently have an accordion with a bunch of options in the form of checkboxes. The user can select the checkboxes as expected however, I want to already have some of those checkboxes checked depending on certain conditions. The issue is sometimes that is determined after the page has loaded. Below are simplified examples to demonstrate my code (my code is for work and would not be able to share due to confidentiality issues).
My HTML looks like this:
<div class="row">
<div "ngFor="let value of filteredPeople" >
<div>
<input type="checkbox" (click)="selectPeople(value)" [checked]="checkedPeople.get(value)">
{{ value }}
</div>
</div>
</div
My Javascript:
public checkPeople() {
this.selectedPeople.forEach(element => {
this.checkedPeople.set(element, true);
});
}
To explain some variables and methods:
Variables:
filterPeople - a string array of all possible people
checkedPeople - a map with a KVP of string (the people) and boolean (whether or not their checkbox is checked)
selectedPeople - a string array of people whose checkboxes I want already checked
Methods:
selectPeople - checks the corresponding checkbox when user clicks on it
checkPeople - a method called when I want the specific checkboxes checked (these specific checkboxes change based on other factors so I cannot hard code it)
For some reason my checkPeople method does not seem to select the expected checkboxes, I am not sure why but I have a feeling it is to do with the fact that I have used "[checked]" in the HTML and that it should be something else. Can someone clarify the issue for me and help me identify a fix? (Note: as the title suggests, I am using Angular)
Edit:
I have just debugged my code and added breakpoints, the checkedPeople map has the correct mapping of people to true for each of the elements in selectedPeople which shows that the checkPeople method is working as expected. Therefore the issue must be with the [checked] attribute if I'm not mistaken. I cannot see why it wouldn't work though.
You should use [ngModel]="checkedPeople.get(value)"
instead of [checked]="checkedPeople.get(value)"
to initialize the checkbox and
(change)="checkUncheckPeople(value, $event)"
to update the value when you check or uncheck it, where
checkUncheckPeople(value, e) {
this.checkedPeople.set(value, e.target.value);
}
So, in conclusion, your HTML input element will be:
<input
type="checkbox"
[ngModel]="checkedPeople.get(value)"
(change)="checkUncheckPeople(value, $event)"
/>
If you choose to use an object instead of a map then you can also directly use
[(ngModel)]="checkedPeople[value]"
to two-way bind the variable

Mystery: checkboxes correct array line is not being executed. Where's the bug?

Working with data in Google Sheets.
I might have done something wrong with checkboxes.
I have this single checkbox in my html file (from MaterializeCss.com):
<label id = "check" >
<input type="checkbox" class="filled-in" checked="checked" />
<span></span>
</label>
Then on window.onload() I clone it dynamically in a loop - as many times as many names I get from script properties:
//cloning the original checkbox
var checkbox = document.getElementById("check").cloneNode(true)
//setting unique IDs
checkbox.setAttribute("id", "check" + i);
//appending it to the collection element
collection.appendChild(checkbox);
I clone the <label> tag and state "true" which should clone all parts that are inside including class I guess.
All this data goes to a sidebar and work fine.
Then I have a button which I assign my function to:
document.getElementById("btn").addEventListener("click", requestCheckedCalendars)
Then I am trying to establish client-server communication and I can't get the data stably - it showed couple of times but mostly not, I don't even get what was changed.
This simple line is the first line of a function and it is not working, not being logged, recorded to a cell etc.
var allCheckboxes = document.getElementsByClassName('filled-in');
What do I do wrong here?

Updating array model deselects iterated <Input> elements

Trying to implement a add/remove sub-item entry; increment/decrement buttons add slots into the array and input fields are added/removed automatically:
<div *ngFor="let item of itemsInNewOrder; let i = index">
<input [(ngModel)]="itemsInNewOrder[i]" xtype="text" name="order" title="order" />
</div>
This is working functionally, but every time a letter is entered into the input, the element is deselected and must be clicked again to enter yet one more letter. How can I solve this?
http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html
The link above explains change detection in Angular 2. It also describes some other change detection strategies that you might find useful.
Basically since you did a 2 way binding [(ngModel)]="itemsInNewOrder[i]" to the itemsInNewOrder array, Angular is trying to be helpful and update the view for you.
Each keystroke updates your model (itemsInNewOrder)
Angular detects the update
Angular renders the view again with new form controls
Again, these are new form controls so the user hasn't focused one yet. This is why you have to click into the control after every entered character.
You can fix this by removing your 2 way binding and instead listening to the blur event for each input or implementing something described in the link above.
Blur event listening example:
<input (blur)="blurHandler(item,i)" type="text" name="order" title="order" />
Component
blurHandler(val:string,ndx:number) {
this.itemsInNewOrder[ndx] = val;
}
This might help too
Angular 2 change event - model changes
You can also wrap the values into objects. I don't know why you don't lose focus if you do it like this.
Component:
itemsInNewOrder = [{value: 'Soda'}, {value: 'Burger'}, {value: 'Fries'}];
template:
<div *ngFor="let item of itemsInNewOrder; let i = index">
<input
type="text"
name="order"
title="order"
[(ngModel)]="itemsInNewOrder[i].value"/>
</div>
The simplest answer to this was that I needed to learn and implement Angular 2's reactive forms library, which is part of Angular 2. Using this, I ran into none of difficulties I was experiencing.

Angular 2 change template input values on click

Working with a set of column components with id input in my template:
<div class="panel-body" *ngIf="columns">
<div class="col-md-4">
<column [id]=columns[current_left_column].Id></column>
</div>
<div class="col-md-4">
<column [id]=columns[current_middle_column].Id></column>
</div>
<div class="col-md-4">
<column [id]=columns[current_right_column].Id></column>
</div>
</div>
I have buttons that increment and decrement the columns by changing the values of current_left_column, current_middle_column, and current_right_column. When I click those buttons I log the values of the three column id's and they're representative of where they should be however the template doesn't reload.
I did attempt to use ApplicationRef.tick() to trigger change detection, but the fact that it didn't change makes me think it's a binding issue, but I'm thus far unable to find anything that matches my case as 2-way binding seems to necessitate a more traditional input element and it currently is one way bound.
I think you need to change the name of the input. I have seen it several times that it collides with the id property every HTML element has.
I really appreciate the comments and info given, it helped me get a better handle on what I was actually trying to do.
I ended up taking an example from a different answer and using ngModel with an input to show the individual column id's. Then since I could prove my id's were switching but it wasn't updating even with the tick() I realized I was working on one component level too high and had to be initiating the change from the Column level. So I took the ngOnInit method that populates the columns with data and put it in a ngOnChanges() method, cleared the OnInit() because it was doubling up, and it started moving fine.
Then I had to deal with the information from the columns being added on top of the prior column, but that was relatively minor.
ngOnChanges(){
this._columnService.GetSingleColumn(this.id).subscribe(column => { this.columnData = column; });
this._cardService.GetCardsByColumnId(this.id).subscribe(cards => { this._cardColumnService.LoadCards(cards); console.log(this.cards); });
if (this.columnData == undefined) {
this.LoadDummyData();
}
}

Knockout - Checking checkboxes according to observableArray

So I have a list of schedules that I need to display for a user and show which schedules he or she is currently on and give them the possibility to jump on and off said schedules.
My viewmodel looks like this
self = this;
self.shifts = ko.observableArray();
self.selectedShifts = ko.observableArray();
//I populate self.shifts here with a WEB API call
//Run through each shift and check if current user is on it and set checked / not checked value for checkbox
ko.utils.arrayForEach(self.shifts(), function(shift) {
//Clear array
self.usersOnShift([]);
//Populate array with all users on the shift
self.usersOnShift = ko.observableArray(WEB API CALL HERE);
var userInShift = ko.utils.arrayFirst(self.usersOnShift(), function(user) {
if (selectedUserId == user.ID) {
return true;
}
});
if (userInShift) {
self.selectedShifts.push(shift.ID);
}
});
ko.applyBindings(self);
My HTML looks like this
<div class="simple_overlay" id="shiftOverlay">
<div class="details">
<div data-bind="foreach: shifts">
<div><span class="staff-initials" data-bind="text:wardName"> </span><input type="checkbox" data-bind="value: ID, checked: $root.selectedShifts"/> </div>
</div>
<div>
Connect
Close
</div>
</div>
</div>
I can see that the value of the checkboxes are set correctly to the ID of the corresponding shifts. However a shift that I know the user in question is on is not checked and I know that the selectedShifts observableArray contains the value.
Somehow the "checked: $root.selectedShifts" call / check is not working but I know that it contains the right value. What am I doing wrong?
The problem is that your value is an integer, but when bound to the checkbox element, it becomes a string. When the checked binding tries to find the value in the array, it doesn't find a match because it uses strict equality for comparison and (2 === "2") is false.
The simplest way to work around this problem is to convert your values to string when you add them to the array:
self.selectedShifts.push("" + shift.ID);
Of course this means that your model has to change, and that might not be a great solution. I came up with a custom binding, checkedInArray that replaces checked and supports any type of value. You can learn about it, see it in action, and use it like this:
<input type="checkbox" data-bind="checkedInArray: {value: ID, array: $root.selectedShifts }" />
In Knockout 2.3.0 (which is still in development) there will be a new binding, checkedValue, that will allow you use any type of value with the checked binding. Using that version, you could update your HTML to use checkedValue:
<input type="checkbox" data-bind="checkedValue: ID, checked: $root.selectedShifts"/>
Is shift.ID an observable property? If it is, then you need to add it to the array like this:
self.selectedShifts.push(shift.ID());
Otherwise you're just adding the whole observable to the array, and not the value.

Categories

Resources