2
votes

I have a nested reactive form in angular.

The child has 2 properties and the second one is either enabled or disabled based on the value of the first.

The parent form has an edit button that enables/disables fields through this.parentForm.enable().

My problem is this enables the second child field no matter what and doesn't use the logic in the child to determine if it should be enabled or disabled.

What is the suggested approach for reactive forms with enable/disable logic in the children? I tried using [disabled] but it didn't work and I received warnings that I shouldn't do this.

Here's a stackblitz with a sample problem.

When edit is clicked it should only enable the last input if Yes is checked. It should be disabled if No is checked.

3
you should put your code in your question. - bryan60
@bryan60 instead of in the stack blitz? - Ashley
both is recommended, links can go bad in the future but they're helpful now. - bryan60

3 Answers

4
votes

so add a function like this to your controller:

 private setChildState() { 
    // this function gets the ctrl to enable/disable and does so based on the value
    const v = this.parentForm.get('typeDetails').get('identity').value;
    let ctrl = this.parentForm.get('typeDetails').get('identityname');
    if (v === 'yes') {
      ctrl.enable();
    } else {
      ctrl.disable();
    }
 }

this is to set your child form enable / disable state, then just add these few lines:

this in your ngOnViewInit()

// this listens to value changes and updates form state
this.parentForm.get('typeDetails').get('identity').valueChanges.subscribe(v => {
  this.setChildState();
});

and then this in your edit function:

this.setChildState();

fixed blitz: https://stackblitz.com/edit/angular-bwfn35?file=src/app/app.component.ts

EDIT:

if you want to keep this all (mostly) in the child, change your child to this:

  ngOnInit() {
    this.identifyForm.get('identity').valueChanges.subscribe(v => {
      this.setState();
    })
  }

  setState() {
    const v = this.identifyForm.get('identity').value;
    let ctrl = this.identifyForm.get('identityname');
    if (v === 'yes') {
      ctrl.enable();
    } else {
      ctrl.disable();
    }
  }

then add this to your edit function in the parent:

this.childComponent.setState();
0
votes

Move the Radio Group code to your parent component and subscribe to the changes

based on the value yes or no

enable and disble child component form control

filed stackblitz: https://stackblitz.com/edit/angular-kszjwj

0
votes

Another aproach is improve the enabled directive of this stackoverflow answer

We are going to improve the directive to allow disabled not only the control else all the controls inside a form.

@Directive({
  selector: '[disableControl]'
})
export class DisableControlDirective {
  @Input() set disableControl(condition: boolean) {
    if (this.ngControl) { //if is an ngControl
      if (condition)
        this.ngControl.control.disable();
      else
        this.ngControl.control.enable();
    }
    if (this.controls) { //if is a formGroup, we ask about the inners controls
      this.controls.forEach((x: any) => {
        let control:boolean=false;
        if (this.innerControl) //we check if the control has
                               //his own DisableControlDirective
          control=(this.innerControl.find(inner=>x==inner.ngControl)!=null)

        if (!control) { //if it has not a own DisabledControlDirective
          if (condition)
            x.control.disable();
          else
            x.control.enable()
        }
      })
    }
  }
  @ContentChildren(NgControl) controls: QueryList<NgControl>
  @ContentChildren(DisableControlDirective)
             innerControl:QueryList<DisableControlDirective>
  //see that make public ngControl to use when check if a innerControl has 
  //the directive
  constructor(@Optional() public ngControl: NgControl) {}
}

If we apply the directive to a fromGroup, disabled/enabled all the inners controls. For get the inners control we are using @ContentChildren(NgControl). When we inject NgControl, we need put @Optional() because in a FormGroup we has NO NgControl -remember that NgControl is anything that has [formControlName], [NgModel] or [formControl] directive

We need check if our inner controls has no [disableControl] directive. In this case we need leap this control. For this, we need get these controls using @ContentChildren(DisableControlDirective) and check it.

Our form becomes very "easy", but we pass as @Input the edit mode to the child

<form [formGroup]="parentForm" [disableControl]="!editMode" (ngSubmit)="onSave()">
  <input formControlName="id">
  <input formControlName="name">
  <app-child-component [editMode]="editMode"></app-child-component>
</form>

//and our children component

<form [formGroup]="identifyForm" [disableControl]="!editMode">
  <input type="radio" name="identity" value="yes" formControlName="identity"> Yes
  <input type="radio" name="identity" value="no" formControlName="identity" > No

  <input formControlName="identityname" 
     [disableControl]="!editMode || identifyForm.get('identity').value=='no'">
</form>

We can see the final result in this stackblitz