0
votes

I have a 'control value accessor' class from here http://blog.rangle.io/angular-2-ngmodel-and-custom-form-components/

export class ValueAccessorBase<T> implements ControlValueAccessor {
  private innerValue: T;
  private changed = new Array<(value: T) => void>();
  private touched = new Array<() => void>();

  get value(): T {
    return this.innerValue;
  }
  set value(value: T) {
    if (this.innerValue !== value) {
      this.innerValue = value;
      this.changed.forEach((f) => f(value));
    }
  }
  touch() {
    this.touched.forEach((f) => f());
  }
  writeValue(value: T) {
    this.innerValue = value;
  }
  registerOnChange(fn: (value: T) => void) {
    this.changed.push(fn);
  }
  registerOnTouched(fn: () => void) {
    this.touched.push(fn);
  }
}

I extend that class in a component:

export class PricingComponent extends ValueAccessorBase<any> implements OnInit {
  constructor() {
    super(); // value accessor base
  }
}

Inside PricingComponent template using ngModelGroup to group multiple input form controls into an object:

  <div ngModelGroup="value">
    <md-select [(ngModel)]="value.type" name="type">
      <md-option *ngFor="let c of list" [value]="c.code">{{c.dsc}}</md-option>
    </md-select>
    <md-input-container>
      <input [(ngModel)]="value.amount" name="amount" mdInput>
    </md-input-container>
  </div>

And PricingComponent is used like this:

<form #form="ngForm">
  <app-pricing name="prices" ngModel></app-pricing>
  {{form.value | json}}
</form>

Now, what I would like to get from the form.value is something like:

{ prices: { type: 'high', amount: 542 } }

But I'm getting this error:

No provider for ControlContainer
2

2 Answers

1
votes

You need to add PricingComponent to list of NG_VALUE_ACCESSOR. So in you component metadata add:

providers: [
  {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => PricingComponent),
    multi: true
  }
]
0
votes

Not sure this is the best way but I have solved it by not extending the ValueAccessorBase class, and instead I use ngModelGroup:

Using the Pricing component:

<app-pricing ngModelGroup="multiCurrency"></app-pricing>

The Pricing component class:

export class PricingComponent implements OnInit, AfterViewChecked {
  @ContentChild(NgModelGroup)
  private _group: NgModelGroup;

  @ViewChild('pricingType')
  private _type: NgModel;

  @ViewChild('pricingAmount')
  private _amount: NgModel;

  private _registered = false;

  ngAfterViewChecked() {
    if (!this._registered && this._group.control != null) {
      this._group.control.registerControl('type', this._type.control);
      this._group.control.registerControl('amount', this._amount.control);
      this._registered = true;
    }
  }
}

Pricing component template:

  <div>
    <md-select #pricingType="ngModel" name="type">
      <md-option></md-option>
    </md-select>
    <md-input-container>
      <input #pricingAmount="ngModel" name="amount" mdInput>
    </md-input-container>
  </div>

source: http://plnkr.co/edit/xJr1ZZBkSz3TuT43Tpwm?p=preview