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
. , ! .
. , . !