fix(core): incorrectly throwing error for self-referencing component (#50559)

Components are implied to be self-referencing, but if they explicitly set themselves in the `imports` array, they would throw an error because we weren't filtering them out.

Fixes #50525.

PR Close #50559
This commit is contained in:
Kristiyan Kostadinov
2023-06-02 23:13:27 +02:00
committed by Alex Rickabaugh
parent ef35ccd522
commit 2b6da93e19
2 changed files with 65 additions and 3 deletions
+1 -1
View File
@@ -245,7 +245,7 @@ function getStandaloneDefFunctions(type: Type<any>, imports: Type<any>[]): {
// Standalone components are always able to self-reference, so include the component's own
// definition in its `directiveDefs`.
cachedDirectiveDefs = [getComponentDef(type)!];
const seen = new Set<Type<unknown>>();
const seen = new Set<Type<unknown>>([type]);
for (const rawDep of imports) {
ngDevMode && verifyStandaloneImport(rawDep, type);
@@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {DOCUMENT} from '@angular/common';
import {ApplicationRef, Component, ComponentRef, createComponent, createEnvironmentInjector, Directive, ElementRef, EmbeddedViewRef, EnvironmentInjector, inject, Injectable, InjectionToken, Injector, Input, NgModule, OnDestroy, reflectComponentType, Renderer2, Type, ViewChild, ViewContainerRef, ViewEncapsulation, ɵsetDocument} from '@angular/core';
import {DOCUMENT, NgIf} from '@angular/common';
import {ApplicationRef, Component, ComponentRef, createComponent, createEnvironmentInjector, Directive, ElementRef, EmbeddedViewRef, EnvironmentInjector, forwardRef, inject, Injectable, InjectionToken, Injector, Input, NgModule, OnDestroy, reflectComponentType, Renderer2, Type, ViewChild, ViewContainerRef, ViewEncapsulation, ɵsetDocument} from '@angular/core';
import {stringifyForError} from '@angular/core/src/render3/util/stringify_utils';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
@@ -400,6 +400,68 @@ describe('component', () => {
.toThrowError(
/NG0300: Multiple components match node with tagname comp: CompA and CompB/);
});
it('should not throw if a standalone component imports itself', () => {
@Component({
selector: 'comp',
template: '<comp *ngIf="recurse"/>hello',
standalone: true,
imports: [Comp, NgIf]
})
class Comp {
@Input() recurse = false;
}
@Component({
template: '<comp [recurse]="true"/>',
standalone: true,
imports: [Comp],
})
class App {
}
let textContent = '';
expect(() => {
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
textContent = fixture.nativeElement.textContent.trim();
}).not.toThrow();
// Ensure that the component actually rendered.
expect(textContent).toBe('hellohello');
});
it('should not throw if a standalone component imports itself using a forwardRef', () => {
@Component({
selector: 'comp',
template: '<comp *ngIf="recurse"/>hello',
standalone: true,
imports: [forwardRef(() => Comp), NgIf]
})
class Comp {
@Input() recurse = false;
}
@Component({
template: '<comp [recurse]="true"/>',
standalone: true,
imports: [Comp],
})
class App {
}
let textContent = '';
expect(() => {
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
textContent = fixture.nativeElement.textContent.trim();
}).not.toThrow();
// Ensure that the component actually rendered.
expect(textContent).toBe('hellohello');
});
});
it('should use a new ngcontent attribute for child elements created w/ Renderer2', () => {