Valentin Kononov
Full Stack Developer, Speaker
what to avoid
strict settings of angular and compiler
ng new MyAwesomeAngularApp
regular TS settings
regular linter settings
default Angular Compiler settings
any is allowed!
ng new MyAwesomeAngularApp --strict
{
"compilerOptions": {
...
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
},
"angularCompilerOptions": {
"strictInjectionParameters": true,
"strictTemplates": true
}
}
"schematics": {
"@schematics/angular:application": {
"strict": true
}
},
...
"angularCompilerOptions": {
"strictInjectionParameters": true,
"strictTemplates": true
}
"fullTemplateTypeCheck": true,
"trace": true,
"preserveWhitespaces": true
"no-any": true
"promise-function-async": true
"await-promise": true
"no-async-without-await": true
"no-debugger": true
"no-duplicate-variable": true
"no-var-keyword": true
"max-file-line-count": 140
"no-duplicate-imports": true
...
ANY shall not pass
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}`);
}
export class CustomLineGeometry extends LineGeometry {
constructor(geometry?: BufferGeometry) {
super();
(this as any)._maxInstanceCount = 100;
}
}
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.service.getData() | async }}
</span>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SubscriptionComponent {
constructor(private service: Service) {}
}
@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">
Id: {{ data.id }}
Count: {{ data.count }}
</ng-container>
id$: Observable<number>;
count$: Observable<number>;
<div>
Id: {{ id$ | async }}
Count: {{ 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
observer2
Subject
Source
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>
<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
improved Bad Practices talk for ngHeidelberg meetup, July 2020