3

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.

Maksim Fomin
  • 1,227
  • 2
  • 10
  • 8
  • How about extending the Default One or Wrapping it in your Custom One so even if its yours the one getting used all the time you can fork the code to decide which of the two functionalities to run? – Langley Jan 25 '16 at 15:59

2 Answers2

7

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

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
1

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>
Nishant
  • 33
  • 4