Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ngModel custom ValuesAccessor

Advanced question about ngModel and DI.

As i can see here = https://github.com/angular/angular/blob/2.0.0-beta.1/modules/angular2/src/common/forms/directives/ng_model.ts#L68 ngModel wating for providers to come from NG_VALUE_ACCESSOR OpaqueToken. This mean if i want to create custom components which should support ngModel binding i should pass my realization of ValueAccessor to DI. So there is two questions in my mind.

1) How can i do this?

2) What's about default ValueAccessor for <input> elements? How to make it continue to work and use mine only for custom components?

Btw as i see in here: https://github.com/angular/angular/blob/2.0.0-beta.1/modules/angular2/src/common/forms/directives/shared.ts#L102 defaultValueAccessor is last. So this mean if i globally pass mine ValueAccessor throught DI system than default one have never been returned.

like image 288
Maksim Fomin Avatar asked Feb 20 '26 14:02

Maksim Fomin


2 Answers

You can register a custom ControlValueAccessor like this within the providers (the bindings one is deprecated) attribute of the corresponding directive:

const CUSTOM_VALUE_ACCESSOR = CONST_EXPR(new Provider(
  NG_VALUE_ACCESSOR,
  {useExisting: forwardRef(() => TagsValueAccessor), multi: true}));

@Directive({
  selector: 'tags',
  host: {'(labelsChange)': 'onChange($event)'},
  providers: [CUSTOM_VALUE_ACCESSOR]
})
export class TagsValueAccessor implements ControlValueAccessor {
  (...)
}

Then this accessor will be automatically selected when you will use ngModel and / or ngFormControl for the component with selector tags:

@Component({
  (...)
  directives: [ TagsComponent, TagsValueAccessor ],
  template: `
    <tags [(ngModel)]="company.labels" 
          [ngFormControl]="companyForm.controls.labels"></tags>
  `
  })
  export class DetailsComponent {
    (...)
  }

A complete sample of this is available in this question: Angular 2 custom form input.

Hope it helps you, Thierry

like image 161
Thierry Templier Avatar answered Feb 23 '26 03:02

Thierry Templier


Why to create a new value accessor when you can use the inner ngModel.

That answers the 2nd question to @Maksim Fomim

template:

<div class="form-group" [ngClass]="{'has-error' : hasError}">
    <div><label>{{label}}</label></div>
    <input type="text" [placeholder]="placeholder" ngModel [ngClass]="{invalid: (invalid | async)}" [id]="identifier"        name="{{name}}-input" />    
</div>

Component:

export class MyInputComponent {
    @ViewChild(NgModel) innerNgModel: NgModel;

    constructor(ngModel: NgModel) {
        //First set the valueAccessor of the outerNgModel
        this.outerNgModel.valueAccessor = this.innerNgModel.valueAccessor;

        //Set the innerNgModel to the outerNgModel
        //This will copy all properties like validators, change-events etc.
        this.innerNgModel = this.outerNgModel;
    }
}

Use as:

<my-input class="col-sm-6" label="First Name" name="firstname" 
    [(ngModel)]="user.name" required 
    minlength="5" maxlength="20"></my-input>
like image 29
Nishant Avatar answered Feb 23 '26 05:02

Nishant