|
7 | 7 | */
|
8 | 8 |
|
9 | 9 | import {CommonModule} from '@angular/common';
|
10 |
| -import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, ContentChild, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; |
| 10 | +import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; |
11 | 11 | import {ɵINJECTOR_SCOPE} from '@angular/core/src/core';
|
12 | 12 | import {ViewRef as ViewRefInternal} from '@angular/core/src/render3/view_ref';
|
13 | 13 | import {TestBed} from '@angular/core/testing';
|
@@ -599,6 +599,93 @@ describe('di', () => {
|
599 | 599 | expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for DirectiveB/);
|
600 | 600 | });
|
601 | 601 |
|
| 602 | + it('should not have access to the directive injector in a standalone injector from within a directive-level provider factory', |
| 603 | + () => { |
| 604 | + // https://github.com/angular/angular/issues/42651 |
| 605 | + class TestA { |
| 606 | + constructor(public injector: string) {} |
| 607 | + } |
| 608 | + class TestB { |
| 609 | + constructor(public a: TestA) {} |
| 610 | + } |
| 611 | + |
| 612 | + function createTestB() { |
| 613 | + // Setup a standalone injector that provides `TestA`, which is resolved from a |
| 614 | + // standalone child injector that requests `TestA` as a dependency for `TestB`. |
| 615 | + // Although we're inside a directive factory and therefore have access to the |
| 616 | + // directive-level injector, `TestA` has to be resolved from the standalone injector. |
| 617 | + const parent = Injector.create({ |
| 618 | + providers: [{provide: TestA, useFactory: () => new TestA('standalone'), deps: []}], |
| 619 | + name: 'TestA', |
| 620 | + }); |
| 621 | + const child = Injector.create({ |
| 622 | + providers: [{provide: TestB, useClass: TestB, deps: [TestA]}], |
| 623 | + parent, |
| 624 | + name: 'TestB', |
| 625 | + }); |
| 626 | + return child.get(TestB); |
| 627 | + } |
| 628 | + |
| 629 | + @Component({ |
| 630 | + template: '', |
| 631 | + providers: [ |
| 632 | + {provide: TestA, useFactory: () => new TestA('component'), deps: []}, |
| 633 | + {provide: TestB, useFactory: createTestB}, |
| 634 | + ], |
| 635 | + }) |
| 636 | + class MyComp { |
| 637 | + constructor(public readonly testB: TestB) {} |
| 638 | + } |
| 639 | + |
| 640 | + TestBed.configureTestingModule({declarations: [MyComp]}); |
| 641 | + |
| 642 | + const cmp = TestBed.createComponent(MyComp); |
| 643 | + expect(cmp.componentInstance.testB).toBeInstanceOf(TestB); |
| 644 | + expect(cmp.componentInstance.testB.a.injector).toBe('standalone'); |
| 645 | + }); |
| 646 | + |
| 647 | + it('should not have access to the directive injector in a standalone injector from within a directive-level provider factory', |
| 648 | + () => { |
| 649 | + class TestA { |
| 650 | + constructor(public injector: string) {} |
| 651 | + } |
| 652 | + class TestB { |
| 653 | + constructor(public a: TestA|null) {} |
| 654 | + } |
| 655 | + |
| 656 | + function createTestB() { |
| 657 | + // Setup a standalone injector that provides `TestB` with an optional dependency of |
| 658 | + // `TestA`. Since `TestA` is not provided by the standalone injector it should resolve |
| 659 | + // to null; both the NgModule providers and the component-level providers should not |
| 660 | + // be considered. |
| 661 | + const injector = Injector.create({ |
| 662 | + providers: [{provide: TestB, useClass: TestB, deps: [[TestA, new Optional()]]}], |
| 663 | + name: 'TestB', |
| 664 | + }); |
| 665 | + return injector.get(TestB); |
| 666 | + } |
| 667 | + |
| 668 | + @Component({ |
| 669 | + template: '', |
| 670 | + providers: [ |
| 671 | + {provide: TestA, useFactory: () => new TestA('component'), deps: []}, |
| 672 | + {provide: TestB, useFactory: createTestB}, |
| 673 | + ], |
| 674 | + }) |
| 675 | + class MyComp { |
| 676 | + constructor(public readonly testB: TestB) {} |
| 677 | + } |
| 678 | + |
| 679 | + TestBed.configureTestingModule({ |
| 680 | + declarations: [MyComp], |
| 681 | + providers: [{provide: TestA, useFactory: () => new TestA('module'), deps: []}] |
| 682 | + }); |
| 683 | + |
| 684 | + const cmp = TestBed.createComponent(MyComp); |
| 685 | + expect(cmp.componentInstance.testB).toBeInstanceOf(TestB); |
| 686 | + expect(cmp.componentInstance.testB.a).toBeNull(); |
| 687 | + }); |
| 688 | + |
602 | 689 | onlyInIvy('Ivy has different error message for circular dependency')
|
603 | 690 | .it('should throw if directives try to inject each other', () => {
|
604 | 691 | @Directive({selector: '[dirB]'})
|
|
0 commit comments