Top 131 câu hỏi phỏng vấn Angular

01

Angular giải quyết vấn đề gì trong một frontend application?

Angular cung cấp một framework đầy đủ để xây dựng web application bằng TypeScript. Framework này không chỉ render UI mà còn cung cấp routing, forms, HTTP client, dependency injection, testing utilities, build system và conventions cho dự án lớn.

Câu trả lời tốt cần nhấn mạnh: Angular phù hợp khi ứng dụng cần cấu trúc rõ, nhiều màn hình, nhiều form, nhiều luồng dữ liệu và nhiều người cùng phát triển.

02

Component trong Angular gồm những phần nào và trách nhiệm chính là gì?

Component là khối UI cơ bản. Một component thường gồm class TypeScript, template HTML, style và metadata trong @Component.

Trách nhiệm chính của component là nhận dữ liệu, hiển thị UI và phát event từ tương tác người dùng. Business logic phức tạp, gọi API hoặc state dùng chung thường nên tách ra service hoặc facade để component không bị phình to.

ts
@Component({
  selector: 'app-user-card',
  templateUrl: './user-card.html',
  styleUrl: './user-card.scss'
})
export class UserCardComponent {}
03

Interpolation, property binding và event binding khác nhau thế nào?

Interpolation hiển thị giá trị ra template. Property binding truyền giá trị từ component vào property của element hoặc child component. Event binding lắng nghe sự kiện từ UI và gọi hàm trong component.

Ba cơ chế này tạo luồng dữ liệu cơ bản giữa class component và template.

ts
<h2>{{ title }}</h2>
<img [src]="avatarUrl" />
<button (click)="save()">Save</button>
04

@if@for trong Angular mới dùng để làm gì?

@if dùng để render UI theo điều kiện. @for dùng để render danh sách. Cú pháp mới giúp template rõ hơn và không cần phụ thuộc vào structural directive kiểu cũ trong nhiều trường hợp.

Với @for, phần track nên luôn được khai báo bằng key ổn định như id, giúp Angular tái sử dụng DOM đúng cách khi danh sách thay đổi.

ts
@if (user()) {
  <p>{{ user()!.name }}</p>
}

@for (item of items(); track item.id) {
  <app-item-row [item]="item" />
}
05

Service trong Angular thường dùng để làm gì?

Service dùng để chứa logic không nên đặt trực tiếp trong component, ví dụ gọi API, mapping dữ liệu, chia sẻ state, xử lý rule nghiệp vụ hoặc giao tiếp với browser API.

Service giúp code dễ tái sử dụng, dễ test và giảm coupling giữa các component.

06

Dependency Injection trong Angular hoạt động theo ý tưởng nào?

Dependency Injection là cơ chế Angular cung cấp dependency cho class thay vì class tự tạo dependency. Nhờ vậy code ít phụ thuộc cứng, dễ mock trong test và dễ thay đổi implementation.

Trong Angular hiện đại, dependency có thể inject qua constructor hoặc hàm inject() trong injection context.

ts
private readonly userService = inject(UserService);
07

Reactive Forms phù hợp với loại form nào?

Reactive Forms phù hợp với form có logic rõ ràng, nhiều validation, dynamic field hoặc cần test kỹ. Form model được khai báo trong TypeScript nên dễ kiểm soát state, value, validator và submit flow.

Template-driven forms vẫn có thể dùng cho form nhỏ, nhưng form nghiệp vụ phức tạp thường nên dùng Reactive Forms.

ts
form = new FormGroup({
  email: new FormControl('', [Validators.required, Validators.email]),
  password: new FormControl('', Validators.required)
});
08

Angular Router dùng để làm gì?

Angular Router ánh xạ URL với component hoặc feature tương ứng trong single-page application. Router hỗ trợ lazy loading, guard, resolver, nested route và route parameters.

Router tốt giúp cấu trúc ứng dụng rõ theo màn hình và hỗ trợ tách bundle theo route.

09

Signal trong Angular là gì?

Signal là primitive reactive để lưu state hiện tại và thông báo khi state thay đổi. Giá trị signal được đọc bằng cách gọi signal như function.

Signal phù hợp với local UI state, derived state và những phần template cần cập nhật rõ theo dependency.

ts
count = signal(0);
double = computed(() => this.count() * 2);

increase() {
  this.count.update(v => v + 1);
}
10

AOT là gì trong Angular?

AOT là Ahead-of-Time compilation. Angular compile template và metadata trong quá trình build thay vì compile template ở browser tại runtime.

AOT giúp phát hiện lỗi template sớm, giảm chi phí compile ở browser, cải thiện bảo mật và thường là lựa chọn mặc định cho production build.

11

Khi nào nên dùng switchMap thay vì mergeMap?

switchMap phù hợp khi kết quả cũ không còn giá trị sau khi có input mới. Ví dụ phổ biến là search box, filter thay đổi liên tục hoặc route param đổi nhanh. Request cũ nên bị hủy hoặc bỏ qua để tránh ghi đè UI bằng dữ liệu đã lỗi thời.

mergeMap phù hợp khi nhiều tác vụ có thể chạy song song và mọi kết quả đều cần xử lý, ví dụ gửi nhiều log event hoặc lưu nhiều item độc lập.

ts
search.valueChanges.pipe(
  debounceTime(300),
  distinctUntilChanged(),
  switchMap(keyword => this.api.search(keyword))
);
12

concatMap, mergeMap, switchMap, exhaustMap khác nhau thế nào?

Bốn operator này khác nhau ở cách xử lý concurrency. concatMap chạy tuần tự, phù hợp khi thứ tự quan trọng. mergeMap chạy song song, phù hợp khi mọi tác vụ đều cần hoàn thành. switchMap bỏ tác vụ cũ khi có tác vụ mới, phù hợp với search hoặc filter. exhaustMap bỏ input mới khi tác vụ hiện tại chưa xong, phù hợp chống double submit.

Câu trả lời tốt cần gắn operator với hành vi nghiệp vụ, không chỉ đọc định nghĩa.

13

Làm sao giảm memory leak khi dùng Observable trong component?

Ưu tiên dùng async pipe nếu dữ liệu chỉ cần render trong template vì Angular tự subscribe và unsubscribe. Khi bắt buộc subscribe trong TypeScript, nên dùng takeUntilDestroyed() hoặc cơ chế cleanup tương đương.

Các stream dài hạn như valueChanges, route params, WebSocket hoặc service subject cần cleanup rõ ràng. HTTP request một lần thường tự complete, nhưng vẫn cần xử lý khi kết hợp với operator dài hạn.

ts
this.form.valueChanges
  .pipe(takeUntilDestroyed())
  .subscribe(value => this.preview.set(value));
14

OnPush giúp gì và dễ gây hiểu nhầm ở đâu?

OnPush giảm số lần component bị kiểm tra khi dữ liệu không đổi. Component thường được check lại khi input reference đổi, event xảy ra trong component, signal dependency đổi hoặc observable qua async pipe emit.

Hiểu nhầm phổ biến là mutate object/array cũ rồi kỳ vọng child component cập nhật. Với OnPush, nên dùng immutable update để tạo reference mới.

ts
// Không nên
user.name = 'New name';

// Nên
user = { ...user, name: 'New name' };
15

Vì sao track trong @for quan trọng?

track giúp Angular nhận biết item nào giữ nguyên khi danh sách thay đổi. Nếu thiếu key ổn định, Angular có thể render lại DOM không cần thiết, làm mất state của child component hoặc gây chậm với list lớn.

Key tốt nên là giá trị định danh ổn định như database id. Không nên dùng index nếu danh sách có reorder, insert hoặc delete ở giữa.

ts
@for (row of rows(); track row.id) {
  <app-row [row]="row" />
}
16

Route guard nên làm gì và không nên làm gì?

Route guard nên quyết định có cho phép navigation hay không, ví dụ kiểm tra login, permission, unsaved changes hoặc redirect. Guard không nên chứa business logic nặng, không nên gọi nhiều API không cần thiết và không nên làm navigation bị treo lâu.

Nếu cần redirect, trả về UrlTree thường rõ hơn tự gọi router.navigate() bên trong guard.

17

Interceptor nên chứa loại logic nào?

Interceptor phù hợp cho logic HTTP mang tính cross-cutting: thêm token, thêm correlation id, log request, refresh token, xử lý lỗi chung hoặc chuẩn hóa response.

Logic riêng của từng màn hình không nên đặt hết vào interceptor vì sẽ làm interceptor khó hiểu và tạo side effect toàn app.

ts
export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const token = inject(AuthService).accessToken();
  return next(req.clone({
    setHeaders: token ? { Authorization: `Bearer ${token}` } : {}
  }));
};
18

Khi nào cần ControlValueAccessor?

Cần ControlValueAccessor khi một custom component cần hoạt động như form control chuẩn của Angular. Ví dụ: money input, date picker, autocomplete, tag input hoặc rich text editor.

Component đó phải nhận value từ form, báo value mới về form, báo touched state và nhận disabled state. Nhờ vậy component dùng được với formControlName, validator và form state.

19

Validation trong Reactive Forms nên tổ chức thế nào?

Validation đơn giản có thể đặt ngay tại control. Validation dùng lại nhiều nơi nên tách thành validator function. Validation cần gọi backend nên là async validator hoặc nằm trong submit flow rõ ràng.

Không nên đặt logic validation phức tạp trong template vì khó test và khó maintain.

ts
export function positiveNumber(control: AbstractControl) {
  return control.value > 0 ? null : { positiveNumber: true };
}
20

State nào nên nằm trong component, state nào nên đưa vào service?

State chỉ dùng bởi một component nên nằm trong component. State được chia sẻ trong một feature nên nằm trong feature service hoặc facade. State toàn app như auth user, theme, tenant config hoặc permission nên nằm ở core service hoặc store.

Nguyên tắc tốt là giữ state ở phạm vi nhỏ nhất có thể, chỉ nâng phạm vi khi có nhu cầu chia sẻ thật.

21

computed()effect() khác nhau thế nào?

computed() tạo derived state có giá trị trả về và nên thuần. effect() dùng để chạy side effect khi signal dependency thay đổi, ví dụ log, sync local storage hoặc gọi integration ngoài Angular.

Không nên dùng effect() để thay thế mọi luồng nghiệp vụ. Nếu dữ liệu có thể biểu diễn bằng derived state, computed() thường rõ hơn.

ts
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);

effect(() => {
  console.log('Selected id:', this.selectedId());
});
22

Khi nào Observable vẫn phù hợp hơn Signal?

Observable phù hợp với event stream và async pipeline: debounce, retry, cancellation, WebSocket, route param stream, form value changes hoặc polling. Signal phù hợp với state hiện tại cần đọc trong template.

Kiến trúc tốt thường dùng Observable cho luồng dữ liệu async và signal cho UI state, với boundary chuyển đổi rõ ràng.

23

AOT giúp phát hiện những lỗi template nào?

AOT có thể phát hiện binding sai property, input sai type, gọi method không tồn tại, pipe/directive chưa import hoặc template variable dùng sai kiểu. Khi bật strictTemplates, template được kiểm tra gần giống TypeScript code.

Điểm quan trọng là lỗi được phát hiện ở build time, không đợi người dùng mở màn hình ở production.

24

strictTemplates có tác dụng gì?

strictTemplates tăng độ nghiêm ngặt khi Angular type-check template. Angular kiểm tra type của input, output, template reference variable, pipe result, safe navigation và nhiều expression trong template.

Dự án lớn nên bật strict template checking càng sớm càng tốt. Với codebase cũ, có thể rollout theo feature để giảm rủi ro.

ts
// tsconfig.app.json
{
  "angularCompilerOptions": {
    "strictTemplates": true
  }
}
25

Vì sao production build có thể fail trong khi dev server vẫn chạy?

Production build thường bật AOT, optimization, minification và kiểm tra nghiêm ngặt hơn. Dev server ưu tiên tốc độ rebuild nên một số lỗi template, import hoặc optimization chỉ lộ ra khi build production.

CI nên luôn chạy production build để tránh lỗi chỉ xuất hiện sau khi deploy.

26

Lazy loading theo route giải quyết vấn đề gì?

Lazy loading tách code theo route hoặc feature. Người dùng chỉ tải code của phần đang truy cập, giúp giảm initial bundle và cải thiện thời gian load ban đầu.

Các feature ít dùng hoặc nặng như admin, report, dashboard chi tiết, settings thường phù hợp để lazy load.

ts
{
  path: 'reports',
  loadComponent: () => import('./reports.page').then(m => m.ReportsPage)
}
27

@defer khác lazy loading route như thế nào?

Lazy loading route tách code theo navigation. @defer tách code ở cấp template trong cùng một route.

@defer phù hợp với phần UI không cần cho first render như chart, map, comments, rich editor hoặc widget dưới viewport. Cần có placeholder/loading để UX không bị trống khó hiểu.

ts
@defer (on viewport) {
  <app-heavy-chart />
} @placeholder {
  <p>Biểu đồ sẽ tải khi cần.</p>
}
28

Vì sao không nên gom toàn bộ component vào một folder components?

Folder components toàn cục làm mất ngữ cảnh nghiệp vụ, khó biết component thuộc feature nào và dễ tạo import chéo. Với dự án lớn, cấu trúc theo feature/domain giúp code liên quan nằm gần nhau và dễ ownership.

Bên trong mỗi feature vẫn có thể có folder components, nhưng không nên là nơi chứa mọi component của toàn app.

29

core, sharedfeatures khác nhau thế nào?

core chứa singleton service và cấu hình cấp app như auth, interceptor, logger, config. shared chứa UI component, pipe, directive dùng chung và ít phụ thuộc business. features chứa màn hình và logic theo domain nghiệp vụ.

Sai lầm phổ biến là đưa business component vào shared, làm shared trở thành nơi chứa mọi thứ.

30

Unit test service gọi API nên kiểm tra gì?

Test nên kiểm tra URL, method, query params, request body, response mapping và error handling. Không nên chỉ test rằng hàm được gọi.

HTTP testing utilities giúp mock request và flush response để kiểm tra service độc lập với backend thật.

31

Component test nên tập trung vào điều gì?

Component test nên kiểm tra behavior quan sát được: render state, input/output, form validation, button disabled, error message hoặc event emit.

Không nên test quá sâu private method hoặc internal implementation khiến refactor nhỏ cũng làm test vỡ.

32

Angular có tự bảo vệ khỏi XSS hoàn toàn không?

Angular có cơ chế sanitize trong nhiều binding nguy hiểm, nhưng không thay thế tư duy bảo mật. Nếu dùng DomSanitizer để bypass hoặc đưa HTML không tin cậy vào DOM, XSS vẫn có thể xảy ra.

Dữ liệu từ người dùng hoặc nguồn bên ngoài cần được validate/sanitize ở backend và xử lý cẩn thận ở frontend.

33

Một form Angular cần chú ý accessibility gì?

Form cần label liên kết đúng với input, error message rõ ràng, trạng thái invalid dễ nhận biết, keyboard navigation và không chỉ dùng màu để truyền đạt lỗi.

Accessibility nên là một phần của thiết kế component, không phải bước sửa cuối dự án.

ts
<label for="email">Email</label>
<input id="email" formControlName="email" aria-describedby="email-error" />
<p id="email-error" role="alert">Email không hợp lệ.</p>
34

Dấu hiệu nào cho thấy một component đang quá tải?

Dấu hiệu gồm file quá dài, vừa gọi API vừa xử lý business vừa format UI, nhiều subscription thủ công, template lồng nhiều điều kiện, khó test và nhiều dependency inject vào constructor/class.

Cách xử lý là tách presentational component, service/facade, pipe, directive hoặc computed view model.

35

NgZone xuất hiện trong Angular để giải quyết vấn đề gì?

NgZone giúp Angular biết khi nào async task hoàn tất để chạy change detection. Các task như timer, promise, DOM event hoặc HTTP callback có thể khiến Angular cập nhật UI sau khi dữ liệu thay đổi.

Ở mức Middle, cần hiểu rằng quá nhiều async task trong Angular zone có thể làm change detection chạy nhiều lần không cần thiết.

36

Thiết kế Angular app lớn nên chia theo technical layer hay feature/domain?

Với app lớn, feature/domain-based architecture thường tốt hơn. Code liên quan đến cùng nghiệp vụ nằm gần nhau, ownership rõ hơn, lazy loading dễ hơn và dependency boundary dễ kiểm soát hơn.

Technical layer như components, services, models vẫn có thể tồn tại bên trong từng feature. Không nên dùng chúng làm cấu trúc chính cho toàn app vì sẽ làm code phân tán theo kỹ thuật thay vì nghiệp vụ.

txt
features/
  orders/
    pages/
    components/
    data-access/
    models/
    orders.routes.ts
37

Một feature boundary tốt trong Angular cần có đặc điểm gì?

Feature boundary tốt có public API rõ, dependency hướng một chiều, không import ngược, không lộ implementation nội bộ và có owner rõ.

Component trong feature khác không nên import tùy tiện component/service private của feature này. Khi cần giao tiếp, nên dùng route, shared contract, facade hoặc domain event rõ ràng.

38

Smart component và presentational component còn phù hợp với standalone và signals không?

Vẫn phù hợp nếu dùng linh hoạt. Smart component xử lý data loading, state, permission, route param và side effect. Presentational component nhận input, phát output và tập trung render UI.

Standalone giúp component độc lập hơn. Signals giúp state rõ hơn. Hai tính năng này không làm pattern smart/presentational lỗi thời; chúng làm cách triển khai gọn hơn.

39

Khi nào nên tạo feature service thay vì global service?

Feature service phù hợp khi logic chỉ phục vụ một domain hoặc nhóm màn hình. Global service chỉ nên dùng cho thứ thật sự toàn app như auth, config, theme, logger, tenant hoặc permission.

Dùng global service quá nhiều khiến state sống quá lâu, coupling tăng và bug khó khoanh vùng.

40

Facade service trong Angular giải quyết vấn đề gì?

Facade service tạo lớp API đơn giản giữa component và phần data/state phức tạp. Component đọc state và gọi action qua facade, không cần biết bên dưới dùng HTTP service, signal store, NgRx hay cache.

Facade hữu ích khi cần thay đổi implementation phía dưới mà ít ảnh hưởng UI.

ts
@Injectable()
export class OrdersFacade {
  private readonly api = inject(OrdersApi);
  readonly orders = signal<OrderVm[]>([]);

  load() {
    this.api.getOrders().subscribe(dto => {
      this.orders.set(dto.map(toOrderVm));
    });
  }
}
41

Data-access layer nên chứa gì?

Data-access layer nên chứa API client, repository/facade, DTO types, mapping DTO sang ViewModel, cache của feature và error mapping gần backend.

UI component không nên biết endpoint chi tiết, payload backend hoặc mapping phức tạp. Tách data-access giúp backend thay đổi ít làm vỡ UI.

42

DTO và ViewModel khác nhau thế nào?

DTO phản ánh dữ liệu backend trả về hoặc nhận vào. ViewModel phản ánh dữ liệu đã được chuẩn hóa cho UI.

Không nên để template phụ thuộc trực tiếp vào DTO phức tạp hoặc chưa ổn định. Mapping DTO sang ViewModel giúp template dễ đọc, dễ test và ít bị ảnh hưởng khi API đổi field.

ts
type UserDto = { first_name: string; last_name: string; status_code: string };
type UserVm = { fullName: string; statusLabel: string };

function toUserVm(dto: UserDto): UserVm {
  return {
    fullName: `${dto.first_name} ${dto.last_name}`,
    statusLabel: mapStatus(dto.status_code)
  };
}
43

Route-level provider hữu ích khi nào?

Route-level provider hữu ích khi service chỉ cần sống trong phạm vi route hoặc lazy feature. Khi route bị destroy, service state cũng có thể được giải phóng.

Cách này tránh biến mọi service thành singleton toàn app và giúp state ownership rõ hơn.

44

Shared UI component nên có API như thế nào?

Shared UI component nên có input/output rõ ràng, ít phụ thuộc business logic, hỗ trợ accessibility, có style có thể cấu hình vừa đủ và behavior dễ đoán.

Component shared không nên gọi API nghiệp vụ hoặc phụ thuộc vào feature cụ thể. Nếu component cần biết quá nhiều domain, nó không còn là shared UI component.

45

Làm sao tránh shared trở thành nơi chứa mọi thứ?

Cần rule rõ: shared chỉ chứa thứ thật sự dùng chung và không gắn domain. Business component nên nằm trong feature/domain. Nếu nhiều feature cùng cần logic nghiệp vụ, nên tạo domain library hoặc contract riêng thay vì nhét vào shared.

Review dependency graph định kỳ giúp phát hiện shared bị lạm dụng.

46

Khi nào local signal đủ, khi nào cần store phức tạp hơn?

Local signal đủ cho state trong một component hoặc cụm component nhỏ. Service + signal phù hợp với state chia sẻ trong một feature. Store như NgRx phù hợp khi state có nhiều nguồn update, cần action trace, cache phức tạp, undo/redo hoặc nhiều team cùng phát triển.

Không nên đưa mọi state vào global store vì làm tăng boilerplate và coupling.

47

State ownership trong Angular app lớn nên xác định thế nào?

Mỗi state cần owner rõ: component, route-level service, feature facade, global service hoặc store. State không có owner rõ thường bị nhiều nơi cùng sửa, gây race condition, stale data hoặc bug khó debug.

Nguyên tắc tốt là state nằm ở phạm vi nhỏ nhất có thể và chỉ nâng scope khi nhu cầu chia sẻ rõ ràng.

48

Signal có thể thay thế toàn bộ RxJS không?

Không. Signal phù hợp với state hiện tại và derived state. RxJS vẫn phù hợp với event stream, async pipeline, cancellation, retry, debounce, polling và WebSocket.

Kiến trúc tốt không ép một primitive xử lý mọi bài toán. Boundary giữa signal và observable cần rõ để tránh code vừa khó đọc vừa khó debug.

49

Boundary giữa Observable và Signal nên đặt ở đâu?

Boundary thường nên đặt gần UI hoặc facade. API service có thể trả Observable vì đó là luồng async. Component hoặc facade có thể convert sang signal để template đọc dễ hơn.

Không nên convert Observable sang Signal rồi lại convert ngược nhiều lần qua nhiều lớp vì làm mất tính rõ ràng của data flow.

ts
readonly users = toSignal(this.userService.users$, {
  initialValue: []
});
50

Khi nào effect() tạo ra kiến trúc khó kiểm soát?

effect() gây khó kiểm soát khi được dùng để điều phối business flow phức tạp, ghi state dây chuyền hoặc gọi API theo nhiều dependency ẩn. Khi đó luồng xử lý trở nên khó lần theo vì side effect chạy tự động.

Nếu có thể biểu diễn bằng computed() hoặc method action rõ ràng, nên tránh dùng effect() cho nghiệp vụ chính.

51

Một Angular page render chậm nên phân tích theo thứ tự nào?

Nên đo trước khi sửa. Kiểm tra bundle size, initial request, API latency, số lượng component render, list thiếu track, function nặng trong template, change detection quá rộng, third-party library và memory leak.

Tối ưu bằng cảm tính dễ sửa sai điểm nghẽn. Profiler và build analyzer nên được dùng trước khi refactor lớn.

52

Vì sao function trong template có thể gây chậm?

Function trong template có thể bị gọi nhiều lần trong change detection. Nếu function tạo object/array mới hoặc tính toán nặng, child component có thể render lại liên tục và UI bị chậm.

Nên chuyển sang computed signal, pure pipe hoặc precomputed ViewModel.

ts
// Không nên nếu calculateTotal nặng
<p>{{ calculateTotal(items()) }}</p>

// Nên
total = computed(() => calculateTotal(this.items()));
53

Tối ưu bundle Angular nên bắt đầu từ đâu?

Bắt đầu bằng phân tích bundle thực tế. Kiểm tra dependency lớn, import sai cách, route chưa lazy load, component nặng chưa @defer, duplicate packages, polyfill không cần thiết và asset quá lớn.

Mục tiêu là giảm initial JavaScript và trì hoãn phần chưa cần thiết cho first render.

54

Khi nào nên dùng @defer, khi nào không nên dùng?

Nên dùng @defer cho UI nặng không cần ngay lúc first render như chart, map, editor, comments hoặc dashboard widget. Không nên dùng cho nội dung chính cần xuất hiện ngay, phần ảnh hưởng SEO quan trọng hoặc component nhỏ không đáng tách chunk.

@defer cần placeholder/loading/error rõ để UX không bị đứt đoạn.

55

AOT ảnh hưởng thế nào đến cách viết template và component API?

AOT yêu cầu template và metadata có thể phân tích tốt ở build time. Component API nên có input/output rõ type, dependency import đầy đủ, template không gọi property không tồn tại và metadata không phụ thuộc runtime dynamic khó phân tích.

Với component dùng chung, type càng rõ thì consuming template càng dễ được kiểm tra chính xác.

56

AOT khác JIT ở điểm quan trọng nào cho production?

JIT compile template tại runtime trong browser, còn AOT compile trước trong quá trình build. Với production, AOT giúp phát hiện lỗi sớm, giảm compile work ở browser và giảm rủi ro liên quan đến dynamic template compilation.

Production Angular app hiện đại nên xem AOT là tiêu chuẩn, không phải tùy chọn phụ.

57

AOT metadata error thường đến từ đâu?

AOT metadata error thường đến từ metadata không thể phân tích tĩnh, export thiếu symbol cần dùng trong template/provider, expression quá dynamic trong decorator hoặc thư viện được build không đúng chuẩn Angular.

Cách xử lý là giữ metadata đơn giản, export public API rõ và tránh dựa vào runtime reflection trong phần Angular cần phân tích.

58

strictTemplates nên rollout thế nào trong codebase cũ?

Không nên bật strict template rồi sửa toàn bộ trong một lần nếu codebase lớn. Nên rollout theo feature, ưu tiên màn hình hay lỗi, code mới bật strict, lỗi cũ được giảm dần.

Quan trọng là sửa type thật thay vì ép any hàng loạt. Nếu dùng any để qua build, lợi ích của strict template gần như mất.

59

AOT có liên quan gì đến tree-shaking?

AOT không phải tree-shaking, nhưng output rõ và dependency tĩnh giúp bundler tối ưu tốt hơn. Tree-shaking cần import rõ, side effect thấp và code không dùng bị loại bỏ được.

Nếu code import cả module lớn hoặc library có side effect, bundle vẫn có thể lớn dù dùng AOT.

60

AOT có thể làm lộ vấn đề gì trong Angular library nội bộ?

AOT có thể làm lộ lỗi public API thiếu export, metadata không phân tích được, input/output type không rõ, dependency peer không đúng hoặc template trong app tiêu thụ không type-check được.

Library nội bộ nên được test bằng app tiêu thụ giả lập, không chỉ build riêng library.

61

CI pipeline cho Angular app nên kiểm tra những bước nào?

CI nên chạy install sạch, lint, type check, unit test, production build với AOT, kiểm tra bundle budget và E2E cho flow trọng yếu nếu có.

Production build là bước bắt buộc vì nhiều lỗi AOT/template chỉ xuất hiện ở build tối ưu.

62

NgZone trong Angular dùng để làm gì?

NgZone là service cho phép chạy code bên trong hoặc bên ngoài Angular zone. Khi code chạy trong Angular zone, các async task như promise, timer, DOM event hoặc HTTP callback có thể khiến Angular schedule change detection.

Hiểu NgZone giúp xử lý hai nhóm lỗi: UI cập nhật quá nhiều gây chậm, hoặc UI không cập nhật vì code chạy ngoài zone.

63

Khi nào nên dùng runOutsideAngular()?

runOutsideAngular() phù hợp cho tác vụ có tần suất cao nhưng không cần cập nhật UI liên tục: scroll listener, mousemove, resize, animation loop, chart library, map library hoặc tracking event.

Sau khi có trạng thái thật sự cần render, phần cập nhật UI có thể quay lại Angular zone bằng run() hoặc cập nhật qua cơ chế reactive phù hợp.

ts
this.ngZone.runOutsideAngular(() => {
  window.addEventListener('scroll', this.onScroll);
});

private onImportantChange() {
  this.ngZone.run(() => {
    this.visible.set(true);
  });
}
64

Dấu hiệu nào cho thấy zone pollution đang xảy ra?

Zone pollution xảy ra khi tác vụ async không liên quan UI vẫn kích hoạt change detection quá thường xuyên. Dấu hiệu gồm scroll bị lag, animation giật, profiler cho thấy nhiều change detection cycle, nhiều setInterval/requestAnimationFrame trong zone hoặc third-party library tạo task liên tục.

Cách xử lý thường là đưa task tần suất cao ra ngoài Angular zone, debounce/throttle event và chỉ quay lại zone khi cần cập nhật UI.

65

Vì sao UI có thể không cập nhật khi code chạy ngoài Angular zone?

Code ngoài Angular zone không tự schedule change detection. Nếu callback bên ngoài zone thay đổi state mà Angular không biết, template có thể không cập nhật.

Cần đưa phần cập nhật UI vào ngZone.run(), dùng signal/state update được Angular nhận biết hoặc trigger change detection có kiểm soát trong trường hợp đặc biệt.

66

NgZone.run() có nên bọc toàn bộ callback third-party không?

Không nên bọc toàn bộ nếu callback chạy rất thường xuyên. Cách tốt hơn là giữ phần event nhiều lần ở ngoài zone, lọc hoặc tính toán trước, chỉ đưa phần thay đổi UI quan trọng vào run().

Nếu đưa mọi callback tần suất cao vào zone, change detection có thể chạy quá nhiều và làm mất lợi ích tối ưu.

67

runOutsideAngular() có thay thế debounce/throttle không?

Không. runOutsideAngular() giảm việc Angular bị trigger change detection, nhưng không làm giảm số event thực tế. Với scroll, resize hoặc mousemove, vẫn nên debounce/throttle nếu logic xử lý nặng.

Hai kỹ thuật giải quyết hai vấn đề khác nhau: zone kiểm soát change detection, debounce/throttle kiểm soát tần suất xử lý.

68

Third-party chart library nên tích hợp với NgZone như thế nào?

Khởi tạo chart, animation và event nội bộ nên chạy ngoài Angular zone nếu không cần cập nhật UI Angular liên tục. Chỉ khi event quan trọng xảy ra, ví dụ click một điểm dữ liệu cần cập nhật panel Angular, mới quay lại ngZone.run().

Cần cleanup chart instance trong destroy hook để tránh memory leak.

ts
this.ngZone.runOutsideAngular(() => {
  this.chart = createChart(element, {
    onPointClick: point => {
      this.ngZone.run(() => this.selectedPoint.set(point));
    }
  });
});
69

Zoneless Angular thay đổi cách suy nghĩ về update UI như thế nào?

Zoneless giảm phụ thuộc vào Zone.js để tự động phát hiện mọi async task. Điều này yêu cầu state update rõ ràng hơn, thường qua signals, explicit marks hoặc cơ chế Angular nhận biết.

Lợi ích là kiểm soát change detection tốt hơn và giảm overhead từ zone. Chi phí là code cũ dựa vào side effect async tự cập nhật UI cần được audit kỹ.

70

Có cần xóa toàn bộ NgZone.run() khi chuyển sang zoneless không?

Không nhất thiết. NgZone.run()runOutsideAngular() vẫn có thể tồn tại để tương thích và tối ưu với code/library còn dùng Zone.js. Xóa máy móc có thể gây regression performance ở app hoặc library vẫn chạy trong môi trường có zone.

Migration tốt cần audit theo hành vi thực tế, không xóa theo search-replace.

71

SSR và hydration ảnh hưởng kiến trúc Angular app thế nào?

SSR khiến code có thể chạy trên server và browser. Hydration gắn behavior vào HTML đã render sẵn từ server. Do đó code cần tránh truy cập trực tiếp window, document, localStorage khi chưa kiểm tra platform.

Browser-only logic nên được bọc trong service hoặc chạy sau khi xác định đang ở browser.

72

Làm sao xử lý browser-only API trong SSR?

Cần kiểm tra platform bằng isPlatformBrowser hoặc dùng abstraction service có implementation riêng. Không nên gọi browser API trong constructor nếu component có thể render ở server.

Cách này tránh crash SSR và giúp hydration ổn định hơn.

ts
const platformId = inject(PLATFORM_ID);

if (isPlatformBrowser(platformId)) {
  localStorage.setItem('theme', 'dark');
}
73

Error handling toàn app nên chia thành những lớp nào?

Nên chia thành interceptor cho lỗi HTTP chung, service cho lỗi nghiệp vụ gần API, component cho UI state như empty/retry/toast và global error handler cho lỗi không bắt được.

Không nên xử lý mọi lỗi bằng một message chung vì làm UX kém và debug khó.

74

Refresh token trong interceptor cần tránh vấn đề gì?

Cần tránh nhiều request cùng refresh token đồng thời, tránh infinite loop khi refresh fail, tránh retry request không an toàn và xử lý logout rõ ràng khi token hết hạn thật.

Luồng tốt thường queue các request 401 trong khi refresh chạy, sau đó retry bằng token mới hoặc logout nếu refresh thất bại.

75

Frontend permission check có đủ bảo mật không?

Không. Frontend permission check chỉ là UX layer để ẩn/hiện UI hoặc chặn route. Backend vẫn phải kiểm tra quyền cho mọi API quan trọng.

Vì code frontend chạy trên máy người dùng, mọi kiểm tra quyền ở frontend đều có thể bị bypass về mặt kỹ thuật.

76

Dynamic form nên thiết kế theo schema như thế nào?

Dynamic form nên tách schema, form model, renderer và validation. Schema mô tả field, type, label, validation, visibility và dependency. Renderer chuyển schema thành UI. Form model quản lý value và state.

Không nên hard-code quá nhiều rule trong từng template nếu form thay đổi thường xuyên theo nghiệp vụ.

77

Form phức tạp nên tách component theo tiêu chí nào?

Có thể tách theo section nghiệp vụ, nhóm field lặp lại, custom control hoặc validation boundary. Parent form vẫn nên giữ quyền submit và tổng hợp state nếu flow nghiệp vụ cần toàn bộ form.

Control độc lập có thể dùng ControlValueAccessor. Section form có thể nhận FormGroup hoặc FormArray từ parent.

78

Thiết kế cache API frontend cần trả lời câu hỏi nào?

Cần xác định dữ liệu nào được cache, key cache là gì, cache sống bao lâu, khi nào invalidate, có background refresh không, có phân biệt user/tenant/filter không và có optimistic update không.

Cache không có invalidation rõ thường tạo bug dữ liệu cũ rất khó debug.

79

DomSanitizer nên dùng trong trường hợp nào?

Chỉ nên dùng khi nguồn dữ liệu đã được kiểm soát và lý do bypass rõ ràng. Không nên dùng DomSanitizer để tắt cảnh báo cho nhanh.

Nếu dữ liệu đến từ user input hoặc nguồn ngoài chưa tin cậy, bypass sanitizer có thể tạo lỗ hổng XSS.

80

i18n trong Angular app lớn cần thiết kế từ đâu?

Cần thống nhất translation key, lazy load translation theo feature nếu cần, fallback language, format date/number/currency, quy trình cập nhật bản dịch và kiểm soát hard-coded text.

Không nên để text rải rác trong component vì khi mở rộng ngôn ngữ sẽ rất khó kiểm soát.

81

Custom component cần đảm bảo accessibility thế nào?

Custom component cần hỗ trợ keyboard interaction, focus management, ARIA phù hợp, label rõ, disabled/error state, contrast và screen reader behavior.

Với modal, autocomplete, dropdown hoặc tab, accessibility là một phần của API component, không phải chi tiết phụ.

82

Test strategy cho Angular app lớn nên phân bổ thế nào?

Unit test cho pure logic, service và helper quan trọng. Component/integration test cho UI state, form và interaction. E2E test cho flow nghiệp vụ trọng yếu như login, checkout, tạo đơn hoặc phân quyền.

Không nên dùng E2E cho mọi thứ vì chậm và dễ flaky.

83

Làm sao viết test ít phụ thuộc implementation detail?

Test nên kiểm tra hành vi quan sát được: text hiển thị, button disabled, event emit, request được gọi, route thay đổi hoặc error message xuất hiện.

Tránh test private method hoặc internal property không liên quan hành vi. Test như vậy bền hơn khi refactor.

84

Dấu hiệu nào cho thấy Angular app mất maintainability?

Dấu hiệu gồm circular dependency, shared chứa quá nhiều thứ, component quá lớn, service ôm nhiều trách nhiệm, global state không rõ owner, build chậm, test khó viết và bug sửa một nơi hỏng nơi khác.

Maintainability nên được đánh giá qua tốc độ thêm feature, độ dễ debug và độ an toàn khi refactor.

85

Code review Angular senior nên tập trung vào những điểm nào?

Nên xem responsibility của component/service, state ownership, dependency direction, template complexity, subscription cleanup, performance list, form validation, error handling, accessibility và test cho logic quan trọng.

Review senior không chỉ bắt lỗi style mà còn phát hiện rủi ro kiến trúc.

86

Migrate từ NgModule sang standalone nên làm thế nào?

Nên migrate từng phần. Component mới viết standalone, route mới dùng loadComponent, shared module tách dần thành standalone imports, feature cũ giữ nguyên nếu chưa cần sửa.

Không nên rewrite toàn bộ chỉ để đổi sang standalone nếu không có mục tiêu rõ về maintainability, bundle hoặc migration dài hạn.

87

Migrate từ RxJS-heavy sang signals nên tránh lỗi gì?

Không nên thay toàn bộ Observable bằng signal. Nên giữ RxJS cho stream phức tạp và dùng signal cho UI state hiện tại.

Cần xác định boundary rõ, refactor theo feature và tránh convert qua lại nhiều tầng.

88

Angular library nội bộ cần thiết kế public API thế nào?

Public API nên nhỏ, rõ và ổn định. Chỉ export những thành phần được hỗ trợ chính thức. Không nên export sâu vào file nội bộ vì app tiêu thụ sẽ phụ thuộc implementation detail.

Library cần versioning, peer dependency rõ, test và documentation đủ dùng.

89

Feature flag trong Angular nên tổ chức thế nào?

Nên có feature flag service hoặc config layer tập trung. Flag có thể theo environment, user, role hoặc tenant. Component chỉ nên đọc API đơn giản, không nên tự xử lý nhiều rule phức tạp.

Flag cần lifecycle rõ để tránh dead code tồn tại lâu.

90

Multi-tenant Angular app cần tách những cấu hình nào?

Cần tách branding, theme token, feature flag, permission, locale, currency, route availability và có thể cả API base URL. Tenant logic không nên rải rác trong template.

Nên có tenant config service làm nguồn cấu hình tập trung.

91

Design system Angular cần quan tâm gì ngoài UI đẹp?

Design system cần component API ổn định, accessibility, design tokens, theming, documentation, versioning, test và migration guide. Component phải dễ compose, dễ dùng đúng và khó dùng sai.

UI đẹp nhưng API thiếu ổn định sẽ tạo chi phí maintain lớn.

92

List rất lớn trong Angular nên xử lý thế nào?

Không nên render hàng nghìn DOM node cùng lúc. Nên dùng pagination server-side, virtual scroll, track item ổn định, OnPush, tránh function nặng trong cell và chỉ render column cần thiết.

Nếu filter/sort dữ liệu rất lớn, backend nên xử lý thay vì frontend ôm toàn bộ.

93

Bundle budget trong Angular có giá trị gì?

Bundle budget giúp CI/build cảnh báo hoặc fail khi bundle vượt ngưỡng. Đây là guardrail để tránh dependency lớn hoặc import sai làm bundle tăng âm thầm.

Budget không thay thế profiling, nhưng giúp phát hiện regression sớm.

94

Monorepo Angular nhiều team cần rule dependency như thế nào?

Monorepo nhiều team cần rule dependency được enforce bằng tooling, không chỉ ghi trong tài liệu. Dependency nên đi từ app xuống feature, từ feature xuống data-access/shared/util, không import ngược và không import xuyên qua private implementation.

Câu trả lời mạnh cần nhắc đến ownership, dependency graph, lint rule, affected build/test, versioning nội bộ và cách xử lý shared contract khi nhiều team cùng dùng.

ts
apps/admin -> libs/orders/feature-list -> libs/orders/data-access -> libs/shared/http
// Không nên: shared/http -> orders/feature-list
95

Micro frontend với Angular phù hợp trong điều kiện nào?

Micro frontend phù hợp khi nhiều team cần deploy độc lập, domain tách rõ, release cadence khác nhau và tổ chức đủ khả năng vận hành độ phức tạp. Nếu chỉ có một team nhỏ hoặc release chung, micro frontend thường làm hệ thống phức tạp hơn.

Cần giải quyết routing, auth, shared dependency, observability, rollback, contract giữa shell/remote và chiến lược version mismatch.

96

Module Federation trong Angular có rủi ro kiến trúc nào?

Rủi ro gồm version mismatch, shared singleton sai, runtime failure khó debug, remote deploy làm hỏng shell, duplicate dependency, CSS conflict và contract giữa shell/remote không rõ.

Thiết kế tốt cần fallback UI, monitoring, rollback, semantic version policy và test tích hợp shell với remote trước khi release.

97

Permission system enterprise trong Angular nên thiết kế theo hướng nào?

Permission system nên dựa trên capability rõ ràng thay vì rải role check trong template. Role/claim từ backend nên được map sang permission/capability, sau đó UI dùng directive, guard hoặc service để kiểm tra.

Frontend permission vẫn chỉ là UX layer. Backend bắt buộc enforce quyền ở API.

ts
@if (can('user.update')) {
  <button>Edit user</button>
}
98

White-label Angular app ở quy mô lớn nên tránh lỗi kiến trúc nào?

Lỗi lớn nhất là rải if tenant === ... khắp component. Cách này khiến code khó test, khó thêm tenant mới và dễ tạo regression.

Nên có config layer cho theme, logo, copy text, route, feature flag và integration. Khác biệt lớn nên tách bằng plugin/feature boundary thay vì điều kiện lồng nhau.

99

Tối ưu Angular theo Core Web Vitals cần chiến lược gì?

Cần tối ưu LCP bằng giảm critical resource, SSR/hydration hoặc ưu tiên nội dung chính. Tối ưu INP bằng giảm JavaScript execution, tránh handler nặng và giảm change detection không cần thiết. Tối ưu CLS bằng giữ layout ổn định.

Câu trả lời tốt cần phân biệt lab data và field data. Lighthouse local chỉ là một tín hiệu, không thay thế dữ liệu production.

100

Frontend observability cho Angular enterprise app cần log những gì?

Nên log unhandled error, route error, API error quan trọng, performance metric, app version, build hash, correlation id, feature flag/tenant context khi cần debug và user journey chính.

Không log token, password, thông tin cá nhân nhạy cảm hoặc payload chứa dữ liệu bí mật.

101

Một Angular legacy app chậm và khó maintain nên được cứu theo thứ tự nào?

Không nên rewrite ngay. Nên đo performance, vẽ dependency graph, xác định flow nghiệp vụ quan trọng, thêm test bảo vệ, sửa memory leak, tách feature boundary, lazy load route lớn, chuẩn hóa error handling và refactor từng phần.

Rewrite toàn bộ chỉ hợp lý khi có bằng chứng chi phí maintain hiện tại vượt xa chi phí migration và có kế hoạch song song an toàn.

102

Nâng cấp Angular major version cho app lớn nên giảm rủi ro thế nào?

Cần kiểm tra compatibility Node/TypeScript/RxJS, đọc update guide, upgrade từng bước, chạy migration, sửa deprecation, chạy production build, test regression và kiểm tra third-party libraries.

Không nên nâng major version, đổi build system, migrate standalone và đổi state management trong một pull request lớn.

103

Compiler strictness nên rollout thế nào trong codebase cũ hàng trăm màn hình?

Nên rollout theo chiến lược tăng dần: bật strict cho code mới, ưu tiên feature đang thay đổi, tạo dashboard số lỗi còn lại, sửa theo nhóm lỗi và tránh ép kiểu any hàng loạt.

Mục tiêu là tăng độ an toàn lâu dài mà không chặn toàn bộ delivery. Cần có owner, guideline và CI rule theo giai đoạn.

104

Một lỗi chỉ xuất hiện trong AOT production build nhưng không tái hiện ở dev nên debug thế nào?

Cần tái hiện bằng command production build giống CI, kiểm tra template type error, optimizer/minifier issue, import side effect, dependency build format, environment replacement và code phụ thuộc runtime reflection.

Không nên sửa bằng cách tắt AOT hoặc nới strict ngay lập tức. Cần xác định lỗi thuộc template type, metadata, bundler hay third-party package.

105

Tại sao dynamic template compilation là anti-pattern trong Angular production?

Angular tin tưởng template code. Nếu template được tạo động từ dữ liệu người dùng rồi compile ở runtime, cơ chế bảo vệ mặc định có thể bị vượt qua và tạo rủi ro bảo mật nghiêm trọng.

Ngoài bảo mật, dynamic compilation làm mất lợi ích AOT, khó tree-shake, khó type-check và khó kiểm soát bundle. Dynamic UI nên được xây bằng schema, component mapping hoặc configuration an toàn thay vì compile template string.

106

Khi chuyển dần sang zoneless, cần audit những loại code nào?

Cần audit code dựa vào async task tự trigger change detection: third-party callback, DOM event ngoài Angular, timer, WebSocket, SDK callback, chart/map library, custom event bus và promise chain.

Mỗi chỗ update UI cần được chuyển sang signal/state update rõ ràng hoặc explicit change detection phù hợp. Test interaction thật trong browser rất quan trọng vì unit test có thể không lộ hết vấn đề.

107

Vì sao xóa toàn bộ NgZone.runOutsideAngular() có thể gây regression khi app chuyển sang zoneless?

runOutsideAngular() không chỉ phục vụ compatibility với Zone.js. Nó còn thể hiện ý đồ rằng một tác vụ tần suất cao không nên kéo Angular vào vòng update UI liên tục. Trong môi trường vẫn có Zone.js hoặc library khác dùng zone, xóa lệnh này có thể làm change detection chạy lại nhiều hơn.

Migration zoneless tốt cần giữ lại optimization có chủ đích, chỉ xóa code khi profiling chứng minh không còn giá trị.

108

Một chart library phát ra 60 event/giây làm UI lag. Thiết kế tích hợp Angular thế nào?

Chart nên được khởi tạo và xử lý event tần suất cao ngoài Angular zone. Event cần cập nhật UI nên được throttle/sample trước, sau đó mới quay lại Angular zone hoặc cập nhật signal.

Cần cleanup listener/chart instance khi component destroy. Nếu mỗi event đều gọi ngZone.run(), change detection sẽ chạy quá nhiều và làm UI lag.

ts
this.ngZone.runOutsideAngular(() => {
  const sub = chart.events$
    .pipe(throttleTime(100))
    .subscribe(point => {
      this.ngZone.run(() => this.hoveredPoint.set(point));
    });

  this.destroyRef.onDestroy(() => sub.unsubscribe());
});
109

Một SDK callback cập nhật signal nhưng UI không đổi trong một app dùng OnPush và zoneless. Hướng debug là gì?

Cần kiểm tra callback có chạy trong injection context/change detection context phù hợp không, signal có thật sự được set giá trị mới không, template có đọc đúng signal không, object có bị mutate giữ nguyên reference không và app đã cấu hình zoneless đúng chưa.

Nếu callback đến từ SDK ngoài Angular, cần đảm bảo state update đi qua primitive Angular nhận biết hoặc explicit mark phù hợp. Không nên đoán rằng mọi async callback đều tự làm UI cập nhật như khi còn dựa vào Zone.js.

110

ApplicationRef.isStable có thể bị ảnh hưởng bởi zone pollution thế nào?

Nếu trong Angular zone có setInterval, polling, animation loop hoặc task lặp không kết thúc, application có thể không đạt trạng thái stable đúng lúc. Điều này ảnh hưởng SSR/hydration, testing hoặc logic chờ app ổn định.

Tác vụ lặp không cần render nên chạy ngoài Angular zone, còn update UI quan trọng mới quay lại zone hoặc cập nhật state có kiểm soát.

ts
const zone = inject(NgZone);
zone.runOutsideAngular(() => {
  setInterval(() => sendHeartbeat(), 10000);
});
111

Sự khác nhau giữa tối ưu bằng OnPush, runOutsideAngular() và zoneless là gì?

OnPush giới hạn phạm vi và điều kiện check component. runOutsideAngular() ngăn một số async task kích hoạt change detection. Zoneless thay đổi cơ chế scheduling change detection ở cấp app, giảm phụ thuộc vào Zone.js.

Ba kỹ thuật này không thay thế nhau hoàn toàn. Một app có thể dùng cả ba: OnPush để giới hạn component check, runOutsideAngular cho event-heavy integration và zoneless để kiểm soát scheduling rõ hơn.

112

Khi nào không nên tối ưu bằng runOutsideAngular()?

Không nên dùng khi event hiếm, logic đơn giản và không gây performance issue. Cũng không nên đưa code ra ngoài zone nếu team chưa hiểu rõ cách UI cập nhật lại, vì có thể tạo bug UI không refresh.

Tối ưu bằng zone nên dựa trên profiling hoặc tích hợp event-heavy thật sự, không nên áp dụng mặc định cho mọi async callback.

113

Một component tree lớn bị check quá nhiều dù dùng OnPush. Nguyên nhân sâu có thể là gì?

Nguyên nhân có thể là input luôn tạo reference mới, function trong template tạo object mới, async event toàn app liên tục, observable emit quá nhiều, signal dependency đặt quá rộng, shared service phát state global cho quá nhiều component hoặc third-party library gây zone pollution.

Cần dùng profiler để xác định nguồn trigger và component bị render lại, sau đó sửa data flow hoặc boundary thay vì thêm OnPush một cách máy móc.

114

Hydration mismatch thường đến từ đâu trong Angular app?

Hydration mismatch thường đến từ HTML server render khác với HTML client render. Nguyên nhân có thể là dùng Date.now(), random value, browser-only state, localStorage, timezone khác nhau, dữ liệu API không đồng nhất hoặc DOM bị third-party script thay đổi trước hydration.

Code SSR-safe cần đảm bảo first render ở server và client nhất quán.

115

Một component dùng chart canvas trong SSR/hydration nên thiết kế thế nào?

Chart canvas thường là browser-only. Server render nên hiển thị placeholder hoặc container ổn định kích thước. Chart chỉ khởi tạo sau khi chạy ở browser, tốt nhất ngoài Angular zone nếu chart có animation/event nhiều.

Cách này tránh SSR crash, giảm hydration mismatch và giảm zone pollution.

116

Làm sao giữ Angular architecture không suy thoái sau nhiều năm?

Cần có dependency rule, code ownership, review checklist, lint rule, architecture decision record, documentation ngắn, migration plan định kỳ và automation kiểm tra boundary.

Architecture không được duy trì bằng tài liệu đơn lẻ. Cần tooling, CI và thói quen review nhất quán.

117

Versioning design system Angular nên xử lý breaking change thế nào?

Cần semantic versioning, changelog rõ, migration guide, deprecation period và codemod nếu thay đổi lớn. Component API nên được thiết kế để hạn chế breaking change thường xuyên.

Vì design system là dependency chung, breaking change có thể ảnh hưởng nhiều team cùng lúc.

118

Làm sao giảm flaky E2E test trong Angular project lớn?

Cần selector ổn định, test data kiểm soát được, backend mock hoặc seed rõ, không phụ thuộc timeout cứng, chờ điều kiện thật, reset state giữa test và giảm animation khi cần.

Flaky test phải được sửa nguyên nhân. Chỉ tăng timeout thường làm test chậm hơn nhưng không làm test đáng tin hơn.

119

Câu hỏi system design Angular senior/expert nên đánh giá điều gì?

Câu hỏi system design nên đánh giá cách chia boundary, xác định state ownership, xử lý performance, security, testing, migration, deploy strategy và trade-off.

Câu trả lời mạnh cần nêu quyết định, lý do, rủi ro, cách đo kết quả và cách rollback nếu phương án sai.

120

Cache theo tenant, permission và filter trong Angular có bẫy gì?

Cache key thiếu tenant, user, permission hoặc filter có thể trả dữ liệu sai người hoặc sai ngữ cảnh. Đây không chỉ là bug UX mà có thể thành vấn đề bảo mật nếu dữ liệu nhạy cảm bị hiển thị nhầm.

Cache key cần bao gồm các dimension ảnh hưởng dữ liệu. Invalidation cần xảy ra khi tenant, role, permission hoặc filter thay đổi.

121

Frontend token storage nên được đánh giá theo trade-off nào?

Cần đánh giá rủi ro XSS, CSRF, refresh token lifetime, silent refresh, logout, multi-tab sync và yêu cầu backend. Không có lựa chọn hoàn hảo cho mọi app.

Câu trả lời tốt không chỉ nói localStorage hay cookie, mà phân tích threat model và cơ chế giảm rủi ro tương ứng.

122

Một dependency làm bundle tăng mạnh nhưng chỉ dùng một function. Hướng xử lý là gì?

Cần xác minh bằng bundle analyzer, kiểm tra import có kéo cả package không, package có hỗ trợ tree-shaking không, có side effect không và có alternative nhỏ hơn không. Có thể đổi import path, lazy load phần dùng dependency hoặc thay bằng implementation nhẹ hơn.

Không nên đoán theo kích thước package trên npm; cần xem output bundle thật.

123

Migrate một AngularJS/Angular hybrid app sang Angular hiện đại nên chia chiến lược thế nào?

Cần chia theo route hoặc domain, giữ contract giữa phần cũ và mới, ưu tiên flow có giá trị cao, tránh rewrite big-bang, thêm test bảo vệ và có kế hoạch gỡ dependency cũ.

Standalone, lazy loading và facade có thể giúp phần Angular mới có kiến trúc sạch hơn mà không phụ thuộc quá sâu vào phần legacy.

124

Một Angular library nội bộ bị nhiều app dùng sai API. Cách sửa bền vững là gì?

Cần thu hẹp public API, thêm documentation, thêm runtime guard nếu cần, tạo lint rule hoặc schematic/codemod, đánh dấu deprecated API và đưa migration guide.

Chỉ nhắc team dùng đúng thường không đủ. API nên được thiết kế sao cho đường đúng dễ dùng nhất và đường sai khó dùng hơn.

125

Runtime configuration khác build-time environment ở điểm nào?

Build-time environment được đóng gói vào bundle khi build. Runtime configuration được tải khi app khởi động hoặc từ server, phù hợp khi cùng một artifact deploy cho nhiều môi trường/tenant.

Runtime config cần xử lý loading state, fallback, validation schema và không được chứa secret thật vì frontend vẫn có thể bị xem.

126

Một app zoneless có nhiều component không cập nhật sau khi WebSocket emit. Thiết kế lại data flow thế nào?

WebSocket stream nên đi vào một service/facade rõ ràng. Dữ liệu từ socket được map, validate, rồi cập nhật signal/store mà template đọc trực tiếp. Không nên để từng component tự subscribe socket và mutate object cục bộ.

Nếu cần backpressure, reconnect, retry hoặc buffer, RxJS vẫn phù hợp cho tầng stream; signal phù hợp ở tầng state hiện tại cho UI.

ts
messages$ = socket.connect().pipe(
  map(normalizeMessage),
  retry({ delay: 1000 })
);

messages = toSignal(messages$, { initialValue: [] });
127

Làm sao chứng minh một tối ưu NgZone thật sự có hiệu quả?

Cần đo trước và sau bằng profiler. Chỉ số cần xem gồm số change detection cycle, scripting time, frame drops, INP hoặc latency của interaction liên quan. Test cảm giác bằng mắt không đủ.

Nếu tối ưu làm code phức tạp hơn nhưng không cải thiện chỉ số, nên cân nhắc rollback để giữ maintainability.

128

Một Angular app nhiều team bị circular dependency liên tục. Giải pháp ở cấp tổ chức và code là gì?

Ở cấp code, cần tách contract/interface, đảo chiều dependency bằng abstraction, đưa shared type xuống layer thấp hơn và cấm import private feature. Ở cấp tổ chức, cần ownership rõ, review rule và tooling fail CI khi có cycle.

Circular dependency lặp lại thường là dấu hiệu boundary nghiệp vụ chưa rõ, không chỉ là lỗi import.

129

Khi nào nên chấp nhận duplication thay vì tạo abstraction dùng chung trong Angular?

Nên chấp nhận duplication khi hai phần code trông giống nhau nhưng thuộc nghiệp vụ khác, có khả năng tiến hóa khác nhau hoặc abstraction sẽ phải nhận quá nhiều option.

Abstraction sớm có thể tạo shared component phức tạp, khó test và khó thay đổi. Duplication nhỏ đôi khi rẻ hơn coupling sai.

130

Thiết kế dashboard Angular có 30 widget, mỗi widget gọi API riêng, cần tối ưu gì?

Cần lazy/defer widget dưới viewport, batch hoặc prioritize API nếu backend hỗ trợ, cache theo widget/filter, skeleton loading, error boundary từng widget, cancel request khi filter đổi, track list widget và tránh một widget lỗi làm hỏng cả dashboard.

Kiến trúc tốt nên có widget registry, config-driven layout và data-access riêng cho từng widget hoặc nhóm widget.

131

Thiết kế Angular app offline-first có điểm khó nào?

Offline-first cần cache strategy, sync queue, conflict resolution, optimistic UI, retry, versioned data schema, thông báo trạng thái sync và xử lý auth/token khi offline lâu.

Khó nhất không phải lưu dữ liệu local, mà là đồng bộ lại chính xác khi dữ liệu ở server đã thay đổi.