Blazor, Checkbox keep checked, event after StateHasChanged() method called - checkbox

I have this code :
#foreach (var item in list)
{
<input type="checkbox" #onchange="#(e => HandleCheckChanged(e, item.Name))" checked="#item.IsChecked">
}
private async Task HandleCheckChanged(ChangeEventArgs e, string itemName)
{
// do something ...
StateHasChanged();
}
if I check a checkbox it calls the HandleCheckChanged and the checkbox is checked
But if I change the list items the previews checked checkbox is still checked and is not updated based on the new list items.
for example suppose that I have a list of ordered numbers {1-20} I follow these steps :
1 : list = GetAll().Where(c => c.Id < 10);
2 : I check the first checkbox (number 1)
3 : list = GetAll().Where(c => c.Id >= 10); (list updated and state has changed)
4 : the problem raises here , checkbox 11 is checked ??? but its value is false

This happens because the renderer just sees the 'same' list of checkboxes and sees no reason to send an update.
A simple solution using #key to express that they are different items:
#foreach (var item in list)
{
<input #key="item" type="checkbox" #onchange="#(e => HandleCheckChanged(e, item.Name))" checked="#item.IsChecked">
}
You do not have to call StatehasChanged() but it won't do any harm either.

I had the same problem, what #Henk said is correct and there is also other solution: You can wrap your HTML input with <EditForm Model='YourModel'>. because whenever EdtitForm.Model changes, EditForm.OnParametersSet is excuted and OnParametersSet will call StateHasChanged to rerender the existing component.
Blazor-university has good explanation.

Related

Is this a React prevState issue and how can I solve it?

This is kind of complicated to explain. I'm trying to make a toggle function that adds and deletes items on a different page with useContext. Everything adds and deletes fine until I tab to the other page, and that's where the error begins. Once I do that the function ignores what's in the array and will duplicate items in the array. What's odd about it is if I console.log or manually check the new item with the current array items it shows me everything. For example in order to add the new item to the array it checks if the index of new item is -1. If it is it will add the item and if not it will delete the item. However once I leave the page it doesn't see the item anymore and adds it anyway. If I console.log the item name and new item name, I can see both, and if I use === to check it also works fine until I switch tabs and then even though it's still console logging both names somehow it's still adding the item and ignoring that it already contains the item.
The code directory in my sandbox is src/Helpers/MyPicsContext. Here is the link to my sandbox codesandbox
The tabs on the website are Picks and the search page which you can access on Picks before any items are added or by clicking the magnifying glass in the top right of page.
And here is the actual code for the context page.
export const MyPicksContextProvider = props => {
const [picksList, setPicksList] = useState(
JSON.parse(localStorage.getItem('picksList'))
||
[]
)
//console.log(picksList)
useEffect(() => {
localStorage.setItem("picksList", JSON.stringify(picksList));
}, [picksList]);
const deleteCoin = coin => {
if (picksList.length === 1) {
setPicksList([]);
} else {
setPicksList(picksList.filter(list => {
console.log(list)
//console.log(coin)
return list !== coin;
}))
}
console.log('deleted');
console.log(picksList)
}
const toggleCoin = (coin) => {
if (picksList.length === 0) {
setPicksList([...picksList, coin]);
} else {
if (picksList.indexOf(coin) === -1 ) {
setPicksList([...picksList, coin]);
console.log('added 1')
} else {
deleteCoin(coin)
}
}
}
Perhaps I just don't understand useState and prevState, but I can't seem to find any examples that apply to what I'm trying to do here. It makes total sense in creating a counter or something simple like that.
The issue is that .indexOf checks for referential equality of items, same as using ===. This means that const a = {id: 'x'}; const b = a; console.log(a === b); will output true, but const a = {id: 'x'}; const b = {id: 'x'}; console.log(a === b); will output false.
In your situation, upon changing pages / refreshing, the state is reset and loaded from local storage. However, this creates new objects which are not referentially equal. Instead of using .indexOf you want to use .find, something like array.find((element) => element.id === newItem.id) to find the index of the item. You could also do your own deep equality check (confirming every field matches), but I suspect the ID alone is sufficient.
In fact, I would also recommend only keeping the array of "picks" as an array of string ID's. Then you can lookup the full data from your table for each of these ID's. Otherwise the current price will be stored in localStorage, and could be out of date.

Ag-grid isRowSelectable conditional not activating

We have an Ag-Grid table with row selection via the built in checkbox functionality. The check boxes are conditionally displaying via the isRowSelectable options:
isRowSelectable: function (rowNode) {
return rowNode.data ? rowNode.data.published === false : true;
}
The published column is being updated as part of a modal called from another column. A redrawRows is being called when the modal is closed:
modal.result.then(() => {
const row = params.api.getDisplayedRowAtIndex(params.rowIndex as number);
//this.gridApi?.redrawRows([row] as any);
this.gridApi?.redrawRows();
});
The display values in the row are being updated when the modal is closed, however, the checkbox is not appearing when the published value is set to false. If I hang a breakpoint in Dev Tools the isRowSelectable code does not appear to be hit.
Any suggestions would be much appreciated.
Cheers,
-John
Can you try this and see if it works?
let itemsToUpdate = [];
let data = rowNode.data;
// modify data fields
// Ex: data.field1 = "new value";
itemsToUpdate.push(data);
this.gridApi.applyTransaction({update: itemsToUpdate});
https://www.ag-grid.com/javascript-data-grid/data-update-transactions/
Use refreshCells
this.gridApi?.refreshCells({force:true});
https://www.ag-grid.com/react-data-grid/view-refresh/#redraw-rows

Bind comma separated list to checkboxes in Angular 9

In my reactive form I have an array that I bind the checkbox list to using following syntax:
structure of array is {id:number, name:string}
TS
ngOnInit(): void {
this.initFCForm();
this.addCheckboxes();
}
initFCForm(): void {
this.fcForm = this.formBuilder.group({
frequency : new FormControl('', [Validators.required]),
rules: new FormArray([])
});
}
get rulesFormArray() {
return this.fcForm.controls.rules as FormArray;
}
private addCheckboxes() {
this.businessrules.forEach(() => this.rulesFormArray.push(new FormControl(false)));
}
HTML
<label class="col-md-7" formArrayName="rules"
*ngFor="let rule of rulesFormArray.controls; let i = index">
<input type="checkbox" [formControlName]="i">
{{businessrules[i].name}}
</label>
Now I need to populate it when the page loads to have the first and the third option selected. I have tried the following code but it only select first two options.
TS
this.fc.rules.patchValue([1,3])
When you do this.fc.rules.patchValue([1,3]), it will set value 1 to first control of the rules FormArray and 3 to the second control of the rules FormArray but nothing to third.
Checkbox formcontrol expects a boolean value (true or false) and here, when setting 1 and 3 using patch, values are automatically 'converted' to true because these are truthy values.
If you want to have the first and third value populated, try this :
this.fc.rules.patchValue([true,null, true])

How to target an element when navigating to another page (partial)

I have a checkbox that I'd like to set the indeterminate state to based on the states of other checkboxes. When I'm on the page that the checkboxes are all in, it updates as expected (i.e. the checkbox is found). But when I navigate to that from another page, my method does not find the checkbox (i.e. returns null).
When I debug in Chrome devtools, I notice
let checkBoxWithIndeterminateState;
let checkbox = false;
fireWhenCheckBoxChanged() {
// returns null when navigating from another page but not when on its own page
checkBoxWithIndeterminateState = document.getElementById('checkBoxWithIndeterminateState')
checkBoxWithIndeterminateState.indeterminate = true
}
Template:
<input type="checkbox" id="checkBoxWithIndeterminateState" data-ng-model="checkbox">
How do I wait until the new template has loaded before my method tries to find the checkbox? I've read some suggestions to use this._$scope.$on('$viewContentLoaded'... but this doesn't work.
Thanks!
What about adding an ng-init directive to your target checkbox and do your logic in it, this way you are sure the element is there, here is a suggestion:
<input type="checkbox" ng-init="initTragetCheckbox()">
In your controller
$scope.initTragetCheckbox = function () {
// your code to execute for other checkboxes
var checkbox1 = document.getElementById("checkbox1");
var checkbox2 = document.getElementById("checkbox2");
....
}

Remove item from list after filtering

I have the following issue:
I've create a list that allow the user to delete an item from list, as following:
When user click on trash icon, the item is removed normally.
The problem is when the user uses the filter on top.
In that case, if I delete the number 6565 (index 4 in original list, 1 on filtered list), the item deleted is on index 1 on original list, resulting on delete the register with number #564456
This is my delete call on click:
$scope.deleteOwn = function (uuid) {
console.log(uuid);
var coupon = $scope.ownsCoupons[uuid];
Coupon.delete({'id' : coupon.uuid}, function () {
$scope.ownsCoupons.splice(uuid, 1);
});
}
And this is my html template:
<td></i></td>
I also try to use the code: $scope.ownsCoupons.splice(coupon, 1);without success.
Does anyone know how to fix that?
I've coded using the following reference: AngularJS How to remove an Item from scope
[EDIT]
I've created a Plunker to this: http://plnkr.co/edit/Fhxp6uZyTJCY05CAQ7yA?p=preview
As mentioned by #pkozlowski.opensource, you can't depend on $index to identify an item in an array in this way. I would make the following changes:
HTML:
<td><a ng-click="deleteWish(coupon)"><i class="icon-trash"></i></a></td>
JS:
$scope.deleteWish = function (coupon) {
var index = $scope.coupons.indexOf(coupon);
if (index != -1) {
$scope.coupons.splice(index, 1);
}
}
Here is a working Plunker: http://plnkr.co/edit/b0b2cYGsM5wtw8hIrQB5?p=preview

Resources