fix(core): forbid destroyed views to be inserted or moved in VC
Fixes #18615
This commit is contained in:
parent
d7be4f12b5
commit
972538be7a
@ -187,6 +187,9 @@ class ViewContainerRef_ implements ViewContainerData {
|
||||
}
|
||||
|
||||
insert(viewRef: ViewRef, index?: number): ViewRef {
|
||||
if (viewRef.destroyed) {
|
||||
throw new Error('Cannot insert a destroyed View in a ViewContainer!');
|
||||
}
|
||||
const viewRef_ = <ViewRef_>viewRef;
|
||||
const viewData = viewRef_._view;
|
||||
attachEmbeddedView(this._view, this._data, index, viewData);
|
||||
@ -195,6 +198,9 @@ class ViewContainerRef_ implements ViewContainerData {
|
||||
}
|
||||
|
||||
move(viewRef: ViewRef_, currentIndex: number): ViewRef {
|
||||
if (viewRef.destroyed) {
|
||||
throw new Error('Cannot move a destroyed View in a ViewContainer!');
|
||||
}
|
||||
const previousIndex = this._embeddedViews.indexOf(viewRef._view);
|
||||
moveEmbeddedView(this._data, previousIndex, currentIndex);
|
||||
return viewRef;
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Compiler, ComponentFactory, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core';
|
||||
import {Compiler, ComponentFactory, ComponentRef, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, ReflectiveInjector, SkipSelf, ViewRef} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {ComponentFactoryResolver} from '@angular/core/src/linker/component_factory_resolver';
|
||||
@ -1030,7 +1030,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
describe('ViewContainerRef.createComponent', () => {
|
||||
describe('ViewContainerRef', () => {
|
||||
beforeEach(() => {
|
||||
// we need a module to declarate ChildCompUsingService as an entryComponent otherwise the
|
||||
// factory doesn't get created
|
||||
@ -1047,6 +1047,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
MyComp, {add: {template: '<div><dynamic-vp #dynamic></dynamic-vp></div>'}});
|
||||
});
|
||||
|
||||
describe('.createComponent', () => {
|
||||
it('should allow to create a component at any bound location', async(() => {
|
||||
const fixture = TestBed.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]})
|
||||
.createComponent(MyComp);
|
||||
@ -1149,7 +1150,8 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
expect(compRef.instance.someToken).toBe('someValue');
|
||||
});
|
||||
|
||||
it('should create a component with the NgModuleRef of the ComponentFactoryResolver', () => {
|
||||
it('should create a component with the NgModuleRef of the ComponentFactoryResolver',
|
||||
() => {
|
||||
@Component({template: ''})
|
||||
class RootComp {
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
@ -1175,11 +1177,13 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
class MyModule {
|
||||
}
|
||||
|
||||
const compFixture =
|
||||
TestBed.configureTestingModule({imports: [RootModule]}).createComponent(RootComp);
|
||||
const compFixture = TestBed.configureTestingModule({imports: [RootModule]})
|
||||
.createComponent(RootComp);
|
||||
const compiler = <Compiler>TestBed.get(Compiler);
|
||||
const myModule = compiler.compileModuleSync(MyModule).create(TestBed.get(NgModuleRef));
|
||||
const myCompFactory = myModule.componentFactoryResolver.resolveComponentFactory(MyComp);
|
||||
const myModule =
|
||||
compiler.compileModuleSync(MyModule).create(TestBed.get(NgModuleRef));
|
||||
const myCompFactory =
|
||||
myModule.componentFactoryResolver.resolveComponentFactory(MyComp);
|
||||
|
||||
// Note: MyComp was declared as entryComponent in MyModule,
|
||||
// and we don't pass an explicit ModuleRef to the createComponent call.
|
||||
@ -1189,6 +1193,40 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.insert', () => {
|
||||
it('should throw with destroyed views', async(() => {
|
||||
const fixture = TestBed.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]})
|
||||
.createComponent(MyComp);
|
||||
const tc = fixture.debugElement.children[0].children[0];
|
||||
const dynamicVp: DynamicViewport = tc.injector.get(DynamicViewport);
|
||||
const ref = dynamicVp.create();
|
||||
fixture.detectChanges();
|
||||
|
||||
ref.destroy();
|
||||
expect(() => {
|
||||
dynamicVp.insert(ref.hostView);
|
||||
}).toThrowError('Cannot insert a destroyed View in a ViewContainer!');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('.move', () => {
|
||||
it('should throw with destroyed views', async(() => {
|
||||
const fixture = TestBed.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]})
|
||||
.createComponent(MyComp);
|
||||
const tc = fixture.debugElement.children[0].children[0];
|
||||
const dynamicVp: DynamicViewport = tc.injector.get(DynamicViewport);
|
||||
const ref = dynamicVp.create();
|
||||
fixture.detectChanges();
|
||||
|
||||
ref.destroy();
|
||||
expect(() => {
|
||||
dynamicVp.move(ref.hostView, 1);
|
||||
}).toThrowError('Cannot move a destroyed View in a ViewContainer!');
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should support static attributes', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, NeedsAttribute]});
|
||||
const template = '<input static type="text" title>';
|
||||
@ -1856,7 +1894,15 @@ class DynamicViewport {
|
||||
componentFactoryResolver.resolveComponentFactory(ChildCompUsingService) !;
|
||||
}
|
||||
|
||||
create() { this.vc.createComponent(this.componentFactory, this.vc.length, this.injector); }
|
||||
create(): ComponentRef<ChildCompUsingService> {
|
||||
return this.vc.createComponent(this.componentFactory, this.vc.length, this.injector);
|
||||
}
|
||||
|
||||
insert(viewRef: ViewRef, index?: number): ViewRef { return this.vc.insert(viewRef, index); }
|
||||
|
||||
move(viewRef: ViewRef, currentIndex: number): ViewRef {
|
||||
return this.vc.move(viewRef, currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[my-dir]', inputs: ['dirProp: elprop'], exportAs: 'mydir'})
|
||||
|
Loading…
x
Reference in New Issue
Block a user