I have the following method in a react component:
handleCheckBoxClick() {
var checkbox = document.getElementById("boldCheckbox").checked;
this.setState({ischecked : checkbox});
if(this.state.ischecked) {
this.setState({weight:'bold'});
} else {
this.setState({weight:'normal'});
}
}
but if I change the if statement to:
if(checkbox) {
this.setState({weight:'bold'});
} else {
this.setState({weight:'normal'});
}
it works fine but I can't figure out why the first way doesn't work.
The method setState() is asynchronous, and updates are often batched together. In your case, ischecked is updated together with the weight, so when you set the weight you still refer to the old value.
One solution is to use setState()'s callback that will be called after the state is updated.
Note: to get the checkbox checked state, use the event object e passed to the handler instead of querying the DOM.
handleCheckBoxClick(e){
var checked = e.target.checked;
this.setState({ischecked : checked}, function() {
if(this.state.ischecked){
this.setState({weight:'bold'});
}else{
this.setState({weight:'normal'});
}
});
}
A better solution is to update both properties because you know if the checkbox is checked:
handleCheckBoxClick(e){
var checked = e.target.checked;
this.setState({
ischecked : checked,
weight: checked ? 'bold' : 'normal'
});
}
Thats cause this.setState({ isChecked : checkbox }); has not finished before you ask it in the if statement.
Related
I try to push a specific string called mendatory1 and mendatory2 in the empty array in the state.
class SignUp extends React.Component {
constructor() {
super();
this.states = {
box1: false,
box2: false,
box3: false,
box4: false,
agreeBox: []
};
}
}
- Code in image here:
When the onClick event occurs SingleClicked function activates and it changes the input checked value from false to true which means the checkbox type of input is ticked.
At this time, I want to update the state of an empty array by using setState!
SingleClicked = e => {
console.dir(e.target);
if (e.target.className === "serviceTerm") {
this.setState({
box1: !this.state.box1,
agreeBox: !this.state.box1
? this.state.agreeBox.push("mendatory1")
: this.state.agreeBox
});
}
if (e.target.className === "InfoSecurity") {
this.SecondClicked(e);
}
if (e.target.className === "PromotionSms") {
this.ThirdClicked(e);
}
if (e.target.className === "PromotionMail") {
this.FourthClicked(e);
}
};
- Code in image here:
If I click the other checkbox, the onClick event function 'SingleClicked' will call 'SecondClicked' function which will change the value of the input checked from false to true.
Here, I gave the same logic code, but it makes the error when the onClick event occurs:
Your error stems from the first time you set your state. The main issue is with:
this.state.agreeBox.push("mendatory1");
The .push() does two things, it adds "mendatory1" to your agreeBox array, and then returns the length of the array with the added string. You then proceed to set your agreeBox property equal to this number.
So, when you try and push into agreeBox again, it is actually a number, not an array. To fix this, you can use .concat() instead of .push(). This won't modify agreeBox in-place like .push() does, but instead, it returns a new array which you can set in setState():
this.state.agreeBox.concat("mendatory1");
You are trying to mutate array state directly and it's cosing the error.
You can update the state easily using the spread operator. This can be in your SingleClicked function:
const agreeBox = !this.state.box1 ?
[...this.state.agreeBox, 'mendatory2'] : [...this.state.agreeBox];
this.setState({
box1: !this.state.box1,
agreeBox,
});
I'm rendering some checkboxes dynamically, but currently I'm only able to check the first box, and all other boxes operate the first one. How do I get the boxes to work independently of each other?
This is typescript in React. I've tried changing the interface I'm referencing in the function, thinking I was referencing the wrong thing, but none of those worked.
This is the function:
handleCheckboxClick = (entitlement: IApiEntitlements, checked: boolean): void => {
if (checked === true) {
this.selectedEntitlementIDs.push(entitlement.id);
} else {
const index: number = this.selectedEntitlementIDs.indexOf(entitlement.id);
this.selectedEntitlementIDs.splice(index, 1);
}
//tslint:disable-next-line:prefer-const
let entitlementChecked: IEntitlementChecked = this.state.entitlementChecked;
entitlementChecked[entitlement.id] = checked;
let selectAll: boolean = false;
if (this.selectedEntitlementIDs.length === this.state.responses.apiResponses.apiClients.length) {
selectAll = true;
}
this.setState({
entitlementChecked: entitlementChecked,
selectAll: selectAll
});
console.log(this.selectedEntitlementIDs, 'hi');
console.log(entitlementChecked, 'hello');
}
And this is where it's being called:
return (
<Checkbox
checked={this.state.entitlementChecked[entitlement.id]}
data-ci-key={entitlement.id}
id='api-checkbox'
key={entitlement.id}
labelText={entitlement.label}
onChange={this.handleCheckboxClick}>
</Checkbox>
);
I expect each checkbox to be able to be checked, but currently on the first one works, and all others check or uncheck that first one.
You shouldn't keep an array as a property on the class that keeps track of selected items, this isn't tied to the React lifecycle and could potentially not update the view when you want to. Instead you should just use your map (entitlementChecked) you already have to determine if something is checked or not.
handleCheckboxClick(id) {
this.setState(prevState => ({
entitlementChecked: {
...prevState.entitlementChecked,
[id]: !prevState.entitlementChecked[id]
}
}));
}
When calling the handler method, you can just pass the id through that you need specifically.
onChange={this.handleCheckboxClick.bind(null, item.id)}
Here's a rudimentary example for more detail :)
In ExtJS document, https://docs.sencha.com/extjs/6.5.3/classic/Ext.Component.html,
There is no clear description of both the methods
To answer this question you need to analyze the source code. Here is the method isHidden:
...
isHidden: function() {
return this.hidden;
},
...
And here is the method isVisible
...
isVisible: function(deep) {
var me = this,
hidden;
if (me.hidden || !me.rendered || me.destroyed) {
hidden = true;
} else if (deep) {
hidden = me.isHierarchicallyHidden();
}
return !hidden;
},
...
Besides hidden, isVisible checks whether this component is already rendered, and whether it is in the process of destruction.
Also when isVisible calling with parameter deep, it can check if the element is displayed, for example, in the collapsed panel
I been trying to do checkbox Checkall and UnCheckall using subscribe and i'm partially successful doing that but i am unable to find a fix in couple of scenarios when i am dealing with subscribe .
Using subscribe :
I am here able to checkAll uncheckAll but when i uncheck a child checkbox i.e test1 or test2 i need my parent checkbox name also to be unchecked and in next turn if i check test1 the parent checkbox should be checked i.e keeping condition both child checkboxes are checked .
For fiddle : Click Here
ViewModel :
self.selectedAllBox.subscribe(function (newValue) {
if (newValue == true) {
ko.utils.arrayForEach(self.People(), function (item) {
item.sel(true);
});
} else {
ko.utils.arrayForEach(self.People(), function (item) {
item.sel(false);
});
}
});
The same scenario can be done perfectly in easy way using computed but due some performance issues i need to use subscribe which is best way it wont fire like computed onload .
Reference : Using computed same thing is done perfectly check this Fiddle
I tried to use change event in individual checkbox binding but its a dead end till now.
Any help is appreciated .
Your subscription only applies to edits on the selectedAllBox. To do what you want, you'll need subscriptions on every Person checkbox as well, to check for the right conditions and uncheck the selectedAllBox in the right situations there.
It strikes me as odd that this would be acceptable but using computed() is not. Maybe you should reconsider that part of your answer. I would much rather compute a "isAllSelected" value based on my viewModel state, then bind the selectedAllBox to that.
I solved a similar problem in my own application a couple of years ago using manual subscriptions. Although the computed observable method is concise and easy to understand, it suffers from poor performance when there's a large number of items. Hopefully the code below speaks for itself:
function unsetCount(array, propName) {
// When an item is added to the array, set up a manual subscription
function addItem(item) {
var previousValue = !!item[propName]();
item[propName]._unsetSubscription = item[propName].subscribe(function (latestValue) {
latestValue = !!latestValue;
if (latestValue !== previousValue) {
previousValue = latestValue;
unsetCount(unsetCount() + (latestValue ? -1 : 1));
}
});
return previousValue;
}
// When an item is removed from the array, dispose the subscription
function removeItem(item) {
item[propName]._unsetSubscription.dispose();
return !!item[propName]();
}
// Initialize
var tempUnsetCount = 0;
ko.utils.arrayForEach(array(), function (item) {
if (!addItem(item)) {
tempUnsetCount++;
}
});
var unsetCount = ko.observable(tempUnsetCount);
// Subscribe to array changes
array.subscribe(function (changes) {
var tempUnsetCount = unsetCount();
ko.utils.arrayForEach(changes, function (change) {
if (change.moved === undefined) {
if (change.status === 'added') {
if (!addItem(change.value))
tempUnsetCount++;
} else {
if (!removeItem(change.value))
tempUnsetCount--;
}
}
});
unsetCount(tempUnsetCount);
}, null, 'arrayChange');
return unsetCount;
}
You'll still use a computed observable in your viewmodel for the the select-all value, but now it'll only need to check the unselected count:
self.unselectedPeopleCount = unsetCount(self.People, 'Selected');
self.SelectAll = ko.pureComputed({
read: function() {
return self.People().length && self.unselectedPeopleCount() === 0;
},
write: function(value) {
ko.utils.arrayForEach(self.People(), function (person) {
person.Selected(value);
});
}
}).extend({rateLimit:0});
Example: http://jsfiddle.net/mbest/dwnv81j0/
The computed approach is the right way to do this. You can improve some performance issues by using pureComputed and by using rateLimit. Both require more recent versions of Knockout than the 2.2.1 used in your example (3.2 and 3.1, respectively).
self.SelectAll = ko.pureComputed({
read: function() {
var item = ko.utils.arrayFirst(self.People(), function(item) {
return !item.Selected();
});
return item == null;
},
write: function(value) {
ko.utils.arrayForEach(self.People(), function (person) {
person.Selected(value);
});
}
}).extend({rateLimit:1});
http://jsfiddle.net/mbest/AneL9/98/
i have a simple checkbox
<CheckBox IsChecked="{Binding ForceInheritance}"/>
In Code i have a class with "INotifyPropertyChanged" and the Property
public bool ForceInheritance
{
get { return forceInheritance; }
set
{
if (forceInheritance != value)
{
value = SomeTest();
if (forceInheritance != value)
{
//something is done here.
}
OnPropertyChanged("ForceInheritance");
}
}
}
If SomeTest() returns !value so the underlying data does not have to change the CheckBox still changes its IsChecked state.
For example:
ForceInheritance is false.
CheckBox is clicked.
SomeTest() returns false.
--> The underlying data is not changed
--> I would expect the CheckBox to stay unchecked
The CheckBox gets checked.
What can i do to not change the IsChecked state of the CheckBox when the setter does not actually change the value?
I thought the "OnPropertyChanged("ForceInheritance");" would do the trick but it didn't.
Thank you for your time.
This problems occurs because when you click checkbox it s value is changed. Then binding causes property set. There you manipulate with its value and hope that calling OnPropertyChanged will cause back binding action updating checkbox value. But doesn't work because updating control after updating property may produce infinite updating loop. To prevent this PropertyChanged is ignored.
What can you do is to move some logic of your property setter to binding validation rule. You can read here about validation rules or leave a comment if you need more information or examples.
Hope it helps.
I would expect the CheckBox to stay unchecked
Now, it seems that CheckBox working as you expect. Something must have changed recently because it works differently now.
Previously
When I needed to correct a value in the setter I use Dispatcher.
public bool IsActive
{
get => _isActive;
set
{
if (_isActive != value)
{
if (value)
{
if (!CanActive())
{
Application.Current.Dispatcher.BeginInvoke((Action)(() => IsActive = false));
return;
}
}
_isActive = value;
OnPropertyChanged(nameof(IsActive));
}
}
}
Now
Now, I don't have to change a value back to false. This works even if I remove OnPropertyChanged call, because after the setter is called, the getter is also called. It looks like the CheckBox is now correcting its state.
public bool IsActive
{
get => _isActive;
set
{
if (_isActive != value)
{
if (value)
{
if (!CanActive())
{
return;
}
}
_isActive = value;
OnPropertyChanged(nameof(IsActive));
}
}
}