{{ product.name }}
{{ product.price | currency:'USD' }}
@if (product.inStock) { } @else { Out of Stock }# Top 25 Câu Hỏi Phỏng Vấn Angular: Hướng Dẫn Đầy Đủ Để Thành Công > 25 câu hỏi phỏng vấn Angular được hỏi nhiều nhất năm 2026. Câu trả lời chi tiết, ví dụ mã và mẹo để giành vị trí lập trình viên Angular. - Published: 2026-02-04 - Updated: 2026-04-27 - Author: SharpSkill - Tags: angular interview, frontend interview, angular questions, typescript, technical interview - Reading time: 16 min --- Phỏng vấn kỹ thuật Angular đánh giá hiểu biết về kiến trúc framework, mức độ thành thạo TypeScript và các thực hành tốt trong phát triển frontend. Hướng dẫn này trình bày 25 câu hỏi được hỏi nhiều nhất kèm theo câu trả lời chi tiết và ví dụ mã để chuẩn bị một cách hiệu quả. > **Mẹo chuẩn bị** > > Các câu hỏi này bao quát các phiên bản Angular gần đây (16+), gồm Signals, component standalone và control flow mới. Việc nắm vững các khái niệm hiện đại này thể hiện sự cập nhật công nghệ một cách chủ động. ## Nền tảng Angular ### 1. Sự khác biệt giữa Angular và AngularJS là gì? Angular (phiên bản 2 trở lên) là phiên bản viết lại hoàn toàn của AngularJS. Khác biệt chính nằm ở kiến trúc, ngôn ngữ và hiệu năng. AngularJS sử dụng JavaScript và mô hình MVC với hệ thống two-way binding có thể gây ra vấn đề hiệu năng. Angular dùng TypeScript, kiến trúc dựa trên component và hệ thống change detection được tối ưu. ```typescript // AngularJS (1.x) - Controller-based // angular.module('app').controller('UserController', function($scope) { // $scope.user = { name: 'Alice' }; // }); // Angular (2+) - Component-based // user.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-user', standalone: true, template: `
{{ user.email }}
{{ product.price | currency:'USD' }}
@if (product.inStock) { } @else { Out of Stock }{{ data }}
` }) export class LifecycleDemoComponent implements OnInit, OnChanges, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy { @Input() data = ''; // 1. Called when an @Input changes (before ngOnInit) ngOnChanges(changes: SimpleChanges) { console.log('ngOnChanges', changes); } // 2. Called once after the first ngOnChanges // Ideal for initializations ngOnInit() { console.log('ngOnInit - Component initialization'); } // 3. Called on every change detection cycle // Use with caution (performance) ngDoCheck() { console.log('ngDoCheck'); } // 4. After content projection (ng-content) ngAfterContentInit() { console.log('ngAfterContentInit'); } // 5. After each projected content check ngAfterContentChecked() { console.log('ngAfterContentChecked'); } // 6. After component view initialization // @ViewChild references are available here ngAfterViewInit() { console.log('ngAfterViewInit - View initialized'); } // 7. After each view check ngAfterViewChecked() { console.log('ngAfterViewChecked'); } // 8. Just before component destruction // Cleanup: unsubscribe, clearInterval, etc. ngOnDestroy() { console.log('ngOnDestroy - Cleanup'); } } ``` Các hook hay dùng nhất là `ngOnInit` để khởi tạo, `ngOnChanges` để phản ứng với thay đổi của input và `ngOnDestroy` để dọn dẹp tài nguyên. ### 4. Data Binding trong Angular là gì? Data binding kết nối dữ liệu của component với template. Angular cung cấp bốn dạng binding. ```typescript // data-binding.component.ts import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-data-binding', standalone: true, imports: [FormsModule], template: `{{ getFullName() }}
Hello, {{ username }}
` }) export class DataBindingComponent { // Properties for interpolation title = 'My Application'; firstName = 'John'; lastName = 'Doe'; // Properties for property binding imageUrl = '/assets/logo.png'; imageAlt = 'Application logo'; isLoading = false; // Property for two-way binding username = ''; getFullName(): string { return `${this.firstName} ${this.lastName}`; } handleClick(): void { console.log('Button clicked'); } onEnter(event: KeyboardEvent): void { const target = event.target as HTMLInputElement; console.log('Entered value:', target.value); } } ``` Two-way binding `[(ngModel)]` là sự kết hợp giữa property binding và event binding, cho phép đồng bộ tự động giữa model và view. ### 5. Sự khác biệt giữa Module và Standalone Component là gì? NgModules nhóm các component, directive và service liên quan với nhau. Component standalone (Angular 14+) cho phép tạo component độc lập mà không cần module. ```typescript // Traditional approach with NgModule // products.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ProductListComponent } from './product-list.component'; import { ProductCardComponent } from './product-card.component'; import { ProductService } from './product.service'; @NgModule({ // Components belonging to this module declarations: [ ProductListComponent, ProductCardComponent ], // Modules needed imports: [CommonModule], // Components usable outside exports: [ProductListComponent], // Services with module scope providers: [ProductService] }) export class ProductsModule {} // Modern approach with Standalone Components // product-list.component.ts import { Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ProductCardComponent } from './product-card.component'; import { ProductService } from './product.service'; @Component({ selector: 'app-product-list', // No NgModule needed standalone: true, // Direct dependency imports imports: [CommonModule, ProductCardComponent], template: ` @for (product of products(); track product.id) {{{ user.email }}
} ` }) export class UserProfileComponent implements OnInit { // Injection via inject() (recommended) private userService = inject(UserService); user$!: ObservableDouble: {{ doubleCount() }}
Message: {{ message() }}
No users found
}{{ data }}
` }) export class DefaultStrategyComponent { data = 'Hello'; } // OnPush strategy: checks only if inputs change // or if an event is triggered within the component @Component({ selector: 'app-onpush-strategy', changeDetection: ChangeDetectionStrategy.OnPush, template: `{{ data().name }}
` }) export class OnPushStrategyComponent { private cdr = inject(ChangeDetectorRef); // Signal: automatically triggers detection data = signal({ name: 'Alice' }); update() { // With signal, update is automatic this.data.set({ name: 'Bob' }); } // For cases where manual detection is needed manualUpdate() { // Mark component for checking this.cdr.markForCheck(); // Or force immediate detection this.cdr.detectChanges(); } } // Practical example: optimized list @Component({ selector: 'app-optimized-list', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: ` @for (item of items(); track item.id) {{{ message() }}
` }) export class ChildComponent { message = input.required{{ message$ | async }}
` }) export class ComponentB implements OnDestroy { private messageService = inject(MessageService); private destroy$ = new SubjectYour cart is empty
} @else { @for (item of cartStore.items(); track item.id) {{itemCount, plural, =0 {No items} =1 {One item} other {{{itemCount}} items} }
` }) export class AppComponent { itemCount = 5; } // 2. ngx-translate (runtime language switching) // app.config.ts import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http, './assets/i18n/', '.json'); } // Configuration provideTranslateService({ defaultLanguage: 'en', loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, deps: [HttpClient] } }) // language-switcher.component.ts @Component({ selector: 'app-language-switcher', standalone: true, imports: [TranslateModule], template: `{{ 'HOME.WELCOME' | translate:{ name: userName } }}
` }) export class LanguageSwitcherComponent { private translate = inject(TranslateService); userName = 'Alice'; changeLanguage(event: Event) { const lang = (event.target as HTMLSelectElement).value; this.translate.use(lang); } } // assets/i18n/en.json { "HOME": { "TITLE": "Welcome", "WELCOME": "Hello {{name}}!" } } // assets/i18n/fr.json { "HOME": { "TITLE": "Bienvenue", "WELCOME": "Bonjour {{name}} !" } } ``` Việc lựa chọn giữa i18n tích hợp sẵn và ngx-translate phụ thuộc vào nhu cầu: biên dịch riêng để có hiệu năng tốt nhất hoặc chuyển đổi động để linh hoạt hơn. ### 25. Đâu là các thực hành tốt nhất để cấu trúc một dự án Angular? Một cấu trúc được tổ chức tốt giúp việc bảo trì và mở rộng dự án dễ dàng hơn. ``` src/ ├── app/ │ ├── core/ # Singleton services, guards, interceptors │ │ ├── guards/ │ │ │ └── auth.guard.ts │ │ ├── interceptors/ │ │ │ └── auth.interceptor.ts │ │ ├── services/ │ │ │ ├── auth.service.ts │ │ │ └── api.service.ts │ │ └── core.provider.ts # Provider configuration │ │ │ ├── shared/ # Reusable components, pipes, directives │ │ ├── components/ │ │ │ ├── button/ │ │ │ └── modal/ │ │ ├── directives/ │ │ ├── pipes/ │ │ └── index.ts # Barrel exports │ │ │ ├── features/ # Feature modules (lazy-loaded) │ │ ├── products/ │ │ │ ├── components/ │ │ │ ├── services/ │ │ │ ├── models/ │ │ │ ├── products.routes.ts │ │ │ └── products.component.ts │ │ ├── cart/ │ │ └── checkout/ │ │ │ ├── layouts/ # Page layouts │ │ ├── main-layout/ │ │ └── auth-layout/ │ │ │ ├── app.component.ts │ ├── app.config.ts │ └── app.routes.ts │ ├── assets/ ├── environments/ └── styles/ ``` ```typescript // Best code practices // 1. Barrel exports to simplify imports // shared/index.ts export * from './components/button/button.component'; export * from './components/modal/modal.component'; export * from './pipes/format-date.pipe'; // 2. Centralized provider configuration // core/core.provider.ts import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { authInterceptor } from './interceptors/auth.interceptor'; export const coreProviders = [ provideHttpClient( withInterceptors([authInterceptor]) ), // Other global providers ]; // 3. Typed models // features/products/models/product.model.ts export interface Product { id: number; name: string; price: number; category: ProductCategory; createdAt: Date; } export type ProductCategory = 'electronics' | 'clothing' | 'books'; export interface CreateProductDto { name: string; price: number; category: ProductCategory; } // 4. Functional interceptor // core/interceptors/auth.interceptor.ts import { HttpInterceptorFn } from '@angular/common/http'; import { inject } from '@angular/core'; export const authInterceptor: HttpInterceptorFn = (req, next) => { const authService = inject(AuthService); const token = authService.getToken(); if (token) { req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } return next(req); }; ``` Các quy ước này cho phép điều hướng mã trực quan và tạo điều kiện thuận lợi cho việc cộng tác trong nhóm. ## Kết luận 25 câu hỏi này bao quát các khái niệm Angular thiết yếu được hỏi trong phỏng vấn. Những điểm chính cần làm chủ: - ✅ **Nền tảng**: component, data binding, vòng đời, DI - ✅ **Angular hiện đại**: Signals, component standalone, control flow mới - ✅ **Tính phản ứng**: RxJS, Observable, quản lý state với Signals - ✅ **Form**: Template-driven vs Reactive, kiểm tra hợp lệ tùy chỉnh - ✅ **Routing**: Guards, lazy loading, truyền dữ liệu - ✅ **Hiệu năng**: OnPush, defer, virtual scrolling - ✅ **Kiểm thử**: unit test, mock, HttpTestingController Chuẩn bị cho phỏng vấn Angular đòi hỏi thực hành đều đặn. Xây dựng các dự án cá nhân giúp củng cố kiến thức này và trình bày chúng một cách tự nhiên trong buổi phỏng vấn. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/angular/top-25-angular-interview-questions