Angular. Использование web components

Стандартно предлагется отключать Angular проверку селекторов через схему CUSTOM_ELEMENTS_SCHEMA.

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}

Очевидно, что при таком подходе Angular не сгенерирует нужную ошибку, когда программист ошибется набирая имя нужного Angular компонента. Второй момент заключается в том, что хотелось бы иметь IDE подсказки во время работы со свойствами веб-компонентов.

Для решения этих проблем можно создать обёртки над веб-компонентами. Например для компонента ссылки может использоваться такая директива:

@Directive({
  selector: `
    ispui-link:not([ngControl]),
    ispui-link:not([formControlName]),
    ispui-link[formControl],
    ispui-link:not([ngFormControl]),
    ispui-link:not([ngModel])
  `,
})
export class ISPUILinkValueAccessor {
  @HostBinding('attr.type') @Input() type: ViewType;
  @HostBinding('attr.size') @Input() size: LinkSize;
  @HostBinding('attr.disabled') @Input() disabled: boolean;
  @HostBinding('attr.target') @Input() target: string;
  @HostBinding('attr.ellipsis') @Input() ellipsis: boolean;
  @HostBinding('attr.color') @Input() color: ThemePalette;
  @HostBinding('attr.href') @Input() href: string;
}

В данном случае :not(что-то) используется шаблонно, так как при использовании веб-компонентов в качестве контролов формы может потребоваться отдельная директива. Например для компонента радиокнопки могут использоваться такие директивы:

@Directive({
  selector: `
    ispui-radio:not([ngControl]),
    ispui-radio:not([formControlName]),
    ispui-radio[formControl],
    ispui-radio:not([ngFormControl]),
    ispui-radio:not([ngModel])
  `,
})
export class ISPUIRadioValueAccessor {}

@Directive({
  selector: `
    ispui-radio[ngControl],
    ispui-radio[formControlName],
    ispui-radio[formControl],
    ispui-radio[ngFormControl],
    ispui-radio[ngModel]`
  ,
  providers: [{ 
    provide: NG_VALUE_ACCESSOR, 
    useExisting: forwardRef(() => ISPUIRadioControlValueAccessor), 
    multi: true
  }],
})
export class ISPUIRadioControlValueAccessor implements ControlValueAccessor {
  @HostListener('change', ['$event.target.value'])
  onChange: CallableFunction = (_: any) => {
    return _;
  };

  @HostListener('blur', ['$event.target'])
  onTouched: CallableFunction = (_: HTMLElement) => {
    return _;
  };

  formCtrlName = '';

  @Input()
  set formControlName(name: string) {
    this.formCtrlName = name;
    this.renderer.setProperty(this.elementRef.nativeElement, 'name', this.formCtrlName);
  }

  get formControlName(): string {
    return this.formCtrlName;
  }

  constructor(private renderer: Renderer2, private elementRef: ElementRef) {}

  writeValue(value: any): void {
    this.renderer.setProperty(this.elementRef.nativeElement, 'checked', value == this.elementRef.nativeElement.value);
  }
  registerOnChange(fn: (_: any) => {}): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: () => {}): void {
    this.onTouched = fn;
  }
}

Похожие записи

Angular dependency injection

Определение Provider (useClass, useValue, useFactory ), Injector. Декоратор @Inject, ключ multi: true

13 ноября 2018 г. в Angular

Angular. Когда не надо отписываться в RxJS?

В async pipe за вас отпишется Angular. Во всех остальных случаях лучше отписываться самостоятельно. Допускается не отписываться в потоках, где будет гарантировано вызван complete.