Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 - Reactive Forms enable/disable not working

I am having trouble enabling and disabling a form controls in my application. It is probably because the form is being enabled/disabled in an asynchronous context.

Here is how the code is.

user.component.html

<form [formGroup]="form">
    <input type="text" id="name" formControlName="name" />
    <input type="text" id="age" formControlName="age" />
</form>

user.component.ts

ngOnInit() {

     this._route.queryParams.subscribe(params => {
            getUser(params.userId).subscribe(user => {
                 this._buildForm(user);
            })
     });
}

private _buildForm(user){
    this.form = _fb.group({
        name: [{value: user.name, disabled: user.someCondition}, Validators.required],
        age: [{value: user.age, disabled: user.anotherCondition}, Validators.required]
    })
}    

When the first time the user is loaded upon param change, the controls do get enabled/disabled on the basis of their relevant conditions. When the params change subsequently, the state of the controls remain the same although the values are set appropriately.

I have tried different ways to fix this but no help. For example, I have tried the following at the end of the _buildForm method.

this.form.disable() //Doesn't do anything

this.form.controls.name.disable(); //Doesn't help

One thing that does work as expected is the following (but this isn't what is required).

<button (click)="form.disable()" value="Disable Form" />
<button (click)="form.enable()" value="Enable Form" />

What I feel is that the problem is due the fact the _buildForm() is being called from the asynchronous context (subscribe method).

How can I fix this problem?

UPDATE

Please note that the Observable subscription is triggered on the basis of the navigation as below.

this._router.navigate([], {
   relativeTo: this._route,
   queryParams: {userId: 10}
})

UPDATE 2 https://angular-cvf1fp.stackblitz.io

This is an easier way to understand my problem

like image 532
Kashif Nazar Avatar asked Apr 26 '18 18:04

Kashif Nazar


4 Answers

You just need enable/disable methods of form control.

Here is stackblitz example. It works perfectly.

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  group;
  isDisabled = true;

  constructor(fb: FormBuilder) {
    this.group = fb.group({
      stuff: fb.control({value: 'stuff', disabled: this.isDisabled})
    });
  }

  toggle() {
    this.isDisabled = !this.isDisabled;

    this.group.controls.stuff[this.isDisabled ? 'enable' : 'disable']();
  }
}
like image 162
Sharikov Vladislav Avatar answered Oct 24 '22 04:10

Sharikov Vladislav


You're right. The subscription is only fired once, so after submitting the form the user entity does not change anymore. To do it, add for instance an event handler to your form submit.

user.component.html

<form [formGroup]="form" (submit)="_updateForm()">
    <input type="text" id="name" fromControlName="name" />
    <input type="text" id="age" fromControlName="age" />
</form>

user.component.ts

private userId: any;


ngOnInit() {
    this._route.queryParams.subscribe(params => {
        this.userId = params.userId;
        this._updateForm();
    });
}

private _buildForm(user){
    this.form = _fb.group({
        name: [{value: user.name, disabled: user.someCondition}, Validators.required],
        age: [{value: user.age, disabled: user.anotherCondition}, Validators.required]
    });
} 

// The update function has to be public to be called inside your template
public _updateForm(){
    getUser(this.userId).subscribe(user => {
        this._buildForm(user);
    });
}    
like image 31
Michael Czechowski Avatar answered Oct 24 '22 04:10

Michael Czechowski


  • Create the formGroup directly in ngOnit w/controls disabled by default so you're sure it exists w/o worrying about async issues.
  • In this._route.queryParams.subscribe(...) you can use this.form.setValue({//set all control values}) or this.form.patchValue({//set individual controls}) to update the individual controls as needed/once available.
  • Use component instance variables for condition and someOtherCondition (or a user object as a whole if you want to separate your data model from the form's model - they don't have to be the same). Initialize these to "false", and refer to them instead for enabling/disabling the controls in the formGroup creation. You can also update those variables in the same .subscribe() block once the user data is in place, instead of waiting to access user data model directly.

This way your controls and formGroup exist, and are disabled, until the async data is available.

like image 2
mc01 Avatar answered Oct 24 '22 03:10

mc01


<form [formGroup]="form">
    <input type="text" id="name" fromControlName="name" />
    <input type="text" id="age" fromControlName="age" />
</form>

do you see a typo here?fromControlName should be formControlName After that change toggling will work ;)

https://angular-rivxs5.stackblitz.io

like image 1
Antoniossss Avatar answered Oct 24 '22 04:10

Antoniossss