Skip to main content

Command Palette

Search for a command to run...

Hiểu về bản chất lỗi NG0100 trong Angular

Published
4 min read
S

Developer with somethings to sharing

Trong bài viết bài, chúng ta tìm hiểu bản chất của lỗi NG0100 trong Angular và cách khắc phục của nó.

Vào thẳng vấn đề chính, lỗi "NG0100: Expression has changed after it was checked" trong Angular xảy ra khi một biểu thức được kiểm tra lần đầu tiên và sau đó thay đổi giá trị trước khi chu kỳ kiểm tra tha đổi (change detection cycle) hoàn tất. Anguar phát hiện ra rằng giá trị của biểu thức đã thay đổi sau khi nó được kiểm tra lần đầu tiên. Điều này dẫn đến cách hành vi không mong muốn hoặc không nhất quán.

Cụ thể như sau:

  1. Quá trình Change Detection của Angular:

    Angular có một cơ chế gọi là change detection để kiểm tra và cập nhật các biểu thức trong template. Quá trình này diễn ra như sau:

    • Angular bắt đầu chu kỳ change detection, kiểm tra tất cả giá trị của các biểu thức trong template.

    • Angular lưu lại giá trị ban đầu của biểu thức.

    • Nếu thay đổi, Angular cập nhật DOM tương ứng.

    • Sau đó Angular sẽ kiểm tra lại các biểu thức để đảm bảo rằng không có sự thay đổi nào xảy ra trong chu kỳ change detection. Nếu có sự thay đổi, Angular sẽ báo lỗi NG0100 này.

  2. Lifecyle Hooks:

    Các lifecycle hooks như "ngOnInit", "ngAfterContentInit", "ngAfterViewInit" được gọi trong quá trình khởi tạo và thay đổi của component. Nếu bạn thay đổi một giá trị bên trong các hook này, nó có thể làm thay đổi giá trị bên trong của biểu thức đã được kiểm tra trước đó.

Nguyên tắc nhất quán của Change Detection:

  • Angular yêu cầu trong một chu kỳ change detection, giá trị của biểu thức phải nhất quán. Điều này đảm bảo rằng template luôn phản ánh đúng giá trị hiện tại của dữ liệu.

  • Khi giá trị này không nhất quán trong cùng một chu kỳ change detection, Angular sẽ báo lỗi.

Tại sao điều này lại là vấn đề?

  • Hành vi không mong muốn: Nếu các giá trị thay đổi ngoài dự kiến trong chu kỳ change detection, có thể dẫn đến việc DOM không phản ánh chính xác trạng thái của dữ liệu.

  • Hiệu suất kém: Nếu Angular phải liên tục chạy lại chu kỳ change dectection do các thay đổi không đồng bộ, hiệu suất của ứng dụng có thể bị ảnh hưởng.

Ví dụ cụ thể:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-example',
  template: `
    <div>{{ message }}</div>
  `
})
export class ExampleComponent implements OnInit {
  message: string = 'Initial message';

  ngOnInit() {
    // Angular bắt đầu chu kỳ change detection
    this.message = 'Updated message';  // Thay đổi giá trị của `message`
    // Angular phát hiện sự thay đổi và báo lỗi NG0100
  }
}

Trong ví dụ này:

  • Angular bắt đầu chu kỳ change detection và lưu lại giá trị của message'Initial message'.

  • Sau đó, trong ngOnInit, giá trị của message được thay đổi thành 'Updated message'.

  • Khi Angular tiếp tục chu kỳ change detection, nó phát hiện rằng giá trị của message đã thay đổi từ 'Initial message' thành 'Updated message' trong cùng một chu kỳ.

  • Do đó, Angular ném lỗi NG0100 để cảnh báo rằng có sự thay đổi không hợp lệ đã xảy ra.

Cách khắc phục:

  1. Sử dụng ChangeDetectorRef.detectChanges():

    Buộc Angular thực hiện lại chu kỳ change detection sau khi bạn đã thay đổi giá trị.

     import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
    
     @Component({
       selector: 'app-example',
       template: `
         <div>{{ message }}</div>
       `
     })
     export class ExampleComponent implements OnInit {
       message: string = 'Initial message';
    
       constructor(private cdr: ChangeDetectorRef) {}
    
       ngOnInit() {
         this.message = 'Updated message';
         this.cdr.detectChanges();
       }
     }
    
  2. Sử dụng setTimeout():

    Đẩy thay đổi vào hàng đợi sự kiện để nó xảy ra sau khi chu kỳ change detection hiện tại hoàn tất.

     import { Component, OnInit } from '@angular/core';
    
     @Component({
       selector: 'app-example',
       template: `
         <div>{{ message }}</div>
       `
     })
     export class ExampleComponent implements OnInit {
       message: string = 'Initial message';
    
       ngOnInit() {
         setTimeout(() => {
           this.message = 'Updated message';
         });
       }
     }
    
  3. Sử dụng các lifecycle hooks khác:

    Thực hiện thay đổi trong ngAfterContentChecked

     import { Component, AfterViewInit } from '@angular/core';
    
     @Component({
       selector: 'app-example',
       template: `
         <div>{{ message }}</div>
       `
     })
     export class ExampleComponent {
       message: string = 'Initial message';
    
       ngAfterContentChecked() {
         this.message = 'Updated message';
       }
     }