Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Component inside form Angular 2

Tags:

angular

So here is my parent form:

<form [ngFormModel]="formModel">
    <ui-form-control *ngFor="#key of controlsKeys"
                     [control]="formModel.controls[key]"
                     [name]="key">
    </ui-form-control>
</form>

here is my component:

@Component({
    selector: 'ui-form-control'
})
@View({
    template: `
    <label>{{name}}: </label>
    <input [ngFormControl]="control" [placeholder]="name">
    `,
    directives: [FORM_DIRECTIVES]
})
export class UiFormControl{
    @Input() control: UiControl;
    @Input() name: string;

    constructor(){
    }
}

I can see the Control value inside my ui-form-component, but when I've change it formModel-ComponentGroup doesn't update. So it looks like two way binding don't work here.

Actually if i'll remove my <ui-form-control> and place simple <input> tag instead it will work and formModel will update as expected.

like image 937
korovaisdead Avatar asked Nov 08 '22 22:11

korovaisdead


1 Answers

I think that you should use two way binding here in your sub component with both @Input and @Output. The latter is notified the parent component that the control changes within the sub component. I think about something like that:

@Component({
  selector: 'ui-form-control'
  template: `
    <label>{{name}}: </label>
    <input [ngFormControl]="control" (change)="inputChanged()" [placeholder]="name">
  `,
  directives: [FORM_DIRECTIVES]
})
export class UiFormControl{
  @Input() control: UiControl;
  @Output() controlChange: EventEmitter;
  @Input() name: string;

  constructor(){
    this.controlChange = new EventEmitter();
  }

  inputChanged() {
    this.controlChange.emit(this.control);
  }
}

I used an intermediate approach for the field of my forms by leveraging ng-content.

@Component({
  selector: 'field',
  template: `
    <div class="form-group form-group-sm" [ngClass]="{'has-error':control && !control.valid}">
      <label for="for"
         class="col-sm-3 control-label">{{label}}</label>

      <div #content class="col-sm-8">
        <ng-content ></ng-content>
        <span *ngIf="control && !control.valid" class="help-block text-danger">
          <span *ngIf="control?.errors?.required">The field is required</span>
        </span>
      </div>
    </div>
  `
})
export class FormFieldComponent {
  @Input()
  label: string;

  @Input()
  state: Control;
}

And its use within the parent component:

<form [ngFormModel]="companyForm">
  <field label="Name" [state]="companyForm.controls.name">
    <input [ngFormControl]="companyForm.controls.name" 
           [(ngModel)]="company.name"/> {{name.valid}}
  </field>
</form>

That way the inputs, selects, textareas remain managed within the parent component but field components tackles field formatting (structure for Bootstrap3 for example) and leverage controls to display errors if any. This way you don't need two way binding anymore for field component and the latter is more generic ;-)

Hope it helps you, Thierry

like image 127
Thierry Templier Avatar answered Nov 15 '22 07:11

Thierry Templier