5 tips to improve your Angular skills

This summer, Roma and I launched a series of tweets with helpful Angular tips and tricks. This initiative was warmly welcomed by the community and I decided to write a summary article. Here are my 5 recommendations to share with developers. These tips will be backed up by specific examples from my twitter . They will help you improve your skills, or at least give you a couple of practical tips.

1. Understand the change checking mechanism

There are many great in-depth articles on the internet about checking changes in Angular. For example, this one. So let's just brush up on the basics and get to the tips.

The basics

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