Angular discrete notes

Anh-Thi Dinh

VScode extensions

For “Go to definition”, use Angular Language Service (ALS).
(Personal exp) Using Angular 13 and this function is not working → instead of modify tsconfig.json where angularCompilerOptions { "strictTemplates": true }, we can go to ALS’s settings and tick “Force Strict Templates

Angular linker, ngcc, Ivy

Prevent destroy / CanDeactivate

Ý tưởng là muốn warning trước khi destroy → nếu không thoả thì ko cho destroy! (This SO)
1// Don't wanna add extra DOM
2<ng-container *ngIf="store.products">
3  <p>Content</p>
4</ng-container>

contenteditable with two-way binding

👉  StackBlitz (for the fixed caret version)

(select) event

Not like input element who has an (select) event. On a div.contenteditable, we don’t have this kind of event.
1<div (click)="checkSelection()" (keyup)="checkSelection()"></div>
1checkSelection() {
2	var selection = window.getSelection();
3  if (selection.toString().length !== 0) {
4    console.log('you selected: ', selection.toString());
5  }
6}

Should always use SimpleChanges

👉 Source.
1@Input() myFirstInputParameter: string;
2
3ngOnChanges(changes: SimpleChanges) {
4  if (changes.myFirstInputParameter && changes.myFirstInputParameter.currentValue) {
5    this.doSomething(this.myFirstInputParameter)
6  }
7}
Changes get called just because of EACH input.

Prevent changing route if there are changes not saved

Idea: In each component, cereate a method called isDirty() → pass it in a guard which extends the CanDeactivate → pass it in the routing modules. Whenever a component return false for isDirty, it means there are some changes not saved.

CSS

:host with ViewEncapsulation.None

Note:host doesn't work with ViewEncapsulation.None!
1/* With ViewEncapsulation.Emulated, use :host selector */
2:host {
3  color: red;
4}
5
6/* With ViewEncapsulation.None, use component selector */
7app-child-encapsulation-none {
8  color: green;
9}

Parent and children

Different ways to add CSS classes

1<div
2	class="default-class"
3	[class.class-A]="condition"
4>Content</div>
1<div
2	class="default-class"
3	[className]="'class-a'"
4>Content</div>
1<div
2	class="default-class"
3	[className]="condition ? 'class-a' : 'other-class'"
4>Content</div>
1<div
2  class="default-class"
3  [ngClass]="{
4  	'class-a': condition-a,
5    'class-b': condition-b
6  }"
7>Content</div>

ngOnInit child with base component

1// Base component
2class BaseComponent implements OnInit {
3	ngOnInit() {
4		// codes from ngOnInit of Base
5	}
6}
1// Child
2class ChildComponent implements OnInit {
3	ngOnInit() {
4		super.ngOnInit(); // without this, the codes in Base's ngOnInit won't be called
5	}
6}

@Input() but child changes => parent changes?

1<app-child [inputChild]="inputParent"></app-child>
Nếu trong app-child, cái inputChild có thay đổi, tức là, ví dụ
1// app-child
2this.inputChild = somethingElse // This will change also the inputParent 
Bởi vì khi assign value giống trên, tức ta assign reference của nó!
👉 Cái này là pass by reference
Còn cái mình hiểu lúc đọc doc của angular là pass by value và dùng decor là @Input()@Output()

OnPush change detection

👇 Idea from this helpful video
  • changeDetection: ChangeDetectionStrategy.OnPush = auto change detection is deactivated.
  • changeDetection: ChangeDetectionStrategy.Default (or CheckAlways) = change detection is automatic.
1<!-- app.component.html -->
2<button (click)="onEditCourse1()">Button 1</button>
3<button (click)="onEditCourse2()">Button 2</button>
4<div>
5  <course *ngFor="let course of courses" [course]="course"></course>
6</div>
1// app.component.ts
2onEditCourse1() // -> change property of the input
3onEditCourse2() // -> change the input itself
1<!-- course.component.html -->
2<h1>{{ course.title }}</h1>
3<input #courseTitle [value]="course.title" (keyup)="onTitleChanged(courseTitle.value)">
1// course.component.ts
2@Component({
3  // with or without
4  changeDetection: ChangeDetectionStrategy.OnPush
5})
6export class AppComponent {
7  @Input() course
8  constructor(){}
9
10  onTitleChanged(title: string) {
11    this.course.title = title;
12  }
13}
  • Change value in the <input>, the value in <h1> changes too (with or without the changeDetection).
  • Clicking on "Button 1" (on app), nothing changes when ChangeDetectionStrategy.OnPush on (But the title changes when we turn this off)! 👈 We mutate a property of @Input() course (but the input is not changed!)
  • Clicking on "Button 2", there are changes! 👈 We change @Input() course object.

Call children’s methods from parent

(Ideta note)

ngFor and ngIf the same time?

It’s impossible if we wanna use them in the same div. Use ng-container instead,
1<ng-container *ngIf="show">
2  <div *ngFor="let thing of stuff">
3    {{log(thing)}}
4    <span>{{thing.name}}</span>
5  </div>
6</ng-container>

ng-template, ng-container and else in html

1<p *ngIf="serverCreated; else noServer">Content</p>
2<ng-template #noServer>
3	<p>Other text</p>
4<ng-template>
1// Or both
2<ng-container *ngIf="store.products" else #noServer>
3  <p>Content</p>
4</ng-container>
5
6<ng-template #noServer>
7	<p>Other text</p>
8<ng-template>

Reuse an HTML block using ng-template

1<ng-template #MsgRef >
2  {{ mycontent }}
3</ng-template>
4
5<ng-template [ngTemplateOutlet]="MsgRef"></ng-template> //reuse any number of times

Safer way to do this.ref.nativeElement.innerHTML = newText

👉 Reference.
1import { ..., AfterViewInit, ElementRef, ViewChild, Renderer2, ... } from '@angular/core';
2...
3export class AdvertisementDirective implements AfterViewInit {
4  @ViewChild('templateRefName') el: ElementRef;
5
6  constructor(private renderer: Renderer2) {}
7
8  someFunction() {
9    this.renderer.setProperty(this.el.nativeElement, 'innerHTML', '<p>Hello World<p>');
10  }
11}

ngFor and trackBy

Without trackBy ⇒ whole list changes. With trackBy ⇒ only the current modif item changes (the comparitor is the function given to trackBy ← angular will compare the previously returned value and currently returned value of this function)
1array = [
2    { "id": 1, "name": "bill" },
3    { "id": 2, "name": "bob" },
4    { "id": 3, "name": "billy" }
5]
6
7const identify = (index: number, item: any) => item;
1<div *ngFor="let e of array; trackBy: identify;">
2   {{e.id}} - {{e.name}}
3</div>
1// app.component.ts
2onEditCourse1() { // <-- change a property
3  this.courses[0].title = "New title";
4}
5onEditCourse2() { // <- change entire object
6  const newCourse = { title: "New title };
7  this.courses[0] = newCourse;
8}
Loading comments...