Como tornamos os componentes bĂĄsicos da IU do Taiga mais flexĂ­veis: o conceito de controladores de componentes no Angular

No curso da evolução de nossa biblioteca de componentes de IU da Taiga, começamos a notar que alguns componentes mais complexos tĂȘm @Input apenas para passar seu valor para @Input de nosso outro componente bĂĄsico dentro deles. Às vezes, hĂĄ esse aninhamento mesmo em trĂȘs camadas.





Fizemos isso com algumas diretivas complicadas chamadas controladores. Eles resolveram completamente o problema de aninhamento e reduziram o peso da biblioteca.





Neste artigo, vou mostrar como organizamos um sistema comum de configuraçÔes para todos os campos de entrada, graças a esse conceito e aos recursos de DI no Angular.





Campo de texto no antigo "Taiga": um bom caso quando vocĂȘ pode usar controladores

Temos um componente bĂĄsico de entrada chamado Campo de Texto Primitivo.





Este componente Ă© uma entrada nativa com o estilo do nosso tema com um invĂłlucro para ele. Ele nĂŁo funciona com formulĂĄrios angulares e Ă© necessĂĄrio para construir controles completos. 





A primeira versĂŁo do textfield era bastante simples e foi usada em vĂĄrios componentes de entrada compostos. Mas logo começou a ficar mais complicado: novos recursos foram adicionados e o nĂșmero de @Inputs para o componente cresceu cada vez mais. 





«» 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