Valentin Kononov
Full Stack Developer, Speaker
https://slides.com/valentinkononov/
angular-bad-practices
https://github.com/valentinkononov/
angular-bad-practices
Book
Articles
Watch YouTube video
Download boilerplate
Start coding
Stackoverflow
RxJs Pipable Operators
import { map, take } from 'rxjs/operators';
...
this.service.getData()
.pipe(
map(value => value.item)
take(1)
);
...
// to have build optimization
npm install @angular-devkit/build-optimizer
// to analyze build statistics
npm install webpack-bundle-analyzer --save-dev
BEFORE
AFTER
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}`);
}
call child component function
@Component({
template: `<ngx-child #child />
<button (click)="child.expand()">Expand</button>
`})
export class ParentComponent {
@ViewChild('child') child: ChildComponent;
}
@Component({ template: `
<span *ngIf="expanded">I am expanded</span>
`})
export class ChildComponent {
@Input() expanded: boolean = false;
public expand(): void {
this.expanded = true;
}
}
@Component({
template: `<ngx-child [expanded]="childExpanded"/>
<button (click)="expand()">Expand</button>
`})
export class ParentComponent {
childExpanded: boolean = false;
public expand(): void { this.childExpanded = true;}
}
@Component({ template: `
<span *ngIf="expanded">I am expanded</span>`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChildComponent {
@Input() expanded: boolean = false;
}
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');
}
@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');
}
}
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">
<app-iterate-item [data]="item">
</app-iterate-item>
</div>
<button type="button" (click)="sortCollection()">
Sort Collection</button>
<div *ngFor="let item of items; trackBy: trackByFn">
<app-iterate-item [data]="item">
</app-iterate-item>
</div>
trackByFn(index: number, item: ItemData) {
return item.id;
}
<button type="button" (click)="sortCollection()">
Sort Collection</button>
<div *ngFor="let item of items; trackBy: trackByFn">
<app-expandable
[label]="getLabel(item)" [expanded]="false">
<app-iterate-item [data]="item"></app-iterate-item>
</app-expandable>
</div>
trackByFn(index: number, item: ItemData) {
return item.id;
}
getLabel(item: ItemData): string {
console.log(`Item ${item.id} label call`);
return `Item ${item.id}`;
}
<button type="button" (click)="sortCollection()">
Sort Collection</button>
<div *ngFor="let item of items; trackBy: trackByFn">
<app-expandable
[label]="getLabel(item)" [expanded]="false">
<app-iterate-item [data]="item"></app-iterate-item>
</app-expandable>
</div>
trackByFn(index: number, item: ItemData) {
return item.id;
}
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: number, args?: any): any {
console.log(`Item ${value} label call`);
return `Item ${value}`;
}
}
<div>
<button type="button" (click)="sortCollection()">
Sort Collection</button>
<div *ngFor="let item of items; trackBy: trackByFn">
<app-expandable [label]="item.id | label"
[expanded]="false">
<app-iterate-item [data]="item"></app-iterate-item>
</app-expandable>
</div>
</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