Angular 6 check box doesn't change state based on ngModel - checkbox

In an array say I have five mantras out of it users have to choose maximum of three. This is my implementation
<input class='f-z-14' type="checkbox" [(ngModel)]='mantrasSelected[i]' [checked]='mantrasSelected[i] === true' (change)='mantraChecked(i, mantrasSelected[i], mantra)'>
And in my ts
mantraChecked(i, event, mantra) {
if (event) {
if (this.selectedCount < 3) {
this.selectedCount += 1;
this.mantrasSelected[i] = true;
} else {
this.mantrasSelected[i] = false;
}
} else {
if (this.selectedCount > 0 && this.selectedCount <= 3) {
this.selectedCount -= 1;
this.mantrasSelected[i] = false;
}
}
}
So If i choose first three means in this.mantrasSelected I will be having [true, true, true, false, false]. So If user checked fourth one means I'm checking in my mantraChecked() if count is greater than 3 means I dont want to check it. after I click 4th mantra I'm changing mantrasSelected to [true, true, true, false, false] but view shows it is checked. How can show to the user that they have checked only three box? How two biding work with checkbox? Thank you.

Related

How can I have only one checkbox checked out of two?

I've got two checkboxes and I need to have only one of them true at the time.
So if checkbox1 is true then checkbox2 should be false.
My current code works only when I play around with first checkbox but the second one doesn't behave correctly.
Here are my checkboxes:
<Checkbox
checked={checkbox1}
onChange={onChange}
/>
<Checkbox
checked={checkbox2}
onChange={onChange}
/>
My CheckBox component:
<input
type="checkbox"
className="filled-in"
checked={this.state.checked}
data-cy={this.props.cyCheckbox}
/>
And my parent component where I am trying to manipulate state to set CheckBox checked true or false:
<Access
checkbox1={this.state.input.checkbox1}
checkbox2={this.state.input.checkbox2}
onChange={this.updateInput}
/>;
state = {
input:{
checkbox1: false,
checkbox2: false,
}
}
updateInput = (key, value) => {
let { input } = this.state;
input[key] = value;
this.setState({ input });
window.input = input;
//this is where I try to set another Checkbox false if first one is true.
if (input.checkbox1) {
input.checkbox2= false;
} else if (input.checkbox2) {
input.checkbox1= false;
} else {
input.checkbox2= true;
input.checkbox1= true;
}
}
I can't be sure because your examples are incomplete, but I think your issue is that your checkbox code can never reach the else if clause unless the first checkbox is already unchecked. Make your conditionals based on key and value and you'll be fine. Something like:
if (key === 'checkbox1' && value) {
input.checkbox1 = true;
input.checkbox2 = false;
} else if (key === 'checkbox2' && value) {
input.checkbox1 = false;
input.checkbox2 = true;
}

I want to set checkbox to true based on a condition in ag-grid

I have a button which basically imports some data. This imported data needs to be compared with the data inside already loaded ag-grid and if there is a match, set the cehckbox of that particlar row node to true.
This is the button which checks for the condition:
enableCheck() {
alert('works');
if (this.DataService.isNotBlank(this.rowDataImport)) {
for (let i = 0; i < this.rowDataImport.length; i++) {
if (this.DataService.isNotBlank(this.rowData)) {
for (let j = 0; j < this.rowData.length; j++) {
if (this.DataService.isNotBlank(this.rowData[j].calDate)) {
for (const calDates of this.rowData[j].calDate) {
if (
this.rowDataImport[i].isin === calDates.isin ||
this.rowDataImport[i].portfolioName === calDates.portfolioName ||
this.rowDataImport[i].valuationDate === calDates.valuationDate
)
{
// alert('true')
this.checkValue = true;
} else {
this.checkValue = false;
}
}
}
}
}
}
}
}
The this.checkValue is a flag which will be true if match is found.
public gridColumnDefs = [
{
headerName: 'Portfolio Name',
field: 'portfolioName',
cellRenderer: 'agGroupCellRenderer',
headerCheckboxSelection: true,
headerCheckboxSelectionFilteredOnly: true,
checkboxSelection: true,
pinned: 'left',
filter: true,
cellRendererParams:(params) => {
console.log(params);
if (this.checkValue) {
params.node.selected = true;
}
}
},
]
here I used cellRendererParams. But this will only for on load I guess. What to do if I want to update the ag-grid row from a value outside i.e. from import check as given above?
First of all, you should add id for each row in defaultColDef
this.defaultColDef = {
getRowNodeId: data => data.id,
};
Then you can find this id and set the checkbox to true.
Also, you can find separate field by name.
It is really easy, you should use the next combination
selectRow() {
this.gridApi.forEachNode(node => {
if (node.id == 1 || node.id == 2 || node.data.country == 'Australia') {
node.setSelected(true);
}
});
}
Working example:
https://plnkr.co/edit/ijgg6bXVleOAmNL8
In this example when we click on the button - we set the checkbox to true for two rows with id 1 and 2 and for each field that has country 'Australia'
And in your case, you are using incorrect configuration.
You should you cellRenderer
cellRenderer: params => {
if(params.value === 'Ireland') {
params.node.setSelected(true)
}
return params.value
},
One more example: https://plnkr.co/edit/PcBklnJVT2NsNbm6?preview
I manpulated a bit and did the below which worked like a charm.
this.gridApi.forEachNode(node => {
if (node.data.isin === this.rowDataImport[i].isin &&
node.data.portfolioName === this.rowDataImport[i].portfolioName &&
node.data.valuationDate === this.rowDataImport[i].valuationDate
) {
node.setSelected(true);
}

How do I validate a checkout form in React?

I am trying to implement a checkout form in React. The form has 4 fields in all: Name, CC Number, CC expiration and CVV. I am using a library that validates each field on unfocus. The validation is triggered by the validationCallback method which takes 3 arguments: field, status, and message. I'd like to key off of the status for each input and only allow submit once each status === true. Here is my code.
constructor(props) {
super(props);
this.state = {
nameOnCard: '',
errorMessage: '',
showLoaderForPayment: '',
collectJs: null,
token: null,
isPaymentRequestCalled: false,
showErrorModal: false,
paymentErrorText: '',
disabled: true,
};
}
I have a disabled property in my state which I'm initially setting to true.
validationCallback: (field, status, message) => {
if (status) {
this.setState({ errorMessage: '' });
} else {
let fieldName = '';
switch (field) {
case 'ccnumber':
fieldName = 'Credit Card';
break;
case 'ccexp':
fieldName = 'Expire Date';
break;
case 'cvv':
fieldName = 'Security Code';
break;
default:
fieldName = 'A';
}
if (message === 'Field is empty') {
this.setState({ errorMessage: `${fieldName} ${message}` });
} else {
this.setState({ errorMessage: `${message}` });
}
}
},
In the above method, I'd like to set disabled to false if each of the field's status===true... Below is the button which I'm setting to be the value of this.state.disabled.
<button
className="continueBtn disabled"
disabled={this.state.disabled}
onClick={this.handleCardSubmit}
>
<span className="fa fa-lock" />
Pay $
{selectedPayment.amount}
</button>
I hope this is enough of the code to help with the issue. I can provide more of the file if need be.
From what i understand, you want to set the button to NOT DISABLED if all the fields are filled properly, i.e. all status are true.
What you can do is maintain a boolean array for each field and update the status in that array, i.e. initialize an array of length = no. of fields (in your case 3) and set all values as false. False depicts that the field hasn't been validated.
this.state = {
statusArray = [false, false, false] // For as many fields
}
Then in validationCallback, set the index as true or false for that field i.e. if the 2nd field status is returned true by your validation library, set statusArray as [false, true, false].
The form will only be validated if all 3 of the values become true. So you can iterate over the array and check if array has all 3 values as true. or you can use the logical AND operator which returns true only if all values are true(the approach which i use below).
For the button,
<button disabled={this.checkDisable()}>
checkDisable = () => {
let temp = this.state.statusArray;
let answer = true;
for(int i=0;i<temp.length;i++)
answer = answer && temp[i];
return answer; // Only returns true if all 3 values are true
}
I hope you get it now.
You need to check 2 things, has the form been touched and are there any errors. I don't know what library you are using but most likely it has a property touched in it, if not add an onFocus to each input field and a touched property in your state. You don't really need a disabled property in your state since its a computed value. Just check on every render if the form has been touched and if there are any errors.
state = {
...,
touched: false,
...
}
handleFocus = () => this.setState({touched: true})
render(){
const disabled = !!(this.state.touched && this.state.errorCode)
return(
...
<input onFocus={this.handleFocus} ... />
...
<button disabled={disabled}
)
}
EDIT:
state = {
...
validInputs: []
}
validationCallback: (field, status, message) => {
if (status) {
this.setState((state) => ({ errorMessage: '', validInputs: [... new Set([...state.validInputs, field])] }));
} else {
...
render(){
const disabled = this.state.length < inputs.length // the number of the input fields
return(
...
<button disabled={disabled} >
...
)

Validate formly angularjs fields are not the same

I have dynamically added fields on click.
addNewFiled() {
let parent = this;
this.scope.fields.push({
key: 'field-'+parent.scope.fields.length,
type: 'horizontalInput',
templateOptions: {
placeholder :'Enter Field',
label: 'Filed',
required: false
},
validators: {
fieldFormat: function($viewValue, $modelValue, scope) {
let value = $viewValue;
if(value.length != 12){
scope.to.message = "Field should be 12 characters";
return false;
}
return true;
}
}
});
}
What I need is to validate the the value entered is not in another field in the validator, I tried looping through the model but its not efficient, any help is appreciated.
I have encountered this case once, I solved the issue using 2 maps
Basically, you will have 2 maps, one which will contain the index of the field mapped to the value of it, the second map will contain the value of the field mapped to the number of the repetitions of that value
In your validator, you decrement the number of repetitions for the previous value ( after done with other validations) and increase the number of repetitions of the new value and check if it's more than 1 then it's repeated.
In your Dialog define the two maps
private valuesMap: any = [];
private keysArray:any = [];
In your field, you inject a controller to save the index of the current field
controller: function ($scope) {
$scope.index = parent.scope.fields.length-1;
parent.keysArray[$scope.index] = $scope.index;
},
then in your validator
if(value) {
if(angular.isDefined(parent.valuesMap[parent.keysArray[scope.index]])) {
parent.valuesMap[parent.keysArray[scope.index]]= parent.valuesMap[parent.keysArray[scope.index]] -1;
}
parent.keysArray[scope.index] = value;
if(angular.isDefined(parent.valuesMap[value]) && parent.valuesMap[value] > 0) {
parent.valuesMap[value] = parent.valuesMap[value]+1;
scope.to.message = "Value is already entered";
return false;
}
parent.valuesMap[value] = 1;
}
Hope this works with your scenario
You don't need a validator on this, there is already default validation for field length through the minlength and maxlength properties in the templateOptions.
Simply do this:
templateOptions: {
placeholder :'Enter Field',
label: 'Filed',
required: false,
minlength: 12,
maxlength: 12
},

Multiple Boolean Filter in Angular?

I have a settings node in a user with three boolean values and a three exact same values in an item. I want to filter the list of items based on the user setting on which to display.
I searched high and low and nothing seems to be working:
item:
{"cash_back": true,
"miles": false,
"points": false
}
user:
settings: {"cash_back": true,
"miles": false,
"points": true
}
html:
ng-repeat="item in items | filter: {cash_back: user.settings.cash_back, points: user.settings.points, miles: user.settings.miles} | limitTo: curLimit"
...but it does work if I have one
filter: {cash_back: user.settings.cash_back}
obviously just for that setting...
can anyone help on this?
I would like to see all that are true (so if points and miles are true see all points and miles items even if there is a miles item with a points value of false)
I figured it out on my own. (One thing to note is in my user node I keep track of a list of item ids - the reason for the initial for loop) I set up a filtering function and compared the two boolean values ( I separated each into an if statement for debugging and tracking results - this can be cleaned up later.)
function:
$scope.settingsFilter = function(card){
var temp = {};
for(c in $scope.cards){
if ($scope.cards[c].id === card){
temp = $scope.cards[c];
break;
}
}
if(($scope.user.settings.cash_back == true && temp.cash_back == true)){
return true;
}
if(($scope.user.settings.points == true && temp.points == true)){
return true;
}
if(($scope.user.settings.miles == true && temp.miles == true)){
return true;
}else{
return false;
}
return false;
}
html:
ng-repeat="card in userCards | filter:settingsFilter track by $index"

Resources