Valentin Kononov
Full Stack Developer, Speaker
https://slides.com/valentinkononov/angular-bad-practices-gorki
https://github.com/valentinkononov/
angular-bad-practices
QUESTIONS
sli.do
#devfest_volga
Book
Articles
Watch YouTube video
Download boilerplate
Start coding
Stackoverflow
usage of Any in typescript
public ngOnInit(): void {
const movie = {
name: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: any): void {
this.toaster.show(`Today's movie is: ${item.name}`);
}
public ngOnInit(): void {
const movie = {
title: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: any): void {
this.toaster.show(`Today's movie is: ${item.name}`);
}
interface Movie {
name: string;
}
public ngOnInit(): void {
const movie: Movie = {
name: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: Movie): void {
this.toaster.show(`Today's movie is: ${item.name}`);
}
interface Movie {
title: string;
}
public ngOnInit(): void {
const movie: Movie = {
name: 'Game of Thrones',
};
this.showToast(movie);
}
private showToast(item: Movie): void {
this.toaster.show(`Today's movie is: ${item.name}`);
}
ngOnInit() {
console.log('Initialized');
this.timerService.initTimer()
.subscribe(x => {
this.result = x;
console.log(x);
});
}
ngOnDestroy() {
console.log('Destroyed');
}
subscription: Subscription;
ngOnInit() {
console.log('Initialized');
this.subscription = this.timerService.initTimer()
.subscribe(x => {
this.result = x;
console.log(x);
});
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
console.log('Destroyed');
}
alive = true;
ngOnInit() {
console.log('Initialized');
this.timerService.initTimer()
.pipe(takeWhile(() => {
console.log('check'); return this.alive; }))
.subscribe(x => {
this.result = x;
console.log(x);
});
}
ngOnDestroy() {
this.alive = false;
console.log('Destroyed');
}
unsubscribe: Subject<void> = new Subject();
ngOnInit() {
console.log('Initialized');
this.timerService.initTimer()
.pipe(takeUntil(this.unsubscribe))
.subscribe(x => {
this.result = x;
console.log(x);
});
}
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
console.log('Destroyed');
}
import {untilDestroyed} from "ngx-take-until-destroy";
ngOnInit() {
console.log('Initialized');
this.timerService.initTimer()
.pipe(untilDestroyed(this))
.subscribe(x => {
this.result = x;
console.log(x);
});
}
ngOnDestroy() {
console.log('Destroyed');
}
@Component({
template: `<span>{{ this.result$ | async }}</span>`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SubscriptionComponent
implements OnInit, OnDestroy {
result$: Observable<number>;
ngOnInit() {
console.log('Initialized');
this.result$ = this.timerService.initTimer()
.pipe(tap(x => { console.log(x); }));
}
ngOnDestroy() {
console.log('Destroyed');
}
}
<ng-container *ngIf="{
id: id$ | async,
count: count$ | async
} as data">
Subscription result:
{{ data.id }} | {{ data.count }}
</ng-container>
id$: Observable<number>;
count$: Observable<number>;
<div>
Subscription result:
{{ id$ | async }} | {{ count$ | async }}
</div>
id$: Observable<number>;
count$: Observable<number>;
ngOnInit() {
console.log('Initialized');
this.result$ = this.timerService
.initTimerForObject()
.pipe(tap(x => {
console.log(`Timer: ${x}`);
}));
this.id$ = this.result$.pipe(map(x => x.id));
this.count$ = this.result$.pipe(map(x => x.count));
}
<div>
Subscription result:
{{ id$ | async }} | {{ count$ | async }}
</div>
id$: Observable<number>;
count$: Observable<number>;
ngOnInit() {
this.result$ = this.timerService
.initTimerForObject()
.pipe(share());
this.id$ = this.result$.pipe(map(x => x.id));
this.count$ = this.result$.pipe(map(x => x.count));
}
observer1
observer1
Subject
Source
<div>{{ id$ | async }} | {{ count$ | async }}</div>
id$: Observable<number>;
count$: Observable<number>;
ngOnInit() {
this.result$ = this.timerService
.initTimerForObject()
.pipe(
share(),
);
this.id$ = this.result$.pipe(map(x => x.id));
this.count$ = this.result$.pipe(map(x => x.count));
}
<div>{{ id$ | async }} | {{ count$ | async }}</div>
id$: Observable<number>;
count$: Observable<number>;
ngOnInit() {
this.result$ = this.timerService
.initTimerForObject()
.pipe(
share(),
catchError((error: any) => {
console.log(error);
return of({ id: 0, count: 0 });
}),
);
this.id$ = this.result$.pipe(map(x => x.id));
this.count$ = this.result$.pipe(map(x => x.count));
}
rxjs-no-ignored-subscription
rxjs-no-ignored-subscribe
rxjs-no-ignored-observable
rxjs-no-ignored-error
rxjs-no-nested-subscribe
rxjs-no-unsafe-takeuntil
rxjs-prefer-async-pipe
https://www.nmpjs.com/package/rxjs-tslint-rules
<button type="button" (click)="sortCollection()">
Sort Collection</button>
<div *ngFor="let item of items; trackBy: trackByFn">
{{ getLabel(item) }}
</div>
getLabel(item: ItemData): string {
console.log(`Item ${item.id} label call`);
return `Item ${item.id}`;
}
@Pipe({ name: 'label'})
export class LabelPipe implements PipeTransform {
transform(value: Item, args?: any): string {
console.log(`Item ${value} label call`);
return `Item ${value.id}`;
}
}
<button type="button" (click)="sortCollection()">
Sort Collection</button>
<div *ngFor="let item of items; trackBy: trackByFn">
{{ item | label }}
</div>
items: ItemData[] = [{
parentId: 1,
name: 'Game Of Thrones',
},
{
parentId: 2,
name: 'The Lord of the Rings',
},
{
parentId: 3,
name: 'Big Bang Theory - Season 1',
},
{
parentId: 3,
name: 'Big Bang Theory - Season 2',
}];
By Valentin Kononov
Update bad practices version for Gorky Dev Fest 2019, November 16