angularangular-elements

Angular elements as native form control in reactive form


I'm trying to export a form control as an Angular Element (web component) and have it work as a native input element so that it could be used with angular reactive forms or similar libraries that work with native html inputs.

The bellow code works when it is exported as an angular component in a module. I'm having issues when I export this as a web component and try to use it in a reactive form.

this code is written using Angular 7.1 framework.

the component template

<input ngDefaultControl value="value" />

the component code:

@Component({
  selector: 'cns-text-input',
  templateUrl: './text-input.component.html',
  styleUrls: ['./text-input.component.scss'],
  providers: [ {
    provide: NG_VALUE_ACCESSOR,
    useExisting:  forwardRef(() => TextInputComponent),
    multi: true
  }]
})
export class TextInputComponent implements ControlValueAccessor,     AfterViewInit {

  @ViewChild(DefaultValueAccessor) valueAccessor:     DefaultValueAccessor;

  public ngAfterViewInit() {
    console.log('value accessor', this.valueAccessor);
    console.log(this);
  }

  public writeValue(obj: any): void {
    this.valueAccessor.writeValue(obj);
  }
  public registerOnChange(fn: any): void {
    this.valueAccessor.registerOnChange(fn);
  }
  public registerOnTouched(fn: any): void {
    this.valueAccessor.registerOnTouched(fn);
  }
  public setDisabledState?(isDisabled: boolean): void {
    this.valueAccessor.setDisabledState(isDisabled);
  }

}

module code:

@NgModule({
  declarations: [TextInputComponent],
  imports: [FormsModule],
  // exports: [TextInputComponent],
  entryComponents: [TextInputComponent]
})
export class FormModule {
  constructor(private injector: Injector) {
    const textInputElement = createCustomElement(TextInputComponent, { 
      injector 
    });
    customElements.define('cns-text-input', textInputElement);
  }

  ngDoBootstrap() {}
}

useage example:

// app component html
<form [formGroup]="group">
  <cns-text-input formControlName="text"></cns-text-input>
</form>

// app component

export class AppComponent {
  title = 'web-components-lib-poc';

  public group: FormGroup;

  constructor(private fb: FormBuilder) {
    this.group = this.fb.group({ text: 'my vlaue' });
    this.group.valueChanges.subscribe(val => console.log(val));
  }
}

This above code results in an error: ERROR Error: No value accessor for form control with name: 'text'

Is there a better way to construct this component so that it behaves more like a native input?


Solution

  • I found the answer. When using non-native input components within an angular application,ngDefaultControl will help to resolve the value accessor errors.

    So the code will be:

    <cns-text-input formControlName="text" ngDefaultControl></cns-text-input>
    

    ** EDIT **

    This will also work for other webcomponents like the Polymer Elements Paper Input