5 consejos para mejorar tus habilidades angulares

Este verano, Roma y yo lanzamos una serie de tweets con consejos y trucos útiles sobre Angular. Esta iniciativa fue muy bien recibida por la comunidad y decidí escribir un artículo de resumen. Aquí están mis 5 recomendaciones para compartir con los desarrolladores. Estos consejos estarán respaldados por ejemplos específicos de mi twitter . Te ayudarán a mejorar tus habilidades, o al menos te darán un par de consejos prácticos.

1. Comprender el mecanismo de verificación de cambios

Hay muchos artículos excelentes en profundidad en Internet sobre cómo verificar cambios en Angular. Por ejemplo, este. Así que repasemos los conceptos básicos y vayamos a los consejos.

Los basicos

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