基本的なTaigaUIコンポーネントをより柔軟にする方法:Angularのコンポーネントコントローラーの概念

Taiga UIコンポーネントライブラリの進化の過程で、いくつかのより複雑なコンポーネントが、その値を他の基本コンポーネントの@Inputに渡すためだけに@Inputを持っていることに気付き始めました。時々、3層でもそのような入れ子があります。





コントローラと呼ばれるいくつかのトリッキーなディレクティブを使用してそれを行いました。彼らは入れ子の問題を完全に解決し、ライブラリの重量を減らしました。





この記事では、この概念とAngularのDI機能のおかげで、すべての入力フィールドに共通の設定システムをどのように編成したかを示します。





以前の「タイガ」のテキストフィールド:コントローラーを使用できる場合の良い例

PrimitiveTextfieldと呼ばれる基本的な入力コンポーネントがあります。





このコンポーネントは、ラッパーを使用したテーマのようなスタイルのネイティブ入力です。Angularフォームでは機能せず、本格的なコントロールを構築するために必要です。 





テキストフィールドの最初のバージョンは非常に単純で、いくつかの複合入力コンポーネントで使用されていました。しかし、すぐにそれはより複雑になり始めました:新しい機能が追加され、コンポーネントの@Inputsの数はますます増えました。 





«» Textfield 17 . :





  • @Input’ , , . , textfield - 17 .





  • @Input’ , . : @Inputs — . 10 , . .





, .





@Input’ , . , , : ( ).





@Input’ , . , . - :





@Directive({
   selector: '[tuiHintContent]'
})
export class TuiHintControllerDirective {
   @Input('tuiHintContent')
   content: PolymorpheusContent = ’’;
 
   @Input('tuiHintDirection')
   direction: TuiDirection = 'bottom-left';
 
   @Input('tuiHintMode')
   mode: TuiHintMode | null = null;
}
      
      



— @Input’ , . “tuiHintContent”, .





. DI . , .





@Input’ OnPush-, @Input’. , , @Input . Controller, :





export abstract class Controller implements OnChanges {
   readonly change$ = new Subject<void>();
 
   ngOnChanges() {
       this.change$.next();
   }
}
      
      



ngOnChanges, . :





@Directive({
   selector: '[tuiHintContent]'
 })
export class TuiHintControllerDirective extends Controller {
    // ...
}

      
      



, change$ . — ChangeDetectorRef, markForCheck change$. , :





constructor(
  @Inject(ChangeDetectorRef) private readonly changeDetectorRef: ChangeDetectorRef,
  @Optional()
  @Inject(TuiHintControllerDirective)
  readonly hintController: TuiHintControllerDirective | null,
) {
  if (!hintController) {
    return;
  }

  hintController.change$.pipe(takeUntil(this.destroy$)).subscribe(() => {
    changeDetectorRef.markForCheck();
  });
}
      
      



. — .





, “tuiHintContent” textfield .





: - @Input’ . .





, : , .





, null, DI- Angular:





constructor(
  @Inject(TUI_HINT_WATCHED_CONTROLLER)
  readonly hintController: TuiHintControllerDirective,
) {}
      
      



. TUI_HINT_WATCHED_CONTROLLER :





export const TUI_HINT_WATCHED_CONTROLLER = new InjectionToken('watched hint controller');
 
export const HINT_CONTROLLER_PROVIDER: Provider = [
   TuiDestroyService,
   {
       provide: TUI_HINT_WATCHED_CONTROLLER,
       deps: [[new Optional(), TuiHintControllerDirective], ChangeDetectorRef, TuiDestroyService],
       useFactory: hintWatchedControllerFactory,
   },
];
 
export function hintWatchedControllerFactory(
   controller: TuiHintControllerDirective | null,
   changeDetectorRef: ChangeDetectorRef,
   destroy$: Observable<void>,
): Controller {
  if (!controller) {
     return new TuiHintControllerDirective();
  }
 
   controller.change$.pipe(takeUntil(destroy$)).subscribe(() => {
      changeDetectorRef.markForCheck();
   });
 
   return controller;
}
      
      



, HINT_CONTROLLER_PROVIDER. “providers” , deps ChangeDetectorRef TuiDestroyService. , ngOnDestroy , ( , ).





:





@Component({
   //...
   providers: [HINT_CONTROLLER_PROVIDER],
})
export class TuiPrimitiveTextfieldComponent {
   constructor(
       //...
       @Inject(TUI_HINT_WATCHED_CONTROLLER)
       readonly hintController: TuiHintControllerDirective,
   ) {}
}
      
      



, . @Input’ .





: DI , .





: hintWatchedControllerFactory , . , .





?

. @Input’ , . : , — , . , . DI , .





-, , . — .





DI , , , . , , DI, API.








All Articles