diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 5d21b2ba90..2c1cbd4b8c 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -1463,8 +1463,8 @@ const LContainerArray: any = ((typeof ngDevMode === 'undefined' || ngDevMode) && export function createLContainer( hostNative: RElement | RComment | LView, currentView: LView, native: RComment, tNode: TNode, isForViewContainerRef?: boolean): LContainer { - ngDevMode && assertDomNode(native); ngDevMode && assertLView(currentView); + ngDevMode && !isProceduralRenderer(currentView[RENDERER]) && assertDomNode(native); // https://jsperf.com/array-literal-vs-new-array-really const lContainer: LContainer = new (ngDevMode ? LContainerArray : Array)( hostNative, // host native diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index 9497e6b4e9..5987eb4dd6 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -93,7 +93,7 @@ function applyToElementOrContainer( lNodeToHandle = lNodeToHandle[HOST] !; } const rNode: RNode = unwrapRNode(lNodeToHandle); - ngDevMode && assertDomNode(rNode); + ngDevMode && !isProceduralRenderer(renderer) && assertDomNode(rNode); if (action === WalkTNodeTreeAction.Create && parent !== null) { if (beforeNode == null) { diff --git a/packages/core/src/render3/util/view_utils.ts b/packages/core/src/render3/util/view_utils.ts index 02492b2594..8de1909721 100644 --- a/packages/core/src/render3/util/view_utils.ts +++ b/packages/core/src/render3/util/view_utils.ts @@ -11,9 +11,9 @@ import {assertTNodeForLView} from '../assert'; import {LContainer, TYPE} from '../interfaces/container'; import {LContext, MONKEY_PATCH_KEY_NAME} from '../interfaces/context'; import {TNode} from '../interfaces/node'; -import {RNode} from '../interfaces/renderer'; +import {RNode, isProceduralRenderer} from '../interfaces/renderer'; import {isLContainer, isLView} from '../interfaces/type_checks'; -import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, TData, TVIEW} from '../interfaces/view'; +import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, RENDERER, TData, TVIEW} from '../interfaces/view'; @@ -93,7 +93,7 @@ export function getNativeByTNode(tNode: TNode, lView: LView): RNode { ngDevMode && assertTNodeForLView(tNode, lView); ngDevMode && assertDataInRange(lView, tNode.index); const node: RNode = unwrapRNode(lView[tNode.index]); - ngDevMode && assertDomNode(node); + ngDevMode && !isProceduralRenderer(lView[RENDERER]) && assertDomNode(node); return node; } @@ -110,7 +110,7 @@ export function getNativeByTNodeOrNull(tNode: TNode, lView: LView): RNode|null { if (index !== -1) { ngDevMode && assertTNodeForLView(tNode, lView); const node: RNode|null = unwrapRNode(lView[index]); - ngDevMode && node !== null && assertDomNode(node); + ngDevMode && node !== null && !isProceduralRenderer(lView[RENDERER]) && assertDomNode(node); return node; } return null; diff --git a/packages/core/test/acceptance/renderer_factory_spec.ts b/packages/core/test/acceptance/renderer_factory_spec.ts index 5f55f4aabb..324d08b5a7 100644 --- a/packages/core/test/acceptance/renderer_factory_spec.ts +++ b/packages/core/test/acceptance/renderer_factory_spec.ts @@ -198,3 +198,49 @@ function getAnimationRendererFactory2(document: any): RendererFactory2 { document.body, new MockAnimationDriver(), new ɵNoopAnimationStyleNormalizer()), fakeNgZone); } + +describe('custom renderer', () => { + @Component({ + selector: 'some-component', + template: `
`, + }) + class SomeComponent { + } + + /** + * Creates a patched renderer factory that creates elements with a shape different than DOM node + */ + function createPatchedRendererFactory(document: any) { + let rendererFactory = getRendererFactory2(document); + const origCreateRenderer = rendererFactory.createRenderer; + rendererFactory.createRenderer = function(element: any, type: RendererType2|null) { + const renderer = origCreateRenderer.call(this, element, type); + renderer.appendChild = () => {}; + renderer.createElement = (name: string) => ({ + name, + el: document.createElement(name), + }); + return renderer; + }; + + return rendererFactory; + } + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [SomeComponent], + providers: [{ + provide: RendererFactory2, + useFactory: (document: any) => createPatchedRendererFactory(document), + deps: [DOCUMENT] + }] + }); + }); + + it('should not trigger errors', () => { + expect(() => { + const fixture = TestBed.createComponent(SomeComponent); + fixture.detectChanges(); + }).not.toThrow(); + }); +}); \ No newline at end of file