Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion packages/core/src/render3/component_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import {debugStringifyTypeForError, stringifyForError} from './util/stringify_ut
import {getComponentLViewByIndex, getNativeByTNode, getTNode} from './util/view_utils';
import {ViewRef} from './view_ref';
import {ChainedInjector} from './chained_injector';
import {unregisterLView} from './interfaces/lview_tracking';

export class ComponentFactoryResolver extends AbstractComponentFactoryResolver {
/**
Expand Down Expand Up @@ -333,6 +334,7 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {

let component: T;
let tElementNode: TElementNode;
let componentView: LView | null = null;

try {
const rootComponentDef = this.componentDef;
Expand All @@ -354,7 +356,7 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
}

const hostTNode = createRootComponentTNode(rootLView, hostRNode);
const componentView = createRootComponentView(
componentView = createRootComponentView(
hostTNode,
hostRNode,
rootComponentDef,
Expand Down Expand Up @@ -388,6 +390,14 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
[LifecycleHooksFeature],
);
renderView(rootTView, rootLView, null);
} catch (e) {
// Stop tracking the views if creation failed since
// the consumer won't have a way to dereference them.
if (componentView !== null) {
unregisterLView(componentView);
}
unregisterLView(rootLView);
throw e;
} finally {
leaveView();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,7 @@ describe('change detection for transplanted views', () => {
// goes and marks the root view dirty, which then starts the process all over again by
// checking the declaration.
expect(() => appRef.tick()).not.toThrow();
app.destroy();
});
it('does not cause infinite loops with exhaustive checkNoChanges', async () => {
TestBed.configureTestingModule({
Expand Down
5 changes: 5 additions & 0 deletions packages/core/test/acceptance/component_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,7 @@ describe('component', () => {
componentRef.instance.name = 'ZoneJS';
componentRef.changeDetectorRef.detectChanges();
expect(hostElement.textContent).toBe('Hello ZoneJS!');
componentRef.destroy();
});

it('should create an instance of an NgModule-based component', () => {
Expand Down Expand Up @@ -797,6 +798,7 @@ describe('component', () => {

componentRef.changeDetectorRef.detectChanges();
expect(hostElement.innerHTML.replace(/\s*/g, '')).toBe('<p>1</p>|<p>2</p>|<p>3</p>');
componentRef.destroy();
});

it('should be able to inject tokens from EnvironmentInjector', () => {
Expand All @@ -817,6 +819,7 @@ describe('component', () => {
componentRef.changeDetectorRef.detectChanges();

expect(hostElement.textContent).toBe('Token: EnvironmentInjector(A)');
componentRef.destroy();
});

it('should be able to use NodeInjector from the node hierarchy', () => {
Expand Down Expand Up @@ -890,6 +893,7 @@ describe('component', () => {
expect(hostElement.tagName.toLowerCase()).toBe(selector);

expect(hostElement.textContent).toBe('Hello Angular!');
componentRef.destroy();
});

it(
Expand Down Expand Up @@ -917,6 +921,7 @@ describe('component', () => {
expect(hostElement.tagName.toLowerCase()).toBe('div');

expect(hostElement.textContent).toBe('Hello Angular!');
componentRef.destroy();
},
);

Expand Down
1 change: 1 addition & 0 deletions packages/core/test/acceptance/content_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,7 @@ describe('projection', () => {
componentRef.changeDetectorRef.detectChanges();

expect(getElementHtml(hostElement)).toContain('<p>override</p>|Two fallback|Three fallback');
componentRef.destroy();
});

it('should render fallback content when ng-content is inside an ng-template', () => {
Expand Down
19 changes: 12 additions & 7 deletions packages/core/test/acceptance/view_container_ref_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,15 @@ describe('ViewContainerRef', () => {

// Add a test component to the view container ref to ensure that
// the "ng-container" comment was used as marker for the insertion.
vcref.createComponent(HelloComp);
const ref = vcref.createComponent(HelloComp);
fixture.detectChanges();

expect(testParent.textContent).toBe('hello');
expect(testParent.childNodes.length).toBe(2);
expect(testParent.childNodes[0].nodeType).toBe(Node.ELEMENT_NODE);
expect(testParent.childNodes[0].textContent).toBe('hello');
expect(testParent.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
ref.destroy();
});

it('should support attribute selectors in dynamically created components', () => {
Expand All @@ -167,7 +168,7 @@ describe('ViewContainerRef', () => {
@ViewChild('container', {read: ViewContainerRef}) vcRef!: ViewContainerRef;

createComponent() {
this.vcRef.createComponent(HelloComp);
return this.vcRef.createComponent(HelloComp);
}
}

Expand All @@ -176,9 +177,10 @@ describe('ViewContainerRef', () => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML).not.toContain('Hello');

fixture.componentInstance.createComponent();
const ref = fixture.componentInstance.createComponent();
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML).toContain('Hello');
ref.destroy();
});

it('should view queries in dynamically created components', () => {
Expand Down Expand Up @@ -294,11 +296,11 @@ describe('ViewContainerRef', () => {
) {}

createComponentViaVCRef() {
this.vcRef.createComponent(HelloComp);
return this.vcRef.createComponent(HelloComp);
}

createComponentViaFactory() {
createComponent(HelloComp, {
return createComponent(HelloComp, {
environmentInjector: this.injector,
hostElement: this.elementRef.nativeElement.querySelector('#factory'),
});
Expand All @@ -308,8 +310,8 @@ describe('ViewContainerRef', () => {
TestBed.configureTestingModule({declarations: [TestComp, HelloComp]});
const fixture = TestBed.createComponent(TestComp);
fixture.detectChanges();
fixture.componentInstance.createComponentViaVCRef();
fixture.componentInstance.createComponentViaFactory();
const firstRef = fixture.componentInstance.createComponentViaVCRef();
const secondRef = fixture.componentInstance.createComponentViaFactory();
fixture.detectChanges();

// Verify host element for a component created via `vcRef.createComponent` method
Expand All @@ -336,6 +338,8 @@ describe('ViewContainerRef', () => {
// Make sure selector-based attrs and classes were not added to the host element
expect(factoryHostElement.classList.contains('class-a')).toBe(false);
expect(factoryHostElement.getAttribute('attr-c')).toBe(null);
firstRef.destroy();
secondRef.destroy();
});
});

Expand Down Expand Up @@ -1534,6 +1538,7 @@ describe('ViewContainerRef', () => {
fixture.componentInstance.viewContainerRef.createComponent(DynamicComponent);
const element = componentRef.location.nativeElement;
expect((element.namespaceURI || '').toLowerCase()).not.toContain('svg');
componentRef.destroy();
});

it('should be compatible with componentRef generated via TestBed.createComponent in component factory', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapRNode"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapRNode"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapRNode"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/bundling/defer/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -2498,6 +2498,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapRNode"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1889,6 +1889,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "untracked"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "untracked"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapRNode"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapRNode"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/bundling/router/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -2159,6 +2159,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapElementRef"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapRNode"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/bundling/todo/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,9 @@
{
"name": "uniqueIdCounter"
},
{
"name": "unregisterLView"
},
{
"name": "unwrapRNode"
},
Expand Down
1 change: 1 addition & 0 deletions packages/core/test/linker/regression_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ describe('regressions', () => {
});

expect(compRef.location.nativeElement.hasAttribute('ng-version')).toBe(false);
compRef.destroy();
});
});

Expand Down
4 changes: 3 additions & 1 deletion packages/core/test/render3/reactivity_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ describe('effects', () => {
),
);
await expectAsync(p).toBeResolvedTo([false, true]);
componentRef.destroy();
});

it('should propagate errors to the ErrorHandler', () => {
Expand Down Expand Up @@ -543,7 +544,7 @@ describe('effects', () => {
const fixture = TestBed.createComponent(DriverCmp);
fixture.detectChanges();

fixture.componentInstance.vcr.createComponent(TestCmp);
const ref = fixture.componentInstance.vcr.createComponent(TestCmp);

// Verify that simply creating the component didn't schedule the effect.
TestBed.flushEffects();
Expand All @@ -552,6 +553,7 @@ describe('effects', () => {
// Running change detection should schedule and run the effect.
fixture.detectChanges();
expect(log).toEqual(['init', 'effect']);
ref.destroy();
});

it('when created in a service provided in a component', () => {
Expand Down