4

I have a dynamic form (made an example using angular.io dynamic form live example plunkr) and I want to disable an input of this form, to display it as a readonly information.

So I decided to add disabled attribute to the question model:

export class QuestionBase<T>{
  value: T;
  key: string;
  label: string;
  required: boolean;
  order: number;
  controlType: string;
  disabled?:boolean;

  constructor(options: {
      value?: T,
      key?: string,
      label?: string,
      required?: boolean,
      order?: number,
      controlType?: string,
      disabled?:boolean
    } = {}) {
    this.value = options.value;
    this.key = options.key || '';
    this.label = options.label || '';
    this.required = !!options.required;
    this.order = options.order === undefined ? 1 : options.order;
    this.controlType = options.controlType || '';
    this.disabled = options.disabled || false;
  }
}

And then I bind disabled to the input:

<input *ngSwitchCase="'textbox'" [disabled]="question.disabled" [formControlName]="question.key"
            [id]="question.key" [type]="question.type">

I get a warning, and the input is not disabled:

It looks like you're using the disabled attribute with a reactive form directive. If you set disabled to true
      when you set up this control in your component class, the disabled attribute will actually be set in the DOM for
      you. We recommend using this approach to avoid 'changed after checked' errors.

      Example: 
      form = new FormGroup({
        first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
        last: new FormControl('Drew', Validators.required)
      });

So I did like it's written in the warning and I get a problem, validator seems to not like the disabled field, even if it's not marked as required.

Here is what I changed the new QuestionControlService class:

@Injectable()
export class QuestionControlService {
  constructor() { }

  toFormGroup(questions: QuestionBase<any>[] ) {
    let group: any = {};

    questions.forEach(question => {
      group[question.key] = question.required ? new FormControl({value: question.value || '', disabled: question.disabled}, Validators.required)
                                              : new FormControl({value: question.value || '', disabled: question.disabled});
    });
    return new FormGroup(group);
  }
}

Problem

The disabled test field is disabled, but not valid, which should not be possible since it has not been modified at all.

Plunkr for my issue: http://plnkr.co/edit/qSDnD2xWWUwafyToDNX1?p=preview

Supamiu
  • 8,501
  • 7
  • 42
  • 76

3 Answers3

2

I submitted an issue on github and turns out that this is the desired behaviour.

My error here was to check each field for its validity instead of checking the whole form.

Supamiu
  • 8,501
  • 7
  • 42
  • 76
0

There are several issues on this topic open on GitHub. Since I was experiencing the same, I tried to build a custom directive to supply the desidered behavior.

@Directive({
  selector: '[formControlName][dynamicDisable]'
})
export class DynamicDisable implements OnInit, OnChanges {
  constructor(
    @Optional() @Host() @SkipSelf() private parent: ControlContainer,
  ) { 

  }

  @Input() formControlName: string;  
  @Input() dynamicDisable: boolean;

  private ctrl: AbstractControl;

  ngOnInit() { 
    if(this.parent && this.parent["form"]) {
      this.ctrl = (<FormGroup>this.parent["form"]).get(this.formControlName);
    }
  }

  ngOnChanges() {
    if (!this.ctrl) return;

    if (this.dynamicDisable) {
      this.ctrl.disable();
    }
    else {
      this.ctrl.enable();
    }
  }
}

Follow updated on this issue: https://github.com/angular/angular/issues/11379#issuecomment-246756547

user2363245
  • 1,675
  • 2
  • 11
  • 13
  • As kara said on my github issue, this behaviour is by design. You should not control each field one by one, else you have to check if the control is active before checking if it is valid or not. – Supamiu Sep 13 '16 at 18:10
0

Use readonly property instead of disabled.

vollukas
  • 66
  • 6