Angular discrete notes

Anh-Thi Dinh
Error: ExpressionChangedAfterItHasBeenCheckedError: Previous value: 'ng-untouched: true'. Current value: 'ng-untouched: false’
  • Đến từ component có ChangeDetectorRef
  • (Ideta) app-input-text dùng 2 cái focus input 1 lúc trong view → conflict → có tình trạng ng-untouched chuyển từ true sang false là vậy!
| async auto unsubribe (source, official doc)
async pipes are automatically unsubscribing from the observables they're fed with
Reactive form array (of object) có thể không return value via this.formArr.value mà phải dùng this.formArr.getRawValue() dù cho ko có control nào disabled cả.
❓Chưa hiểu hoàn toàn.
Cái getRawValue() return value của control cụ thể. Ví dụ array of object {name: string, id: number} ← array 4 cái, sửa name của cái index 3 ← cái getRawValue() cho ra giá trị của từng value của control nhỏ nhất, tức formArr.controls.3.controls.name.value. Tất cả các values chỗ khác đều không update giá trị mới nhất!
Nếu chắc chắn trong form ko có control nào disabled thì dùng getRawValue()
Angular routing and pathMatch
Read this SO answer (wonderful and complete explanation, should read). A pathMatch: full configuration is basically saying this:
Ignore my children and only match me (you = url in the bar, me = configured path in the file). If I am not able to match all of the remaining URL segments myself, then move on.
Nếu dùng redirectTo thì chỉ cần match 1 phần url nó cũng redirect. Để tránh tình trạng này, ta thường dùng pathMatch: full để chắc chắn rằng toàn bộ url phải match thì mới trigger cái redirectTo.
getRawValue()
  • this.form.value chỉ return value của mấy controls mà ko disabled.
  • this.form.getRawValue() reutrn value của tất cả các controls cho dù disabled hay không.
Auto scroll to the bottom of a div when adding a new child to an array
Why do people say that Angular is more complex than React?
Create a BaseComponent
A “hack” is to add @Directive() before the class.
In case prettier isn’t working with VSCode
Open settings JSON file,
1"editor.defaultFormatter": "esbenp.prettier-vscode"
Or for a specific file type,
1"[html]": {
2    "editor.defaultFormatter": "esbenp.prettier-vscode"
3}
Angular change detector wont trigger on array mutations
It may leads to some error, for example, cdkVirtualFor doesn't detect changes in array.
1// Instead of this (delete an item from array)
2delete(i: number) {
3  this.items.splice(i,1);
4}
5
6// Use this
7delete(i: number) {
8  this.items = this.items.filter((a, index) => index !== i);
9}
1// If above option doesn't work
2// Instead of
3this.items = items;
4// do
5this.items = [...items];
Difference between detectChanges and markForCheck
Check this SO.
  • detectChanges() : It means, if there is a case where any thing inside your model (your class) has changed but it hasn't reflected the view, you might need to notify Angular to detect those changes (detect local changes) and update the view.
  • markForCheck() : This is mostly needed when the ChangeDetectionStrategy of your component is OnPush.
Lazy load for list in Angular
We use ScrollingModule from @angular/cdk/scrolling.
Note that, for elements of differing sizes, we can use autosize ← this from @angular/cdk-experimental.
1<cdk-virtual-scroll-viewport autosize class="ideta-scrollbar">
2	<div *cdkVirtualFor="let item of list; let i = index" class="pb-2"></div>
3</cdk-virtual-scroll-viewport>
Creation
1// module with routing
2ng generate m --routing test
3// test.module.ts and test-routing.module.ts
Sau khi tạo module rùi, tạo thêm routing module sẽ không được. Tuy nhiên sau khi tạo module rồi, tạo thêm component vẫn ok.
1ng generate c test // test.component.ts
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.
: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}
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
@Input() but child’s changes → parent’s 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()
@Input() parent’s changes → child’s changes?
Yes → Use ngOnChanges to see the updated value of the input! (Don’t use ngOnInit to check)
OnPush change detection
  • changeDetection: ChangeDetectionStrategy.OnPush = auto change detection is deactivated. Tell angular that the component only depends on its @Input() and need to be checked (ref).
  • changeDetection: ChangeDetectionStrategy.Default (or CheckAlways) = change detection is automatic.
Call children’s methods from parent
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
👇 Source.
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 trackById = (_index: number, item: any) => item.id;
1<div *ngFor="let e of array; trackBy: trackById;">
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...