From 00f13110bef435a2048c9d89527c63ece5b97d46 Mon Sep 17 00:00:00 2001 From: Marc Laval Date: Thu, 16 Aug 2018 17:43:29 +0200 Subject: [PATCH] feat(ivy): support injecting Renderer2 (#25523) PR Close #25523 --- .../src/ngtsc/annotations/src/util.ts | 3 ++ .../test/ngtsc/fake_core/index.ts | 1 + .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 4 +- packages/compiler/src/identifiers.ts | 1 + packages/compiler/src/render3/r3_factory.ts | 10 +++++ .../compiler/src/render3/r3_identifiers.ts | 2 + .../core/src/core_render3_private_export.ts | 1 + packages/core/src/render3/STATUS.md | 1 + packages/core/src/render3/di.ts | 21 ++++++++++- packages/core/src/render3/index.ts | 2 +- packages/core/src/render3/jit/environment.ts | 1 + .../bundling/todo/bundle.golden_symbols.json | 6 +++ .../todo_r2/bundle.golden_symbols.json | 6 +++ packages/core/test/render3/di_spec.ts | 37 ++++++++++++++++++- .../src/compiler_reflector.ts | 3 +- 15 files changed, 93 insertions(+), 6 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts index 21135f9233..33cc471d92 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts @@ -75,6 +75,9 @@ export function getConstructorDependencies( case 'ViewContainerRef': resolved = R3ResolvedDependencyType.ViewContainerRef; break; + case 'Renderer2': + resolved = R3ResolvedDependencyType.Renderer2; + break; default: // Leave as a Token or Attribute. } diff --git a/packages/compiler-cli/test/ngtsc/fake_core/index.ts b/packages/compiler-cli/test/ngtsc/fake_core/index.ts index 89e622ae45..209d2f93e1 100644 --- a/packages/compiler-cli/test/ngtsc/fake_core/index.ts +++ b/packages/compiler-cli/test/ngtsc/fake_core/index.ts @@ -49,6 +49,7 @@ export class ElementRef {} export class Injector {} export class TemplateRef {} export class ViewContainerRef {} +export class Renderer2 {} export class ɵNgModuleFactory { constructor(public clazz: T) {} } diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 71a0ac87dc..f130d35472 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -448,6 +448,7 @@ describe('ngtsc behavioral tests', () => { Component, ElementRef, Injector, + Renderer2, TemplateRef, ViewContainerRef, } from '@angular/core'; @@ -462,6 +463,7 @@ describe('ngtsc behavioral tests', () => { cdr: ChangeDetectorRef, er: ElementRef, i: Injector, + r2: Renderer2, tr: TemplateRef, vcr: ViewContainerRef, ) {} @@ -474,7 +476,7 @@ describe('ngtsc behavioral tests', () => { const jsContents = getContents('test.js'); expect(jsContents) .toContain( - `factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵinjectChangeDetectorRef(), i0.ɵinjectElementRef(), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵinjectTemplateRef(), i0.ɵinjectViewContainerRef()); }`); + `factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵinjectChangeDetectorRef(), i0.ɵinjectElementRef(), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵinjectRenderer2(), i0.ɵinjectTemplateRef(), i0.ɵinjectViewContainerRef()); }`); }); it('should generate queries for components', () => { diff --git a/packages/compiler/src/identifiers.ts b/packages/compiler/src/identifiers.ts index 121f97f21b..b1321675be 100644 --- a/packages/compiler/src/identifiers.ts +++ b/packages/compiler/src/identifiers.ts @@ -28,6 +28,7 @@ export class Identifiers { }; static QueryList: o.ExternalReference = {name: 'QueryList', moduleName: CORE}; static TemplateRef: o.ExternalReference = {name: 'TemplateRef', moduleName: CORE}; + static Renderer2: o.ExternalReference = {name: 'Renderer2', moduleName: CORE}; static CodegenComponentFactoryResolver: o.ExternalReference = { name: 'ɵCodegenComponentFactoryResolver', moduleName: CORE, diff --git a/packages/compiler/src/render3/r3_factory.ts b/packages/compiler/src/render3/r3_factory.ts index 00e91bba2a..2f993b9fe0 100644 --- a/packages/compiler/src/render3/r3_factory.ts +++ b/packages/compiler/src/render3/r3_factory.ts @@ -120,6 +120,11 @@ export enum R3ResolvedDependencyType { * The dependency is for `ChangeDetectorRef`. */ ChangeDetectorRef = 6, + + /** + * The dependency is for `Renderer2`. + */ + Renderer2 = 7, } /** @@ -285,6 +290,8 @@ function compileInjectDependency( return o.importExpr(R3.injectViewContainerRef).callFn([]); case R3ResolvedDependencyType.ChangeDetectorRef: return o.importExpr(R3.injectChangeDetectorRef).callFn([]); + case R3ResolvedDependencyType.Renderer2: + return o.importExpr(R3.injectRenderer2).callFn([]); default: return unsupported( `Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`); @@ -305,6 +312,7 @@ export function dependenciesFromGlobalMetadata( const templateRef = reflector.resolveExternalReference(Identifiers.TemplateRef); const viewContainerRef = reflector.resolveExternalReference(Identifiers.ViewContainerRef); const injectorRef = reflector.resolveExternalReference(Identifiers.Injector); + const renderer2 = reflector.resolveExternalReference(Identifiers.Renderer2); // Iterate through the type's DI dependencies and produce `R3DependencyMetadata` for each of them. const deps: R3DependencyMetadata[] = []; @@ -320,6 +328,8 @@ export function dependenciesFromGlobalMetadata( resolved = R3ResolvedDependencyType.ViewContainerRef; } else if (tokenRef === injectorRef) { resolved = R3ResolvedDependencyType.Injector; + } else if (tokenRef === renderer2) { + resolved = R3ResolvedDependencyType.Renderer2; } else if (dependency.isAttribute) { resolved = R3ResolvedDependencyType.Attribute; } diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 90b4dda1ba..2cdb217b0f 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -116,6 +116,8 @@ export class Identifiers { static injectChangeDetectorRef: o.ExternalReference = {name: 'ɵinjectChangeDetectorRef', moduleName: CORE}; + static injectRenderer2: o.ExternalReference = {name: 'ɵinjectRenderer2', moduleName: CORE}; + static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE}; static templateRefExtractor: diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index a89bc8ff7e..e220f346c1 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -25,6 +25,7 @@ export { injectTemplateRef as ɵinjectTemplateRef, injectViewContainerRef as ɵinjectViewContainerRef, injectChangeDetectorRef as ɵinjectChangeDetectorRef, + injectRenderer2 as ɵinjectRenderer2, injectAttribute as ɵinjectAttribute, getFactoryOf as ɵgetFactoryOf, getInheritedFactory as ɵgetInheritedFactory, diff --git a/packages/core/src/render3/STATUS.md b/packages/core/src/render3/STATUS.md index 50cb4bffea..20e966d2c1 100644 --- a/packages/core/src/render3/STATUS.md +++ b/packages/core/src/render3/STATUS.md @@ -216,6 +216,7 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S | `injectElementRef()` | ✅ | ✅ | ✅ | | `injectViewContainerRef()` | ✅ | ✅ | ✅ | | `injectTemplateRef()` | ✅ | ✅ | ✅ | +| `injectRenderer2()` | ✅ | ✅ | ✅ | | default `inject()` with no injector | ❌ | ❌ | ❌ | | sanitization with no injector | ✅ | ✅ | ❌ | diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index b4b5ac6e26..599d6c965b 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -19,6 +19,7 @@ import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory import {TemplateRef as viewEngine_TemplateRef} from '../linker/template_ref'; import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref'; import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref'; +import {Renderer2} from '../render'; import {Type} from '../type'; import {assertDefined, assertGreaterThan, assertLessThan} from './assert'; @@ -29,7 +30,7 @@ import {DirectiveDefInternal, RenderFlags} from './interfaces/definition'; import {LInjector} from './interfaces/injector'; import {AttributeMarker, LContainerNode, LElementContainerNode, LElementNode, LNode, LNodeWithLocalRefs, LViewNode, TContainerNode, TElementNode, TNodeFlags, TNodeType} from './interfaces/node'; import {LQueries, QueryReadType} from './interfaces/query'; -import {Renderer3} from './interfaces/renderer'; +import {Renderer3, isProceduralRenderer} from './interfaces/renderer'; import {DIRECTIVES, HOST_NODE, INJECTOR, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {addRemoveViewFromContainer, appendChild, detachView, getChildLNode, getParentLNode, insertView, removeView} from './node_manipulation'; @@ -236,6 +237,10 @@ export function injectComponentFactoryResolver(): viewEngine_ComponentFactoryRes } const componentFactoryResolver: ComponentFactoryResolver = new ComponentFactoryResolver(); + +export function injectRenderer2(): Renderer2 { + return getOrCreateRenderer2(getOrCreateNodeInjector()); +} /** * Inject static attribute value into directive constructor. * @@ -320,6 +325,17 @@ function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode): .view[DIRECTIVES] ![hostNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift]); } + + +function getOrCreateRenderer2(di: LInjector): Renderer2 { + const renderer = di.node.view[RENDERER]; + if (isProceduralRenderer(renderer)) { + return renderer as Renderer2; + } else { + throw new Error('Cannot inject Renderer2 when the application uses Renderer3!'); + } +} + /** * If the node is an embedded view, traverses up the view tree to return the closest * ancestor view that is attached to a component. If it's already a component node, @@ -632,6 +648,9 @@ export class NodeInjector implements Injector { if (token === viewEngine_ChangeDetectorRef) { return getOrCreateChangeDetectorRef(this._lInjector, null); } + if (token === Renderer2) { + return getOrCreateRenderer2(this._lInjector); + } return getOrCreateInjectable(this._lInjector, token); } diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 54a40c4500..0628054d3b 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -14,7 +14,7 @@ import {PublicFeature} from './features/public_feature'; import {BaseDef, ComponentDef, ComponentDefInternal, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefInternal, DirectiveType, PipeDef} from './interfaces/definition'; export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2} from './component_ref'; -export {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, directiveInject, getFactoryOf, getInheritedFactory, injectAttribute, injectChangeDetectorRef, injectComponentFactoryResolver, injectElementRef, injectTemplateRef, injectViewContainerRef, templateRefExtractor} from './di'; +export {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, directiveInject, getFactoryOf, getInheritedFactory, injectAttribute, injectChangeDetectorRef, injectComponentFactoryResolver, injectElementRef, injectRenderer2, injectTemplateRef, injectViewContainerRef, templateRefExtractor} from './di'; export {RenderFlags} from './interfaces/definition'; export {CssSelectorList} from './interfaces/projection'; diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index d826596faf..b401c89640 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -35,6 +35,7 @@ export const angularCoreEnv: {[name: string]: Function} = { 'ɵinjectTemplateRef': r3.injectTemplateRef, 'ɵinjectViewContainerRef': r3.injectViewContainerRef, 'ɵtemplateRefExtractor': r3.templateRefExtractor, + 'ɵinjectRenderer2': r3.injectRenderer2, 'ɵNgOnChangesFeature': r3.NgOnChangesFeature, 'ɵPublicFeature': r3.PublicFeature, 'ɵInheritDefinitionFeature': r3.InheritDefinitionFeature, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 3c4558479d..c724b938e9 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -137,6 +137,9 @@ { "name": "RecordViewTuple" }, + { + "name": "Renderer2" + }, { "name": "RendererStyleFlags3" }, @@ -590,6 +593,9 @@ { "name": "getOrCreateNodeInjectorForNode" }, + { + "name": "getOrCreateRenderer2" + }, { "name": "getOrCreateTView" }, diff --git a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json index acac7f9ec7..8462722295 100644 --- a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json @@ -1679,6 +1679,9 @@ { "name": "getOrCreateNodeInjectorForNode" }, + { + "name": "getOrCreateRenderer2" + }, { "name": "getOrCreateTView" }, @@ -1805,6 +1808,9 @@ { "name": "injectElementRef" }, + { + "name": "injectRenderer2" + }, { "name": "injectTemplateRef" }, diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index b8d91d5859..a3b884cbdc 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -6,18 +6,21 @@ * found in the LICENSE file at https://angular.io/license */ -import {Attribute, ChangeDetectorRef, ElementRef, Host, InjectFlags, Optional, Self, SkipSelf, TemplateRef, ViewContainerRef, defineInjectable} from '@angular/core'; +import {Attribute, ChangeDetectorRef, ElementRef, Host, InjectFlags, Optional, Renderer2, Self, SkipSelf, TemplateRef, ViewContainerRef, defineInjectable} from '@angular/core'; import {RenderFlags} from '@angular/core/src/render3/interfaces/definition'; import {defineComponent} from '../../src/render3/definition'; import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di'; -import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; +import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectRenderer2, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, loadDirective, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions'; import {LInjector} from '../../src/render3/interfaces/injector'; +import {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node'; + import {LViewFlags} from '../../src/render3/interfaces/view'; import {ViewRef} from '../../src/render3/view_ref'; +import {getRendererFactory2} from './imported_renderer2'; import {ComponentFixture, createComponent, createDirective, renderComponent, toHtml} from './render_util'; describe('di', () => { @@ -1205,6 +1208,36 @@ describe('di', () => { }); }); + describe('Renderer2', () => { + let comp: MyComp; + + class MyComp { + constructor(public renderer: Renderer2) {} + + static ngComponentDef = defineComponent({ + type: MyComp, + selectors: [['my-comp']], + factory: () => comp = new MyComp(injectRenderer2()), + consts: 1, + vars: 0, + template: function(rf: RenderFlags, ctx: MyComp) { + if (rf & RenderFlags.Create) { + text(0, 'Foo'); + } + } + }); + } + + it('should inject the Renderer2 used by the application', () => { + const rendererFactory = getRendererFactory2(document); + new ComponentFixture(MyComp, {rendererFactory: rendererFactory}); + expect(isProceduralRenderer(comp.renderer)).toBeTruthy(); + }); + + it('should throw when injecting Renderer2 but the application is using Renderer3', + () => { expect(() => new ComponentFixture(MyComp)).toThrow(); }); + }); + describe('@Attribute', () => { class MyDirective { diff --git a/packages/platform-browser-dynamic/src/compiler_reflector.ts b/packages/platform-browser-dynamic/src/compiler_reflector.ts index c0663fcdda..857a85e528 100644 --- a/packages/platform-browser-dynamic/src/compiler_reflector.ts +++ b/packages/platform-browser-dynamic/src/compiler_reflector.ts @@ -7,7 +7,7 @@ */ import {CompileReflector, ExternalReference, Identifiers, getUrlScheme, syntaxError} from '@angular/compiler'; -import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, NgModuleRef, QueryList, Renderer, SecurityContext, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵCodegenComponentFactoryResolver, ɵEMPTY_ARRAY, ɵEMPTY_MAP, ɵReflectionCapabilities as ReflectionCapabilities, ɵand, ɵccf, ɵcmf, ɵcrt, ɵdid, ɵeld, ɵinlineInterpolate, ɵinterpolate, ɵmod, ɵmpd, ɵncd, ɵnov, ɵpad, ɵpid, ɵpod, ɵppd, ɵprd, ɵqud, ɵregisterModuleFactory, ɵstringify as stringify, ɵted, ɵunv, ɵvid} from '@angular/core'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, NgModuleRef, QueryList, Renderer, Renderer2, SecurityContext, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵCodegenComponentFactoryResolver, ɵEMPTY_ARRAY, ɵEMPTY_MAP, ɵReflectionCapabilities as ReflectionCapabilities, ɵand, ɵccf, ɵcmf, ɵcrt, ɵdid, ɵeld, ɵinlineInterpolate, ɵinterpolate, ɵmod, ɵmpd, ɵncd, ɵnov, ɵpad, ɵpid, ɵpod, ɵppd, ɵprd, ɵqud, ɵregisterModuleFactory, ɵstringify as stringify, ɵted, ɵunv, ɵvid} from '@angular/core'; export const MODULE_SUFFIX = ''; const builtinExternalReferences = createBuiltinExternalReferencesMap(); @@ -59,6 +59,7 @@ function createBuiltinExternalReferencesMap() { map.set(Identifiers.NgModuleRef, NgModuleRef); map.set(Identifiers.ViewContainerRef, ViewContainerRef); map.set(Identifiers.ChangeDetectorRef, ChangeDetectorRef); + map.set(Identifiers.Renderer2, Renderer2); map.set(Identifiers.QueryList, QueryList); map.set(Identifiers.TemplateRef, TemplateRef); map.set(Identifiers.CodegenComponentFactoryResolver, ɵCodegenComponentFactoryResolver);