patchValue not working when using [[ngModel]] - html

So when I open an item editor dialog, I want to be able to put some default values that I retrieve from the specific item I selected. This all worked up until I added [[ngModel]] in each of the fields. In fact, it still works when I remove it. I want to be able to do it again without removing the ngModel as I heavily depend on it in my code.
This is my HTML
<div mat-dialog-content class="formCentered">
<form [formGroup]="form">
<mat-form-field class="inputBox">
<mat-label>Scanner</mat-label>
<input matInput formControlName="scannerName" [(ngModel)]="data.scanName" maxlength="50">
</mat-form-field>
<mat-form-field class="inputBox">
<mat-label>Calibration Price</mat-label>
<input matInput formControlName="calibrationPrice" [(ngModel)]="data.caliPrice" maxlength="50">
</mat-form-field>
<mat-form-field class="inputBox">
<mat-label>Calibration Description</mat-label>
<input matInput formControlName="calibrationDescription" [(ngModel)]="data.caliDescription" maxlength="50">
</mat-form-field>
</form>
</div>
<div mat-dialog-actions class="alignRight">
<button mat-button class="confirmationButton" (click)="onNoClick()" class="confirmationButton">Cancel</button>
<button mat-button class="confirmationButton" [mat-dialog-close]="data" cdkFocusInitial class="confirmationButton">Save</button>
</div>
And this is my part of the ts for this
dialogRef.componentInstance.form.patchValue({
scannerName: cal.scannerName, // scannerName is the old value (unedited)
calibrationPrice: cal.calibrationPrice, // calibrationPrice is the old value (unedited)
calibrationDescription: cal.calibrationDescription // calibrationDescription is the old value (unedited)
});
Any idea on how I can achieve that?

It's not a good practice merge reactive forms with ngModel.
You can remove all of the [(ngModel)] in the html, and subscribe to the form changes. Then, when some field of the form change, the subscription would change your varaibles as well.
Add in your ts code something like this:
// after you have inicialized your form
this.form.get('scannerName').valueChanges
.subscribe((val: any) => {
//this.data.scanName = this.form.get('scannerName').value;
this.data.scanName = val
});
this.form.get('calibrationPrice').valueChanges
.subscribe((val: any) => {
//this.data.calibrationPrice= this.form.get('calibrationPrice').value;
this.data.calibrationPrice= val
});
this.form.get('calibrationDescription').valueChanges
.subscribe((val: any) => {
//this.data.calibrationDescription= this.form.get('calibrationDescription').value;
this.data.calibrationDescription= val
});

You can't add NgModel in formGroup.
You have to define the formControl
example:
form = new FormGroup({
scannerName: new FormControl(''),
calibrationPrice: new FormControl(''),
calibrationDescription: new FormControl(''),
})
If you want set default value, you have to set value in new FormControl like this:
form = new FormGroup({
scannerName: new FormControl('test'),
calibrationPrice: new FormControl('value'),
calibrationDescription: new FormControl(someValue),
})
it's seems that you want change data variable, you could do this:
this.data = this.form.value

Related

The value [value] is not assigned in input

I have a little problem using [value] in my inputs.
This is my component .html
<div class="form-group">
<mat-form-field class="example-full-width" style="padding-left: 15px;">
<input matInput [value]='value_2_' [(ngModel)]="form.name_1" placeholder="name_1"
name="name_pro_1" required>
</mat-form-field>
</div>
Everything looks good, but when I run the program, the value is shown in the corresponding input, however, being a value required by the input, it is still in red, it is solved until I add a letter to the value or remove something from the input.
I managed to solve the error by deleting
name = "name_pro_1"
but since I use NgModel I cannot remove the name because I get other errors. Like:
core.js:4352 ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form
control must be defined as 'standalone' in ngModelOptions.
Example 1: <input [(ngModel)]="person.firstName" name="first">
Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">
I think the problem is [(ngModel)]="form.name_1". Try to replace that with
[(ngModel)]="testValue" and define a variable in the class with the same name and it will work.
I can't replicate the problem, but using [(ngModel)] doesn't make using [value] unnecessary? You can set the form.name_1 to the value you want on the component.ts file.
Finally i resolved the issue, that it's my solution:
First thing first, i delete the [(ngModel)] in the input of component.html, then i used (change) to call the function when user select a SKU
<div class="form-group input-sku" >
<mat-form-field class="example-full-width">
<mat-label>SKU</mat-label>
<input type="text" placeholder="Search.." id="sku_select" aria-label="number"
matInput [formControl]="myControl" [matAutocomplete]="auto"
[(ngModel)]="selectedValue" (change)="changedata_1($event)" >
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
<mat-option *ngFor="let data of filter_products | async" [value]="data.sku">{{data.sku}}</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
Input who recived the value:
<div class="form-group inputs-name">
<mat-form-field class="example-full-width">
<mat-label>Name</mat-label>
<input type="text" matInput id="nombre_1" required [value]="name_1" name="name_1" placeholder="Name" >
</mat-form-field>
</div>
And in my component.ts
changedata_1(e) {
this.nombre_1 = e.target.value;
this.DataToSave.nombre_1 = this.nombre_1;}
private _filter(value: string): Products_BD[] {
const filterValue = value.toLowerCase();
const data_ = this.DatasProducts.filter(DataProducts => DataProducts.sku.toLowerCase().includes(filterValue));
for(let element of data_){
const sku1= elemento.sku;
const name1 = element.name;
this.name_1 = name1;
this.DataToSave.sku_1 = sku1;
this.DataToSave.name_1 = name1;
}
return this.DataProducts.filter(DataProducts => DataProducts.sku.toLowerCase().includes(filterValue));}
this.filter_products = this.myControl.valueChanges.pipe(
startWith(''),
map(value => this._filter(value))
);
I hope this solution helps! :)

How to disable a text area or mat-form-field in the same HTML (Angular 9)

I am using Angular 9 with angular Material Reactive Forms. I want to disable the fields, but without using the disabled status on the formControl, just only using HTML instead of programmatically in ts.
My Html:
<form name="nameForm" [formGroup]="nameForm" fxLayout="column">
<mat-form-field fxFlex="50" class="m-4">
<input matInput name="startTime" formControlName="startTime" placeholder="{{'DATE.START' | translate}}" type="date">
</mat-form-field>
<mat-form-field fxFlex="30" class="m-4">
<input matInput name="period" formControlName="period" placeholder="{{'TITLE.PERIOD' | translate}}" type="number" step="0.1">
</mat-form-field>
<mat-form-field fxFlex="20" class="m-4">
<input matInput name="range" formControlName="range" placeholder="{{'TITLE.RANGE' | translate}}" type="number" step="0.1">
</mat-form-field>
</form>
My ts:
ngOnInit(): void {
this.nameForm = this.createNameForm();
... some code ...
}
private createNameForm() {
return this.formBuilder.group({
startTime: new FormControl(this.startTime, []),
period: new FormControl(this.period, []),
range: new FormControl(this.range, []),
});
}
... some code ...
I came across this doubt because some questions like this one (How to disable a text area or mat-form-field) has programmatic answers, and I would like to know if it can be solved using exclusively the Html. I know [disabled]="true" does not work, but maybe with a different directive?.
I hope it's clear enough and thanks in advance.
I think that the disabled directive is not valid for the input (it would work with a select or text-area for example ) but to disable the input we can use the readonly directive
[readonly]="true"
[readonly]="method()"
[readonly]="variable"
Although, if you need to disable it, another solution would be
[attr.disabled]="true"
[attr.disabled]="method()"
[attr.disabled]="variable"
I've tried both and they work :)
I have tried both the [disabled] = 'true' and [readonly] = 'true' for the textarea.
Only [readonly] property works well in all the cases in angular.

Set default value of form field based on the dropdown selected by the user

I have a form where a user has to enter a bunch of info. I am using mat-select for one dropdown field. What I'm trying to do is set the default value of another form field based on the drop down selected by the user. How can I achieve this. Thanks in advance for your help.
Here is my template:
<form [formGroup]="newFormGroup" (ngSubmit)="submit()">
<div f>
<mat-form-field appearance="fill" >
<mat-label>Animal Sound</mat-label>
<input matInput type="text" formControlName="animalSound" [(ngModel)]="sound" name="animalSound" class="form-control" id="animalSound">
</mat-form-field>
<mat-form-field appearance="fill" style="width: 10%;">
<mat-label>Animal</mat-label>
<mat-select formControlName="animal" [(ngModel)]="selectedValue">
<mat-option *ngFor="let animal of animals" [value]="animal.value">
{{animal.value}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</form>
Here is my ts code:
selectedValue: string;
sound = 'bark';
animals: AnimalType[] = [
{value: 'Cat' },
{value: 'Dog'} ];
newFormGroup = new FormGroup({
animalSound: new FormControl('', Validators.required),
animal: new FormControl('', Validators.required)
});
So for example, If user selects dog, I want set the default value for the animal sound field as bark.
create a service which has a behavior subject;
#Injectable({provideIn: 'root'})
class DropdownService {
value = new BehaviourSubject('Default')
setValue(value: string){
this.value.next(value);
}
getValue() {
return this.value.asObservable();
}
}
set value on dropdown component
then get value in form component
constructor(private dropdownService: DropdownService) {}
ngOnInit(){
// after your form initiated
this.dropdownService.getValue().subscribe(value => {
this.form.controls.field.setValue(value)
});
}

Set initial value to a Toggle of a form in Angular Material within HTML

I am working with a simple form:
HTML
<mat-card>
<form (submit)="onAddBet(betForm)" #betForm="ngForm">
<mat-form-field>
<textarea #description="ngModel" matInput rows="6" placeholder="Description" name="description" ngModel
required></textarea>
<mat-error *ngIf="description.invalid">Please enter a Bet Description</mat-error>
</mat-form-field>
<mat-slide-toggle #privacy="ngModel" name="privacy" ngModel>Private</mat-slide-toggle>
<mat-slide-toggle #comments="ngModel" name="comments" ngModel>Allow comments</mat-slide-toggle>
<button mat-raised-button color="primary" type="submit">
CREATE
</button>
</form>
</mat-card>
If I press submit without touching any field of the form I get all the fields empty as it should be expected but I would like to get instead of the status of the form, meaning all the fields as "" but in the fields "privacy" and "comments" as false (boolean) (the default appearance of the toggles is as not marked).
I know that this could be easily done by Typescript from the component following the method:
Typescript
onAddBet(form: NgForm) {
if (!form.value.privacy) {
form.value.privacy = false;
console.log(form.value);
} else console.log(form.value);
if (form.invalid) return;
form.resetForm();
}
But I was wondering if there is any HTML attribute to run the same code avoiding to use Typescript
How could I do it?
You can initialize your ngModel in your HTML by using ngModel with a one-way binding
<mat-slide-toggle #privacy="ngModel" name="privacy" [ngModel]="false">Private</mat-slide-toggle>
<mat-slide-toggle #comments="ngModel" name="comments" [ngModel]="false">Allow comments</mat-slide-toggle>
Then in your component
onAddBet(form: NgForm) {
console.log(form.value);
if (form.invalid) return;
form.resetForm();
}
As per the documentation of slide-toggle you can use [checked]="checked" in HTML.

Reset Angular Formly Form Validations on click event

I am implementing dynamic forms using the angular formly module and It's working fine. What functionality I need is Initially there should be a select box which contains multiple options, Based on selection different form fields should be displayed. As I explained I have Implemented and it's working, here what my problem is If I select option 1 and If I submit the form without filling fields, form displaying validation errors that is also cool. But when I select option 2 form fields are changing, but by default, all required fields are showing errors. How can I resist this? Please suggest me.
html
<div class="row">
<mat-form-field class="col-lg-2">
<mat-select placeholder="Form For" (selectionChange)="getSelectedFormName($event)">
<mat-option value="uf001">UF001</mat-option>
<mat-option value="uf002">UF002</mat-option>
<mat-option value="uf003">UF003</mat-option>
</mat-select>
</mat-form-field>
<div class="col-lg-4">
<button type="button" class="btn btn-default btn-one" (click)="getDynamicForm()">GET FORM</button>
</div>
</div>
<form [formGroup]="form" (ngSubmit)="submit(model)" >
<formly-form [model]="model" [fields]="fields" [form]="form" *ngIf="isFormTypeSelected" >
</formly-form>
<button type="submit" class="btn btn-success">Submit</button>
</form>
ts file
getSelectedFormName(eve) {
this.isFormSaved = false;
this.form = new FormGroup({});
this.fields=[];
this.model = {};
this.parentFormName = eve.value;
}
getDynamicForm() {
this.isFormSaved = false;
this.savedFields=[];
this.getDynamicFormBasedOnSelection(this.parentFormName);
//fields getting from api call
}
getDynamicFormBasedOnSelection(formName: string) {
this.auth.getDynamicFormBasedOnSelction(formName, this.userAgencyCode).subscribe(
(result) => {
const str = JSON.stringify(result);
this.fields = JSON.parse(str);
this.isFormTypeSelected = true;
this.addEvents(this.fields);
});
}
Here I'm providing my screens which are for better understanding
Actually form.reset() just reset the form values. You need to reset the form directive too. for eg.
<form [formGroup]='authForm' (submit)='submitForm(formDirective)' #formDirective="ngForm" class="is-mat-form">
<mat-form-field>
<input matInput placeholder="Email ID" formControlName='login'>
<mat-error *ngIf="authForm.controls.login.hasError('required')">
Email is required
</mat-error>
<mat-error *ngIf="authForm.controls.login.hasError('email')">
Please enter a valid email address
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" formControlName='password' placeholder="Password">
<mat-error *ngIf="authForm.controls.password.hasError('required')">
Password is required
</mat-error>
<mat-error *ngIf="authForm.controls.password.hasError('minlength')">
Password must be minimum 6 digit long.
</mat-error>
</mat-form-field>
and .ts file is
submitForm(formDirective: FormGroupDirective){
if (this.authForm.invalid) {
console.log('form submitted')
this.authForm.reset()
return;
}
this will reset the form values only, to reset the form error we need to reset the formdirective as well.
submitForm(formDirective: FormGroupDirective){
if (this.authForm.invalid) {
console.log('form submitted')
this.authForm.reset()
formDirective.resetForm();
return;
}