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;
}
}