Angular πŸ‡ΊπŸ‡¦ - practical notes
1.63K subscribers
1.6K photos
1 file
532 links
Angular - practical notes

This group is for posting practical notes for Angular developers. Mostly all posts are for quick implementation https://t.me/angular_practical_notes (Commenting on posts only in ENG and UA langs here). Welcome!
Download Telegram
πŸ“„ Custom RxJS operators 1/7: "Π‘ache operator"

#angular #rxjs #cache

Often an RxJS stream represents an http request β€” a stream that emits a single value and completes. Performance-wise, it is a good practice to cache a response from a server for a certain amount of time. The built-in share operator can be configured to provide a caching mechanism with a Time To Live (TTL) for a response.

operator

import { ... } from 'rxjs';

function cache<T>(ttl: number = Infinity): MonoTypeOperatorFunction<T> {
return share({
connector: () => new ReplaySubject(1),
resetOnComplete: () => timer(ttl),
});
}
πŸ”₯1
πŸ“„ Custom RxJS operators 2/7: "filterNil operator"

#angular #rxjs #filterNil

It is quite common to filter out null and undefined values from the resulting stream. Obviously, the built-in filter operator is the right tool to do the job, however in order to ensure proper type narrowing, the predicate function has to be a user-defined type guard. Therefore, it makes sense to encapsulate it into a custom RxJS operator.

operator

import { filter, OperatorFunction } from 'rxjs';

function filterNil<T>(): OperatorFunction<T, NonNullable<T>> {
return filter((v): v is NonNullable<T> => v != undefined);
}
πŸ‘1πŸ”₯1
πŸ“„ Custom RxJS operators 3/7: "firstMatching operator"

#angular #rxjs #firstMatching

For most use cases, the task can be accomplished at least in two ways. However, there is a subtle difference when the source stream completes without having emitted a value that meets requirements. The detailed explanation can be found in one of my RxJS hints. The operator in question allows to choose a given strategy to handle such scenario based on the required argument.

operator

import { of, MonoTypeOperatorFunction, pipe, first, filter, take } from 'rxjs';

function firstMatching<T>(
predicateFn: (v: T) => boolean,
required: boolean = false
): MonoTypeOperatorFunction<T> {
return required ? first(predicateFn) : pipe(filter(predicateFn), take(1));
}
πŸ”₯1
πŸ“„ Scrolling to a Specific QueryList Element

#angular #QueryList

list.component.html
<div 
*ngFor="let item of items; let i = index"
#myElement
>
<!-- Content of each item -->
</div>

<button
*ngFor="let item of items; let i = index"
(click)="scrollToElement(i)"
>
Scroll to Element {{i}}
</button>

list.component.ts


@Component({...})
export class MyComponent {
@ViewChildren('myElement')
public elements: QueryList<ElementRef>;
public items = [...]; // Your array of items

scrollToElement(index: number): void {
const list = this.elements.toArray();
const element = list[index].nativeElement;
element.scrollIntoView({ behavior: 'smooth' });
}
}


βœ… Article link: https://medium.com/@garfunkel61/scrolling-to-a-specific-element-in-angular-ngfor-list-navigating-querylists-with-ease-bba15735b41b
πŸ‘2πŸ”₯2
πŸ“„ Custom RxJS operators 4/7: "withLifecycle operator"

#angular #rxjs #withLifecycle

Debugging RxJS code can be a challenge. Fortunately, the built-in tap operator provides a way to track lifecycle events for a stream.

operator

export function withLifecycle<T>(
streamId: string
): MonoTypeOperatorFunction<T> {
return tap({
subscribe: () => console.log(`[${streamId}]: subscribed`),
unsubscribe: () => console.log(`[${streamId}]: unsubscribed`),
finalize: () => console.log(`[${streamId}]: emitted final value`),
});
}
❀1
πŸ€“ Code Review Checklist for Angular Developers

#angular #review

Service Injection πŸ“›:
It’s recommended to check and review the dependency injection to components and recommended to use TypeScript’s access modifiers (private, public, etc.)
πŸ”₯1
πŸ“„ Custom RxJS operators 5/7: "pollWhile operator"

#angular #rxjs #pollWhile

The good old polling mechanism is still quite relevant in applications which rely on data that changes over a period of time. Let’s consider a scenario when an application has to make http requests in a certain time interval until an http response body fulfils a given condition, e.g. analysis status is set to completed. The built-in repeat operator combined with a limiting operator allows to accomplish the goal with a few lines of code.

operator

interface PollWhileConfig<T> {
predicateFn: (v: T) => boolean;
delay: number;
count?: number;
lastOnly?: boolean;
}

function pollWhile<T>({
predicateFn,
delay,
count = Infinity,
lastOnly = false,
}: PollWhileConfig<T>): MonoTypeOperatorFunction<T> {
const limiter = lastOnly
? pipe(
filter((v: T) => !predicateFn(v)),
take(1)
)
: takeWhile(predicateFn, true);

return pipe(repeat({ delay, count }), limiter);
}
πŸ”₯1
πŸ€“ Code Review Checklist for Angular Developers

#angular #review

Observable Cleanup πŸ“›:
Sometimes, we find ourselves fetching data with observables, doing the subscribe and cleanup in ngOnDestroy
I would recommend using the async pipe to simplify the component code.
πŸ“„ Custom RxJS operators 6/7: "retryForStatus operator"

#angular #rxjs #retryForStatus

The built-in retry operator allows to resubscribe to the source stream once an error has been thrown. It is possible to make a decision whether or not to retry based on the information contained in the error object. In turn, when it comes to streams that represent an http request, the operator can be configured to call an API again only for certain response statuses.

operator

interface ErrorWithStatus extends Error {
status: number;
}

interface RetryForStatusConfig {
retryableStatuses: number[];
delay: number;
count?: number;
}

function retryForStatus<T>({
retryableStatuses,
delay,
count = Infinity,
}: RetryForStatusConfig): MonoTypeOperatorFunction<T> {
return retry({
count,
delay: (err: ErrorWithStatus, retryCount) =>
retryableStatuses.includes(err.status)
? timer(retryCount * delay)
: throwError(() => err),
});
}
πŸ”₯1
πŸ“„ Custom RxJS operators 7/7: "toLatestFrom operator"

#angular #rxjs #toLatestFrom

The built-in withLatestFrom operator allows to add data from supplementary streams to the source stream. However, sometimes the source stream is just a trigger to perform a certain action for which data from supplementary streams is needed. As a result, the array of elements in the output stream contains a dummy first element. A better solution is to neglect the value from the trigger.

operator

function toLatestFrom<T, D1>(d1$: ObservableInput<D1>): OperatorFunction<T, D1>;
function toLatestFrom<T, D1, D extends unknown[]>(
d1$: ObservableInput<D1>,
...data$: [...ObservableInputTuple<D>]
): OperatorFunction<T, [D1, ...D]>;

function toLatestFrom<D1, D extends unknown[]>(
d1$: ObservableInput<D1>,
...data$: [...ObservableInputTuple<D>]
) {
return pipe(
withLatestFrom(d1$, ...data$),
map(([_, ...data]) => (data.length === 1 ? data[0] : data))
);
}
πŸ”₯1
πŸ€“ Code Review Checklist: Magic Values πŸ“› πŸ’£

#angular #review

It is recommended to use constants or enums to make the code more readable and maintainable and avoid magic values
πŸ”₯1
πŸ€“ Code Review Checklist: Consider Extracting logic to Services πŸ“›

#angular #review

If we find out that part of the component logic can be reused in many places, we can optimize that by extracting this logic into services for better code organization and reusability
πŸ€“ Code Review Checklist: Hard-Coded Styles πŸ“›

#angular #review

Be careful when using inline styles like style="margin-top: 12px".it can be hard to maintain
πŸ“„ Change Detection in Angular

#angular #signals #cdr

4. Local Change Detection 🚩Angular 17

@Component({
selector: 'app-timer',
template: `<span>Last Updated: {{ lastUpdateInSeconds() | number:'1.0-0' }} Seconds</span> {{ logCd() }}`,
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [DatePipe, DecimalPipe, AsyncPipe]
})
export class TimerComponent {
@Input() lastUpdate = new Date();
lastUpdateInSeconds = signal(0)
constructor() {
setInterval(() => {
this.lastUpdateInSeconds.set((new Date().getTime() - this.lastUpdate.getTime()) / 1_000);
}, 1000);
}

logCd() {
console.log('log from timer');
}
}



βœ… Article link: https://medium.com/ngconf/local-change-detection-in-angular-410d82b38664
πŸ”₯1πŸ₯°1