Advanced DI
Angular DI faqat class'lardan iborat emas — InjectionToken, factory provider va multi provider kabi kengaytirilgan imkoniyatlar mavjud. Ilova ishga tushishdan oldin konfiguratsiya yuklash ham shu yerda.
InjectionToken — non-class dependency
Nima bu?
Ba'zi dependency'lar class emas — string, ob'ekt, funksiya yoki interface. TypeScript interface runtime'da yo'qoladi, shuning uchun InjectionTokenT`` yaratiladi. Token orqali DI tizimida aniq identifikatsiya qilinadi.
Ikkita klassik capqon: (1) provider ro'yxatga qo'shilmagan tokenni inject(TOKEN) bilan olishga urinsangiz, Angular NullInjectorError tashlaydi — oldini olish uchun inject(TOKEN, { optional: true }) yoki eski @Optional() ishlatiladi. (2) Har safar bootstrapApplication'da qo'lda provider yozish o'rniga, tokenni o'zi tree-shakable qilib e'lon qilish mumkin: new InjectionToken<AppConfig>('app.config', { providedIn: 'root', factory: () => defaultConfig }) — bu holda alohida providers yozish shart emas, token ishlatilmasa bundle'dan chiqib ketadi.
Kod misoli
import { InjectionToken, inject } from '@angular/core';
export interface AppConfig {
apiUrl: string;
featureFlags: Record<string, boolean>;
}
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
// main.ts
bootstrapApplication(AppComponent, {
providers: [
{
provide: APP_CONFIG,
useValue: {
apiUrl: 'https://api.example.com',
featureFlags: { darkMode: true, betaSearch: false },
},
},
],
});
@Injectable({ providedIn: 'root' })
export class ApiService {
private config = inject(APP_CONFIG);
fetchUsers(): Observable<User[]> {
return this.http.get<User[]>(`${this.config.apiUrl}/users`);
}
}
Imtihonda
Savol: Nima uchun 'API_URL' as any o'rniga InjectionToken?
Javob: String token collision xavfi bor (turli modullar bir xil string). InjectionToken unique reference — tip xavfsizligi va tree-shaking uchun standart yondashuv.
Savol: Provider berilmagan tokenni inject() qilsangiz nima bo'ladi va qanday oldini olasiz?
Javob: NullInjectorError tashlanadi. inject(TOKEN, { optional: true }) bilan null qaytariladi, yoki tokenni factory bilan providedIn: 'root' qilib e'lon qilib default qiymat berasiz.
Yodlash uchun
useClass, useValue, useFactory, useExisting
Nima bu?
Provider recipe turlari:
useClass— class instance yarat (default).useValue— tayyor qiymat (ob'ekt, string).useFactory— funksiya orqali dinamik yaratish (boshqa dependency kerak bo'lsa).useExisting— boshqa token'ga alias (bir instance, ikki nom).
Kod misoli
abstract class Logger {
abstract log(msg: string): void;
}
class ConsoleLogger implements Logger {
log(msg: string): void { console.log(msg); }
}
class RemoteLogger implements Logger {
log(msg: string): void { /* API ga yuborish */ }
}
const IS_PROD = new InjectionToken<boolean>('isProd');
bootstrapApplication(AppComponent, {
providers: [
// useValue
{ provide: IS_PROD, useValue: environment.production },
// useClass — shartli
{
provide: Logger,
useClass: environment.production ? RemoteLogger : ConsoleLogger,
},
// useFactory
{
provide: APP_CONFIG,
useFactory: (isProd: boolean) => ({
apiUrl: isProd ? 'https://api.prod.com' : 'http://localhost:3000',
}),
deps: [IS_PROD],
},
// useExisting — alias
{ provide: 'LegacyLogger', useExisting: Logger },
],
});
Imtihonda
Savol: useExisting va useClass farqi?
Javob: useClass — yangi instance yaratadi. useExisting — mavjud token'ning xuddi shu instance'iga ishora (alias, singleton saqlanadi).
Yodlash uchun
multi: true provider
Nima bu?
Odatda bir token — bitta provider. multi: true bo'lsa, bir xil token uchun bir nechta provider ro'yxatga olinadi va inject qilganda array qaytariladi. Interceptor'lar, APP_INITIALIZER va plugin arxitektura uchun ishlatiladi.
Kod misoli
export const VALIDATORS = new InjectionToken<(value: string) => string | null>(
'validators'
);
// Modul A
providers: [
{
provide: VALIDATORS,
useValue: (v: string) => (v.length < 3 ? 'Kamida 3 belgi' : null),
multi: true,
},
]
// Modul B
providers: [
{
provide: VALIDATORS,
useValue: (v: string) => (/[A-Z]/.test(v) ? null : 'Katta harf kerak'),
multi: true,
},
]
@Injectable({ providedIn: 'root' })
export class ValidationService {
private validators = inject(VALIDATORS); // ValidatorFn[]
validate(value: string): string | null {
for (const fn of this.validators) {
const error = fn(value);
if (error) return error;
}
return null;
}
}
// HTTP_INTERCEPTORS ham multi: true
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
Imtihonda
Savol: multi: true bo'lmaganda ikkinchi provider nima qiladi?
Javob: Birinchisini ustiga yozadi (override). Faqat bitta provider qoladi — interceptor zanjiri buziladi.
Yodlash uchun
APP_INITIALIZER — ilova ishga tushishdan oldin
Nima bu?
APP_INITIALIZER — ilova bootstrap qilinishidan oldin bajariladigan funksiyalar. Konfiguratsiya yuklash, auth token tekshirish, i18n fayllar olish uchun. Funksiya Promise yoki Observable qaytarsa, Angular kutadi.
Kod misoli
import { APP_INITIALIZER, inject } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class ConfigLoaderService {
private http = inject(HttpClient);
config: AppConfig | null = null;
load(): Promise<void> {
return firstValueFrom(
this.http.get<AppConfig>('/assets/config.json')
).then((cfg) => {
this.config = cfg;
});
}
}
export function initializeApp(): () => Promise<void> {
const loader = inject(ConfigLoaderService);
return () => loader.load();
}
bootstrapApplication(AppComponent, {
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
multi: true,
deps: [], // inject() ishlatilgani uchun deps bo'sh
},
],
});
Imtihonda
Savol: APP_INITIALIZER juda sekin bo'lsa nima bo'ladi?
Javob: Butun ilova osilib qoladi — foydalanuvchi blank screen ko'radi. Kritik bo'lmagan yuklashni component darajasiga ko'chirish yoki timeout qo'yish kerak.
Yodlash uchun
ENVIRONMENT_INITIALIZER (v14+)
Nima bu?
ENVIRONMENT_INITIALIZER — environment injector yaratilganda (bootstrap vaqtida) ishlaydigan funksiya. APP_INITIALIZER dan farqi: sinxron void qaytaradi, Promise kutmaydi. Global sozlash, third-party kutubxona init, telemetriya registratsiya uchun.
Kod misoli
import { ENVIRONMENT_INITIALIZER, inject } from '@angular/core';
export function setupAnalytics(): void {
const router = inject(Router);
router.events.pipe(
filter((e): e is NavigationEnd => e instanceof NavigationEnd)
).subscribe((e) => {
analytics.trackPageView(e.urlAfterRedirects);
});
}
bootstrapApplication(AppComponent, {
providers: [
{
provide: ENVIRONMENT_INITIALIZER,
useValue: () => setupAnalytics(),
multi: true,
},
{
provide: ENVIRONMENT_INITIALIZER,
useValue: () => {
console.log('Environment ready:', new Date().toISOString());
},
multi: true,
},
],
});
Imtihonda
Savol: APP_INITIALIZER vs ENVIRONMENT_INITIALIZER — qaysi biri async config uchun?
Javob: APP_INITIALIZER — Promise/Observable kutadi, config yuklash uchun. ENVIRONMENT_INITIALIZER — sinxron init (listener, global flag), bootstrap bloklamaydi.
Yodlash uchun
