|
7 | 7 | */ |
8 | 8 |
|
9 | 9 | import {CommonModule} from '@angular/common'; |
10 | | -import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, createEnvironmentInjector, Directive, ElementRef, ENVIRONMENT_INITIALIZER, EnvironmentInjector, EventEmitter, forwardRef, Host, HostBinding, ImportedNgModuleProviders, importProvidersFrom, ImportProvidersSource, inject, Inject, Injectable, InjectFlags, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewChild, ViewContainerRef, ViewRef, ɵcreateInjector as createInjector, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵINJECTOR_SCOPE} from '@angular/core'; |
| 10 | +import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, createEnvironmentInjector, Directive, ElementRef, ENVIRONMENT_INITIALIZER, EnvironmentInjector, EventEmitter, forwardRef, Host, HostBinding, ImportedNgModuleProviders, importProvidersFrom, ImportProvidersSource, inject, Inject, Injectable, InjectFlags, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewChild, ViewContainerRef, ViewEncapsulation, ViewRef, ɵcreateInjector as createInjector, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵINJECTOR_SCOPE} from '@angular/core'; |
11 | 11 | import {ViewRef as ViewRefInternal} from '@angular/core/src/render3/view_ref'; |
12 | 12 | import {TestBed} from '@angular/core/testing'; |
13 | 13 | import {By} from '@angular/platform-browser'; |
@@ -3328,6 +3328,102 @@ describe('di', () => { |
3328 | 3328 | fixture.detectChanges(); |
3329 | 3329 | expect(fixture.nativeElement.innerHTML).toEqual('default value'); |
3330 | 3330 | }); |
| 3331 | + |
| 3332 | + describe('with an options object argument', () => { |
| 3333 | + it('should be able to optionally inject a service', () => { |
| 3334 | + const TOKEN = new InjectionToken<string>('TOKEN'); |
| 3335 | + |
| 3336 | + @Component({ |
| 3337 | + standalone: true, |
| 3338 | + template: '', |
| 3339 | + }) |
| 3340 | + class TestCmp { |
| 3341 | + value = inject(TOKEN, {optional: true}); |
| 3342 | + } |
| 3343 | + |
| 3344 | + expect(TestBed.createComponent(TestCmp).componentInstance.value).toBeNull(); |
| 3345 | + }); |
| 3346 | + |
| 3347 | + it('should be able to use skipSelf injection', () => { |
| 3348 | + const TOKEN = new InjectionToken<string>('TOKEN', { |
| 3349 | + providedIn: 'root', |
| 3350 | + factory: () => 'from root', |
| 3351 | + }); |
| 3352 | + @Component({ |
| 3353 | + standalone: true, |
| 3354 | + template: '', |
| 3355 | + providers: [{provide: TOKEN, useValue: 'from component'}], |
| 3356 | + }) |
| 3357 | + class TestCmp { |
| 3358 | + value = inject(TOKEN, {skipSelf: true}); |
| 3359 | + } |
| 3360 | + |
| 3361 | + expect(TestBed.createComponent(TestCmp).componentInstance.value).toEqual('from root'); |
| 3362 | + }); |
| 3363 | + |
| 3364 | + it('should be able to use self injection', () => { |
| 3365 | + const TOKEN = new InjectionToken<string>('TOKEN', { |
| 3366 | + providedIn: 'root', |
| 3367 | + factory: () => 'from root', |
| 3368 | + }); |
| 3369 | + |
| 3370 | + @Component({ |
| 3371 | + standalone: true, |
| 3372 | + template: '', |
| 3373 | + }) |
| 3374 | + class TestCmp { |
| 3375 | + value = inject(TOKEN, {self: true, optional: true}); |
| 3376 | + } |
| 3377 | + |
| 3378 | + expect(TestBed.createComponent(TestCmp).componentInstance.value).toBeNull(); |
| 3379 | + }); |
| 3380 | + |
| 3381 | + it('should be able to use host injection', () => { |
| 3382 | + const TOKEN = new InjectionToken<string>('TOKEN'); |
| 3383 | + |
| 3384 | + @Component({ |
| 3385 | + standalone: true, |
| 3386 | + selector: 'child', |
| 3387 | + template: '{{value}}', |
| 3388 | + }) |
| 3389 | + class ChildCmp { |
| 3390 | + value = inject(TOKEN, {host: true, optional: true}) ?? 'not found'; |
| 3391 | + } |
| 3392 | + |
| 3393 | + @Component({ |
| 3394 | + standalone: true, |
| 3395 | + imports: [ChildCmp], |
| 3396 | + template: '<child></child>', |
| 3397 | + providers: [{provide: TOKEN, useValue: 'from parent'}], |
| 3398 | + encapsulation: ViewEncapsulation.None, |
| 3399 | + }) |
| 3400 | + class ParentCmp { |
| 3401 | + } |
| 3402 | + |
| 3403 | + const fixture = TestBed.createComponent(ParentCmp); |
| 3404 | + fixture.detectChanges(); |
| 3405 | + expect(fixture.nativeElement.innerHTML).toEqual('<child>not found</child>'); |
| 3406 | + }); |
| 3407 | + |
| 3408 | + it('should not indicate it returns null when optional is explicitly false', () => { |
| 3409 | + const TOKEN = new InjectionToken<string>('TOKEN', { |
| 3410 | + providedIn: 'root', |
| 3411 | + factory: () => 'from root', |
| 3412 | + }); |
| 3413 | + |
| 3414 | + @Component({ |
| 3415 | + standalone: true, |
| 3416 | + template: '', |
| 3417 | + }) |
| 3418 | + class TestCmp { |
| 3419 | + // TypeScript will check if this assignment is legal, which won't be the case if |
| 3420 | + // inject() erroneously returns a `string|null` type here. |
| 3421 | + value: string = inject(TOKEN, {optional: false}); |
| 3422 | + } |
| 3423 | + |
| 3424 | + expect(TestBed.createComponent(TestCmp).componentInstance.value).toEqual('from root'); |
| 3425 | + }); |
| 3426 | + }); |
3331 | 3427 | }); |
3332 | 3428 |
|
3333 | 3429 | it('should be able to use Host in `useFactory` dependency config', () => { |
|
0 commit comments