Angular 14: Nested FormGroup not resetting validators - javascript

I am trying to implement a nested FormGroup. So far, I am getting error while trying to reset the form. The form structure looks like this:
form: UntypedFormGroup;
this.form = this.fb.nonNullable.group({
f1: [''], f2: this.fb.nonNullable.array([], Validators.required),
f3: this.fb.nonNullable.array([]), f4: this.fb.nonNullable.array([]),
f5: this.fb.nonNullable.group({ id: ['', Validators.required] })
});
I noticed that required validator for the id field is not getting reset while resetting the overall form.
I have a similar project built on Angular 14 but there isn't any nested formgroup. But from my experience and research, I wrote this.form.reset() which is resetting the form with initial value but keeping the valid state of id field as false.
How this can be solved? Also the array fields are getting initialized with NULL even though I have set the formgroup & formarray to not allow NULL.
Current Logic:
this.form.reset();
console.log("Form after reset:", this.form.value);
console.log("Id field valid? ", this.form.get('f5').valid);
Current Output:
Output 1: Form after reset: {"f1":"","f2":[null],"f3":[],"f4":[],"f5":{"id":""}}
Output 2: Id field valid? false
NOTE: Here I only set value of f2 field so it is only having NULL now.
Expected Output:
Output 1: Form after reset: {"f1":"","f2":[],"f3":[],"f4":[],"f5":{"id":""}}
For the second output, the form should just reset to it's initial state as it is marking the mat-form-field associated with the id field as red.

Per documentation, control's value will be reset to initial value, if there is any, and if nonNullable is set to true. nonNullable does not mean that value will never be null, falsy or invalid after reset() call.
In Validator.required() documentation you read that empty field value will trigger invalid state. There is example for zero length string:
const control = new FormControl('', Validators.required);
console.log(control.errors); // {required: true}

Related

Yup conditional validation depending on if another field is valid?

I'm working on a project with a long registration form made with MUI, react-hook-forms and yup. There would be one field rendered at a time with a "Next" button to change the displayed input field to a next one (i.e. go from email field to name field). Right now it doesn't let me submit the email field and display the name field because it wants to validate both since there is no conditional validation.
I have a following schema:
const schema = yup.object().shape({
email: yup.string().email().required(),
firstName: yup.string().required("Please enter your first name"),
});
From what I've read online I understand that I need to use the .when method for the firstName field, except I don't know how to make it check based on if the email field is valid. I have tried the following:
const schema = yup.object().shape({
email: yup.string().email().required(),
firstName: yup.string().when("email", {
is: (value) => value !== undefined,
then: yup.string().required("Please enter your first name"),
otherwise: yup.string().notRequired(),
}),
});
But the value is defined onChange so it doesn't work.
I figured it out. I have divided the main schema into an emailSchema and a nameSchema, and then I used the concat method based on the step of the registration (1 being the step with the frist name field).
const schema = step === 1 ? emailSchema.concat(nameSchema) : emailSchema

is there a way to detect the valueChanges for formArray which has been dynamically binded from array of objects in angular8

is there a way to detect if there are any valueChanges for the toggle buttons made to form array which has been binded from dynamic array of objects.
Here, i am getting this.agentDetailsList and this.detailsToggle from the api, if i am in edit mode, this.agentDetails array i am binding to the formArray by using this.detailsToggle as this array has been used to bind in the html for looping. Here even though i made changes to the formarray it is not reflecting.
Basically my requirement is that, if there is any change in this formarray toggles before clicking on save, then API must be called, if there are no changes then api request shouldnt be made. Here i am not able to use valueChanges or statusChanges as both are not working.
Help appreciated.
Thanks in advance
TS:
i have used reactive forms, and the values are getting assigned to the html and the reactive forms is due to the array of object. So, what ever action i perform there must be used for comparing objects with the existing object. So i have used one array of object comparison method. But here the previous value is also getting binded to the new value which has been assigned to the form.
I want the newly edited value and the old value as seperate so that i can compare if the object proerty values are diffrent then i can enable for save.
DEMO: DEMO
TS:
saveDetails() {
this.objectsAreSame(this.agentDetailsList, this.detailsToggle)
console.log(this.agentDetailsList);
console.log(this.detailsToggle);
console.log(this.objectsAreSame,"this.objectsAreSame")
}
}
FORM:
private settingsInfoForm() {
if (!this.agentDetailsList) {
// Add
this.agentSettingsInfoForm = this.FB.group({
agentToogles: this.FB.array([this.detailsToggle]),
});
// this.authService.setDetailsData(this.agentSettingsInfoForm);
} else {
// Edit
if (this.agentDetailsList) {
this.detailsToggle = this.agentDetailsList
this.agentSettingsInfoForm = this.FB.group({
agentToogles: this.FB.array([this.detailsToggle]),
})
}
let settingsInfo = this.agentSettingsInfoForm.valueChanges.subscribe(data => {
this.formEdit = true;
console.log('agentSettingsInfoForm', this.formEdit)
})
}
DEMO
the problem is that you has any FormArray.
//this has no sense
this.FB.array([this.detailsToogle])
Do you want to write ?
this.FB.array(this.detailsToggle.map(x=>this.FB.group(x)))
This is a FromArray of FormGroups
Any way, if you only want to change the boolValue, you don't need create a formArray so complex, you can simply
this.FB.array(this.detailsToggle.map(x=>x.boolValue))
this is FormArray of FormControls
When you has an FormArray is for manage a FormArray. You has some too complex as
<input type="checkbox" [checked]="toggleValue.boolValue" (change)="toggleValue.boolValue = $event.target.checked">
when can be like,e.g. is is a FromArray of FormControls
<input type="checkbox" [formControlName]="i">
Really, you need ask you what data you want get and what data you want change. Tip: Before write nothing in your .html write
<pre>
{{agentSettingsInfoForm.?value |json}}
</pre>

Angular FormGroup - Automatically divide value from another field by 100

I have 3 fields in an angular form group and one of the fields is basically a tool for the user that divides the value from another field automatically. So, the user types in a number and the other field automatically displays that value divided by 100.
It looks like this:
formGroup = new FormGroup({
Buildyear: new FormControl(this.buildyear, [Validators.minLength(4), Validators.maxLength(4)]),
Areasize: new FormControl(this.areasize, [Validators.required] ),
Areasize_divided: new FormControl(''),
});
"Areasize_divided" automatically divides the value that's inside "Areasize", by default there's already a value in that field but if the user types in a new value into "Areasize" that should be automatically calculated in "Areasize_divided" as well. And the other way around, if the user types in a value into "Areasize_divided" it will automatically multiply "Areasize" by 100.
What would be the best way of achieving something like this?
You should listen on form control changes and update your other control accordingly.
formGroup.get('Areasize')
.valueChanges
.subscribe(value => formGroup
.get('Areasize_divided')
.setValue(isNaN(value) ? 0 : value / 100)
);

In React Final Form, providing initialValues to nested fields

I'm working with React Final Form, and for some reason, when I try to pass initial values to its initialValues prop, it doesn't work if the keys I use are in the format 'key6.value' - the Field with that name stays empty. It does, however, work if the format doesn't have the . in the middle, e.g. 'key6value'.
Why doesn't initialValues work for these nested fields (fields with names that have the .)? And what can I do to make it pass the initialValues?
I've tested this thoroughly to make sure I identified the issue, and the only difference between having the Fields populate and not has been the . in their name attribute.
You'll need to initialize with the actual nested structure. Not like this:
{
'key5.value': 'init value' // ❌
...
}
Like this:
{
key5: {
value: 'init value' // ✅
}
...
}
Does that help?

What is the proper way to assign an array item's value based on a checkbox status?

I was hoping this would work:
var title = document.getElementById('cat_title');
var active = document.getElementById('cat_active');
var category = {
title: title.value,
active: $(active).prop("checked");
}
When the form is submitted, and if the checkbox is checked, it inserts "true" into the active field. When it's not checked, it sends nothing but I'd like it to actually send "false".
Example of saved document if form is submitted WITHOUT checkbox checked:
{
_id: "lklajdlfjkasdf",
title: "Some Title"
}
Example of saved document if form is submitted WITH checkbox checked:
{
_id: ";klajsdfadsf"
title: "Some Title",
active: true <-- saved as boolean, good.
}
I'd like to keep the logic within the array. I understand another method to handle this... well there are several. I can perform some if/else and push to the array, but I feel I'm missing something simple with this array.
Thank you!
-------- Edit ---------
Details requested:
I'm inserting the record as a document in MongoDB. When the checkbox isn't checked, it inserts nothing into the active key/value pair. To answer various comments throughout, I need this as a boolean value, not a string. I'm trying some of the suggestions now and will update. Thank you all very much.... I've also updated my question above in more detail.
Even if that code carried the false value up to the database insertion, inserting a false value into the database will only make it a null field, which is the equivalent of setting it to nothing.
So the two options you have are to:
1) Check for a null field when retrieving the rows from the database
OR
2) Use the string value of "false" in the database and check for that instead

Categories