Skip to content

Commit

Permalink
feat(ivy): support injecting Renderer2
Browse files Browse the repository at this point in the history
  • Loading branch information
marclaval committed Aug 20, 2018
1 parent d2be3d5 commit c4a85e8
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 11 deletions.
3 changes: 3 additions & 0 deletions packages/compiler-cli/src/ngtsc/annotations/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-cli/test/ngtsc/fake_core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class ElementRef {}
export class Injector {}
export class TemplateRef<T = any> {}
export class ViewContainerRef {}
export class Renderer2 {}
export class ɵNgModuleFactory<T> {
constructor(public clazz: T) {}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/compiler-cli/test/ngtsc/ngtsc_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ describe('ngtsc behavioral tests', () => {
Component,
ElementRef,
Injector,
Renderer2,
TemplateRef,
ViewContainerRef,
} from '@angular/core';
Expand All @@ -462,6 +463,7 @@ describe('ngtsc behavioral tests', () => {
cdr: ChangeDetectorRef,
er: ElementRef,
i: Injector,
r2: Renderer2,
tr: TemplateRef,
vcr: ViewContainerRef,
) {}
Expand All @@ -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', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/compiler/src/identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions packages/compiler/src/render3/r3_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ export enum R3ResolvedDependencyType {
* The dependency is for `ChangeDetectorRef`.
*/
ChangeDetectorRef = 6,

/**
* The dependency is for `Renderer2`.
*/
Renderer2 = 7,
}

/**
Expand Down Expand Up @@ -281,6 +286,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]}`);
Expand All @@ -301,6 +308,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[] = [];
Expand All @@ -316,6 +324,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;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/compiler/src/render3/r3_identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 defineBase: o.ExternalReference = {name: 'ɵdefineBase', moduleName: CORE};
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/core_render3_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/render3/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ||||

Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/render3/di.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 {Renderer2Adapter, 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';
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -320,6 +325,13 @@ function getOrCreateHostChangeDetector(currentNode: LViewNode | LElementNode):
.view[DIRECTIVES] ![hostNode.tNode.flags >> TNodeFlags.DirectiveStartingIndexShift]);
}



function getOrCreateRenderer2(di: LInjector): Renderer2 {
const renderer = di.node.view[RENDERER];
return isProceduralRenderer(renderer) ? renderer as Renderer2 : new Renderer2Adapter(renderer);
}

/**
* 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,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/render3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
115 changes: 114 additions & 1 deletion packages/core/src/render3/interfaces/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* it will be easy to implement such API.
*/

import {RendererStyleFlags2, RendererType2} from '../../render/api';
import {Renderer2, RendererStyleFlags2, RendererType2} from '../../render/api';


// TODO: cleanup once the code is merged in angular/angular
Expand Down Expand Up @@ -127,6 +127,7 @@ export interface RElement extends RNode {
setAttribute(name: string, value: string): void;
removeAttribute(name: string): void;
setAttributeNS(namespaceURI: string, qualifiedName: string, value: string): void;
removeAttributeNS(namespace: string, name: string): void;
addEventListener(type: string, listener: EventListener, useCapture?: boolean): void;
removeEventListener(type: string, listener?: EventListener, options?: boolean): void;

Expand All @@ -150,3 +151,115 @@ export interface RComment extends RNode {}
// Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types.
export const unusedValueExportToPlacateAjd = 1;

const NAMESPACE_URIS: {[ns: string]: string} = {
'svg': 'http://www.w3.org/2000/svg',
'xhtml': 'http://www.w3.org/1999/xhtml',
'xlink': 'http://www.w3.org/1999/xlink',
'xml': 'http://www.w3.org/XML/1998/namespace',
'xmlns': 'http://www.w3.org/2000/xmlns/',
};

export class Renderer2Adapter extends Renderer2 {
public data = {};
public destroyNode = null;

constructor(private renderer: ObjectOrientedRenderer3) { super(); }

destroy() {}

createElement(name: string, namespace?: string|null): RElement {
if (namespace == null) {
return this.renderer.createElement(name);
} else {
return this.renderer.createElementNS(namespace, name);
}
}

createComment(value: string): RComment { return this.renderer.createComment(value); }

createText(value: string): RText { return this.renderer.createTextNode(value); }

appendChild(parent: RElement, newChild: RNode): void { parent.appendChild(newChild); }

insertBefore(parent: RElement, newChild: RNode, refChild: RNode): void {
parent.insertBefore(newChild, refChild, true);
}

removeChild(parent: RElement, oldChild: RNode): void { parent.removeChild(oldChild); }

selectRootElement(selectorOrNode: string|RNode): RNode|null {
return typeof selectorOrNode === 'string' ? this.renderer.querySelector(selectorOrNode) :
selectorOrNode;
}

parentNode(node: RNode): RNode|null { return (node as any).parentNode; }

nextSibling(node: RNode): RNode|null { return (node as any).nextSibling; }

setAttribute(el: RElement, name: string, value: string, namespace?: string): void {
if (namespace) {
name = `${namespace}:${name}`;
const namespaceUri = NAMESPACE_URIS[namespace];
if (namespaceUri) {
el.setAttributeNS(namespaceUri, name, value);
} else {
el.setAttribute(name, value);
}
} else {
el.setAttribute(name, value);
}
}

removeAttribute(el: RElement, name: string, namespace?: string): void {
if (namespace) {
const namespaceUri = NAMESPACE_URIS[namespace];
if (namespaceUri) {
el.removeAttributeNS(namespaceUri, name);
} else {
el.removeAttribute(`${namespace}:${name}`);
}
} else {
el.removeAttribute(name);
}
}

addClass(el: RElement, name: string) { el.classList.add(name); }

removeClass(el: RElement, name: string) { el.classList.remove(name); }

setStyle(el: RElement, style: string, value: any, flags: RendererStyleFlags2): void {
if (flags & RendererStyleFlags2.DashCase) {
el.style.setProperty(
style, value, !!(flags & RendererStyleFlags2.Important) ? 'important' : '');
} else {
(el as any).style[style] = value;
}
}

removeStyle(el: RElement, style: string, flags: RendererStyleFlags2): void {
if (flags & RendererStyleFlags2.DashCase) {
el.style.removeProperty(style);
} else {
// IE requires '' instead of null
// see https://github.com/angular/angular/issues/7916
(el as any).style[style] = '';
}
}

setProperty(el: RNode, name: string, value: any): void { (el as any)[name] = value; }

setValue(node: any, value: string): void { node.nodeValue = value; }

listen(
target: 'window'|'document'|'body'|RElement, eventName: string,
callback: (event: any) => boolean | void): () => void {
if (typeof target !== 'string') {
target.addEventListener(eventName, callback, false);
return () => target.removeEventListener(eventName, callback, false);
} else {
throw new Error(
`Renderer2Adapter.listen doesn't support event target as a string, use an element instead.`);
}
}
}
1 change: 1 addition & 0 deletions packages/core/src/render3/jit/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵinjectElementRef': r3.injectElementRef,
'ɵinjectTemplateRef': r3.injectTemplateRef,
'ɵinjectViewContainerRef': r3.injectViewContainerRef,
'ɵinjectRenderer2': r3.injectRenderer2,
'ɵNgOnChangesFeature': r3.NgOnChangesFeature,
'ɵPublicFeature': r3.PublicFeature,
'ɵInheritDefinitionFeature': r3.InheritDefinitionFeature,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@
"name": "EMPTY"
},
{
"name": "EMPTY_ARRAY$3"
"name": "EMPTY_ARRAY$2"
},
{
"name": "EMPTY_ARRAY$4"
Expand Down Expand Up @@ -1233,7 +1233,7 @@
"name": "QUOTED_KEYS"
},
{
"name": "QueryList$1"
"name": "QueryList"
},
{
"name": "Quote"
Expand Down Expand Up @@ -2610,7 +2610,7 @@
"name": "createPureExpression"
},
{
"name": "createQuery$1"
"name": "createQuery"
},
{
"name": "createRendererType2"
Expand Down Expand Up @@ -2883,7 +2883,7 @@
"name": "flatten"
},
{
"name": "flatten$3"
"name": "flatten$2"
},
{
"name": "flattenAndDedupeArray"
Expand Down

0 comments on commit c4a85e8

Please sign in to comment.