5 conseils pour améliorer vos compétences angulaires

Cet été, Roma et moi avons lancé une série de tweets contenant des trucs et astuces Angular utiles. Cette initiative a été chaleureusement accueillie par la communauté et j'ai décidé d'écrire un article de synthÚse. Voici mes 5 recommandations à partager avec les développeurs. Ces conseils seront étayés par des exemples spécifiques de mon twitter . Ils vous aideront à améliorer vos compétences ou au moins vous donneront quelques conseils pratiques.

1. Comprendre le mécanisme de vérification des modifications

Il existe de nombreux articles approfondis sur Internet sur la vérification des changements dans Angular. Par exemple, celui-ci. Alors, revoyons simplement les bases et arrivons aux conseils.

Les bases

Angular : Default OnPush. tick . Zone.js, . view , .

Default vs OnPush

, Default. , , OnPush. . , OnPush .

@HostListener Angular OnPush. , RxJS? ChangeDetectorRef markForCheck(), . async , . . 

:

<div *ngIf="stream$ | async as result">
    â€Š
</div>

, falsy-? ngIf . , :

@Directive({
  selector: "[ngLet]"
})
export class LetDirective<T> {
  @Input()
  ngLet: T;

  constructor(
    @Inject(ViewContainerRef) container: ViewContainerRef,
    @Inject(TemplateRef) templateRef: TemplateRef<LetContext<T>>
  ) {
    container.createEmbeddedView(templateRef, new LetContext<T>(this));
  }
}

NgZone

OnPush, . NgZone .runOutsideAngular(). . Default . , mousemove scroll. RxJS- : — , — , :

class ZonefreeOperator<T> implements Operator<T, T> {
  constructor(private readonly zone: NgZone) {}

  call(observer: Observer<T>, source: Observable<T>): TeardownLogic {
    return this.zone.runOutsideAngular(
      () => source.subscribe(observer)
    );
  }
}

export function zonefull<T>(zone: NgZone): MonoTypeOperatorFunction<T> {
  return map(value => zone.run(() => value));
}

export function zonefree<T>(zone: NgZone): MonoTypeOperatorFunction<T> {
  return source => source.lift(new ZonefreeOperator(zone));
}

, @HostListener, — EventManagerPlugin. open-source- ng-event-plugins. . .

2. RxJS

RxJS — . , . , — 

— . , , :

, , . CSS RxJS:

@Directive({
  selector: "[sticky]",
  providers: [DestroyService]
})
export class StickyDirective {
  constructor(
    @Inject(DestroyService) destroy$: Observable<void>,
    @Inject(WINDOW) windowRef: Window,
    renderer: Renderer2,
    { nativeElement }: ElementRef<HTMLElement>
  ) {
    fromEvent(windowRef, "scroll")
      .pipe(
        map(() => windowRef.scrollY),
        pairwise(),
        map(([prev, next]) => next < THRESHOLD || prev > next),
        distinctUntilChanged(),
        startWith(true),
        takeUntil(destroy$)
      )
      .subscribe(stuck => {
        renderer.setAttribute(
          nativeElement, 
          "data-stuck", 
          String(stuck)
        );
      });
  }
}

RxJS Angular- . RxJS , . , , .

, . , RxJS, — . - . ( ).

3. TypeScript

Angular- TypeScript. , , . strict: true. . cannot read property of null undefined is not a function.

TypeScript — , , , . , API. . RxJS- fromEvent:

//     currentTarget
export type EventWith<
  E extends Event,
  T extends FromEventTarget<E>
> = E & {
  readonly currentTarget: T;
};

//   fromEvent
export function typedFromEvent<
  E extends keyof GlobalEventHandlersEventMap,
  T extends FromEventTarget<EventWith<GlobalEventHandlersEventMap[E], T>>
>(
  target: T,
  event: E,
  options: AddEventListenerOptions = {},
): Observable<EventWith<GlobalEventHandlersEventMap[E], T>> {
  return fromEvent(target, event, options);
}

, , currentTarget — , .

API, , , . , .

TypeScript. , . : any. , unknown.

TypeScript, . . , , , : , , — number. TypeScript , runtime :

export function assert<T, K extends keyof T>(
  assertion: (input: T[K]) => boolean,
  messsage: string
): PropertyDecorator {
  return (target, key) => {
    Object.defineProperty(target, key, {
      set(this: T, initialValue: T[K]) {
        let currentValue = initialValue;

        Object.defineProperty(this, key, {
          get(): T[K] {
            return currentValue;
          },
          set(this: T, value: T[K]) {
            console.assert(assertion(value), messsage);
            currentValue = value;
          }
        });
      }
    });
  };
}

, super()? Angular , :

— . Web Audio API Angular, . .

4. Dependency Injection.

DI — , Angular . , . .

, DI, .

RxJS

, RxJS. , , , . Angular :

@Injectable()
export class DestroyService extends Subject<void> implements OnDestroy {
    ngOnDestroy() {
        this.next();
        this.complete();
    }
}

DI. requestAnimationFrame . . , :

DI — . window navigator — Angular Universal . , . . . WINDOW DOCUMENT:

export const WINDOW = new InjectionToken<Window>(
  'An abstraction over global window object',
  {
    factory: () => {
      const {defaultView} = inject(DOCUMENT);

      if (!defaultView) {
        throw new Error('Window is not available');
      }

      return defaultView;
    },
  },
);

, open-source-, . Angular Universal - . , , - .

. DI . .

5. ,

Angular . . . : . , , .

— « »? ngOnChanges . -, , , - . 

— . - , .

— , . . , . , — , .

, , .

Template reference variables

Angular @ViewChild. , :

<input #input>
<button (click)="onClick(input)">Focus</button>

template reference variable , . .

, DOM-, ? @ViewChild(MyComponent, {read: ElementRef}), , exportAs:

@Directive({
    selector: '[element]',
    exportAs: 'elementRef',
})
export class ElementDirective<T extends Element> extends ElementRef<T> {
    constructor(@Inject(ElementRef) {nativeElement}: ElementRef<T>) {
        super(nativeElement);
    }
}

ComponentFactoryResolver . , ngComponentOutlet? . — , Dependency Injection. ngComponentOutlet Injector, .

, , . . .

, . open-source- ng-polymorpheus. , , ngTemplateOutlet, ngContentOutlet . , ! .

. , . !




All Articles