parent
147aec43bd
commit
d1de587ce0
File diff suppressed because one or more lines are too long
|
@ -17,7 +17,7 @@ function noop() {}
|
|||
export function main() {
|
||||
let component: LargeTableComponent;
|
||||
if (typeof window !== 'undefined') {
|
||||
component = renderComponent<LargeTableComponent>(LargeTableComponent, {renderer: document});
|
||||
component = renderComponent<LargeTableComponent>(LargeTableComponent);
|
||||
bindAction('#createDom', () => createDom(component));
|
||||
bindAction('#destroyDom', () => destroyDom(component));
|
||||
bindAction('#updateDomProfile', profile(() => createDom(component), noop, 'update'));
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {C, E, T, V, b, c, defineComponent, detectChanges, e, rC, rc, s, t, v} from '@angular/core/src/render3/index';
|
||||
import {C, E, T, V, b, c, cR, cr, defineComponent, detectChanges, e, s, t, v} from '@angular/core/src/render3/index';
|
||||
import {ComponentDef} from '@angular/core/src/render3/public_interfaces';
|
||||
|
||||
import {TableCell, buildTable, emptyTable} from '../util';
|
||||
|
@ -31,7 +31,7 @@ export class LargeTableComponent {
|
|||
}
|
||||
e();
|
||||
}
|
||||
rC(2);
|
||||
cR(2);
|
||||
{
|
||||
for (let row of ctx.data) {
|
||||
let cm1 = V(1);
|
||||
|
@ -42,7 +42,7 @@ export class LargeTableComponent {
|
|||
c();
|
||||
e();
|
||||
}
|
||||
rC(1);
|
||||
cR(1);
|
||||
{
|
||||
for (let cell of row) {
|
||||
let cm2 = V(2);
|
||||
|
@ -58,12 +58,12 @@ export class LargeTableComponent {
|
|||
v();
|
||||
}
|
||||
}
|
||||
rc();
|
||||
cr();
|
||||
}
|
||||
v();
|
||||
}
|
||||
}
|
||||
rc();
|
||||
cr();
|
||||
},
|
||||
factory: () => new LargeTableComponent(),
|
||||
inputs: {data: 'data'}
|
||||
|
|
|
@ -15,7 +15,7 @@ function noop() {}
|
|||
export function main() {
|
||||
let component: TreeComponent;
|
||||
if (typeof window !== 'undefined') {
|
||||
component = renderComponent(TreeComponent, {renderer: document});
|
||||
component = renderComponent(TreeComponent);
|
||||
bindAction('#createDom', () => createDom(component));
|
||||
bindAction('#destroyDom', () => destroyDom(component));
|
||||
bindAction('#detectChanges', () => detectChanges(component));
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {C, D, E, T, V, b, b1, c, defineComponent, detectChanges as _detectChanges, e, p, rC, rc, s, t, v} from '@angular/core/src/render3/index';
|
||||
import {C, D, E, T, V, b, b1, c, cR, cr, defineComponent, detectChanges as _detectChanges, e, p, s, t, v} from '@angular/core/src/render3/index';
|
||||
import {ComponentDef} from '@angular/core/src/render3/public_interfaces';
|
||||
|
||||
import {TreeNode, buildTree, emptyTree} from '../util';
|
||||
|
@ -50,7 +50,7 @@ export class TreeComponent {
|
|||
}
|
||||
s(0, 'background-color', b(ctx.data.depth % 2 ? '' : 'grey'));
|
||||
t(1, b1(' ', ctx.data.value, ' '));
|
||||
rC(2);
|
||||
cR(2);
|
||||
{
|
||||
if (ctx.data.left != null) {
|
||||
let cm0 = V(0);
|
||||
|
@ -66,8 +66,8 @@ export class TreeComponent {
|
|||
v();
|
||||
}
|
||||
}
|
||||
rc();
|
||||
rC(3);
|
||||
cr();
|
||||
cR(3);
|
||||
{
|
||||
if (ctx.data.right != null) {
|
||||
let cm0 = V(0);
|
||||
|
@ -83,7 +83,7 @@ export class TreeComponent {
|
|||
v();
|
||||
}
|
||||
}
|
||||
rc();
|
||||
cr();
|
||||
},
|
||||
factory: () => new TreeComponent,
|
||||
inputs: {data: 'data'}
|
||||
|
@ -118,7 +118,7 @@ export function TreeTpl(ctx: TreeNode, cm: boolean) {
|
|||
}
|
||||
s(0, 'background-color', b(ctx.depth % 2 ? '' : 'grey'));
|
||||
t(1, b1(' ', ctx.value, ' '));
|
||||
rC(2);
|
||||
cR(2);
|
||||
{
|
||||
if (ctx.left != null) {
|
||||
let cm0 = V(0);
|
||||
|
@ -126,8 +126,8 @@ export function TreeTpl(ctx: TreeNode, cm: boolean) {
|
|||
v();
|
||||
}
|
||||
}
|
||||
rc();
|
||||
rC(3);
|
||||
cr();
|
||||
cR(3);
|
||||
{
|
||||
if (ctx.right != null) {
|
||||
let cm0 = V(0);
|
||||
|
@ -135,5 +135,5 @@ export function TreeTpl(ctx: TreeNode, cm: boolean) {
|
|||
v();
|
||||
}
|
||||
}
|
||||
rc();
|
||||
cr();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ function noop() {}
|
|||
export function main() {
|
||||
let component: TreeFunction;
|
||||
if (typeof window !== 'undefined') {
|
||||
component = renderComponent(TreeFunction, {renderer: document});
|
||||
component = renderComponent(TreeFunction);
|
||||
bindAction('#createDom', () => createDom(component));
|
||||
bindAction('#destroyDom', () => destroyDom(component));
|
||||
bindAction('#detectChanges', () => detectChanges(component));
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
import {ComponentRef, EmbeddedViewRef, Injector} from '../core';
|
||||
|
||||
import {assertNotNull} from './assert';
|
||||
import {NG_HOST_SYMBOL, createError, createViewState, directive, elementHost, enterView, leaveView} from './instructions';
|
||||
import {NG_HOST_SYMBOL, createError, createViewState, directive, enterView, hostElement, leaveView, locateHostElement, renderComponentOrTemplate} from './instructions';
|
||||
import {LElement} from './l_node';
|
||||
import {ComponentDef, ComponentType} from './public_interfaces';
|
||||
import {RElement, Renderer3, RendererFactory3} from './renderer';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './renderer';
|
||||
import {notImplemented, stringify} from './util';
|
||||
|
||||
|
||||
|
@ -22,10 +22,8 @@ import {notImplemented, stringify} from './util';
|
|||
*/
|
||||
export interface CreateComponentOptionArgs {
|
||||
/**
|
||||
* Which renderer to use.
|
||||
* Which renderer factory to use.
|
||||
*/
|
||||
renderer?: Renderer3;
|
||||
|
||||
rendererFactory?: RendererFactory3;
|
||||
|
||||
/**
|
||||
|
@ -138,13 +136,16 @@ export const NULL_INJECTOR: Injector = {
|
|||
*/
|
||||
export function renderComponent<T>(
|
||||
componentType: ComponentType<T>, opts: CreateComponentOptionArgs = {}): T {
|
||||
const renderer = opts.renderer || document;
|
||||
const rendererFactory = opts.rendererFactory || domRendererFactory3;
|
||||
const componentDef = componentType.ngComponentDef;
|
||||
let component: T;
|
||||
const oldView = enterView(createViewState(-1, renderer, []), null);
|
||||
const hostNode = locateHostElement(rendererFactory, opts.host || componentDef.tag);
|
||||
const oldView = enterView(
|
||||
createViewState(-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), []),
|
||||
null !);
|
||||
try {
|
||||
// Create element node at index 0 in data array
|
||||
elementHost(opts.host || componentDef.tag, componentDef);
|
||||
hostElement(hostNode, componentDef);
|
||||
// Create directive instance with n() and store at index 1 in data array (el is 0)
|
||||
component = directive(1, componentDef.n(), componentDef);
|
||||
} finally {
|
||||
|
@ -163,15 +164,8 @@ export function detectChanges<T>(component: T) {
|
|||
createError('Not a directive instance', component);
|
||||
}
|
||||
ngDevMode && assertNotNull(hostNode.data, 'hostNode.data');
|
||||
const oldView = enterView(hostNode.view !, hostNode);
|
||||
try {
|
||||
// Element was stored at 0 and directive was stored at 1 in renderComponent
|
||||
// so to refresh the component, r() needs to be called with (1, 0)
|
||||
(component.constructor as ComponentType<T>).ngComponentDef.r(1, 0);
|
||||
isDirty = false;
|
||||
} finally {
|
||||
leaveView(oldView);
|
||||
}
|
||||
renderComponentOrTemplate(hostNode, hostNode.view, component);
|
||||
isDirty = false;
|
||||
}
|
||||
|
||||
let isDirty = false;
|
||||
|
|
|
@ -18,9 +18,9 @@ import {NgStaticData, LNodeStatic, LContainerStatic, InitialInputData, InitialIn
|
|||
import {assertNodeType} from './node_assert';
|
||||
import {appendChild, insertChild, insertView, processProjectedNode, removeView} from './node_manipulation';
|
||||
import {isNodeMatchingSelector} from './node_selector_matcher';
|
||||
import {ComponentDef, ComponentTemplate, DirectiveDef} from './public_interfaces';
|
||||
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef} from './public_interfaces';
|
||||
import {QueryList, QueryState_} from './query';
|
||||
import {RComment, RElement, RText, Renderer3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3} from './renderer';
|
||||
import {RComment, RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3} from './renderer';
|
||||
import {isDifferent, stringify} from './util';
|
||||
|
||||
export {queryRefresh} from './query';
|
||||
|
@ -73,6 +73,7 @@ let nextNgElementId = 0;
|
|||
* Renderer2.
|
||||
*/
|
||||
let renderer: Renderer3;
|
||||
let rendererFactory: RendererFactory3;
|
||||
|
||||
/** Used to set the parent property when nodes are created. */
|
||||
let previousOrParentNode: LNode;
|
||||
|
@ -278,18 +279,44 @@ export function createLNode(
|
|||
/**
|
||||
*
|
||||
* @param host Existing node to render into.
|
||||
* @param renderer Renderer to use.
|
||||
* @param template Template function with the instructions.
|
||||
* @param context to pass into the template.
|
||||
*/
|
||||
export function renderTemplate<T>(host: LElement, template: ComponentTemplate<T>, context: T) {
|
||||
export function renderTemplate<T>(
|
||||
hostNode: RElement, template: ComponentTemplate<T>, context: T,
|
||||
providedRendererFactory: RendererFactory3, host: LElement | null): LElement {
|
||||
if (host == null) {
|
||||
rendererFactory = providedRendererFactory;
|
||||
host = createLNode(
|
||||
null, LNodeFlags.Element, hostNode,
|
||||
createViewState(-1, providedRendererFactory.createRenderer(null, null), []));
|
||||
}
|
||||
const hostView = host.data !;
|
||||
ngDevMode && assertNotEqual(hostView, null, 'hostView');
|
||||
hostView.ngStaticData = getTemplateStatic(template);
|
||||
const oldView = enterView(hostView, host);
|
||||
renderComponentOrTemplate(host, hostView, context, template);
|
||||
return host;
|
||||
}
|
||||
|
||||
export function renderComponentOrTemplate<T>(
|
||||
node: LElement, viewState: ViewState, componentOrContext: T, template?: ComponentTemplate<T>) {
|
||||
const oldView = enterView(viewState, node);
|
||||
try {
|
||||
template(context, creationMode);
|
||||
if (rendererFactory.begin) {
|
||||
rendererFactory.begin();
|
||||
}
|
||||
if (template) {
|
||||
ngStaticData = template.ngStaticData || (template.ngStaticData = [] as never);
|
||||
template(componentOrContext !, creationMode);
|
||||
} else {
|
||||
// Element was stored at 0 and directive was stored at 1 in renderComponent
|
||||
// so to refresh the component, r() needs to be called with (1, 0)
|
||||
(componentOrContext.constructor as ComponentType<T>).ngComponentDef.r(1, 0);
|
||||
}
|
||||
} finally {
|
||||
if (rendererFactory.end) {
|
||||
rendererFactory.end();
|
||||
}
|
||||
leaveView(oldView);
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +433,10 @@ export function elementStart(
|
|||
let componentView: ViewState|null = null;
|
||||
if (isHostElement) {
|
||||
const ngStaticData = getTemplateStatic((nameOrComponentDef as ComponentDef<any>).template);
|
||||
componentView = addToViewTree(createViewState(-1, renderer, ngStaticData));
|
||||
componentView = addToViewTree(createViewState(
|
||||
-1, rendererFactory.createRenderer(
|
||||
native, (nameOrComponentDef as ComponentDef<any>).rendererType),
|
||||
ngStaticData));
|
||||
}
|
||||
|
||||
// Only component views should be added to the view tree directly. Embedded views are
|
||||
|
@ -453,16 +483,19 @@ export function createError(text: string, token: any) {
|
|||
|
||||
|
||||
/**
|
||||
* Used for bootstrapping existing nodes into rendering pipeline.
|
||||
* Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
|
||||
*
|
||||
* @param elementOrSelector Render element or CSS selector to locate the element.
|
||||
*/
|
||||
export function elementHost(elementOrSelector: RElement | string, def: ComponentDef<any>) {
|
||||
export function locateHostElement(
|
||||
factory: RendererFactory3, elementOrSelector: RElement | string): RElement|null {
|
||||
ngDevMode && assertDataInRange(-1);
|
||||
rendererFactory = factory;
|
||||
const defaultRenderer = factory.createRenderer(null, null);
|
||||
const rNode = typeof elementOrSelector === 'string' ?
|
||||
((renderer as ProceduralRenderer3).selectRootElement ?
|
||||
(renderer as ProceduralRenderer3).selectRootElement(elementOrSelector) :
|
||||
(renderer as ObjectOrientedRenderer3).querySelector !(elementOrSelector)) :
|
||||
((defaultRenderer as ProceduralRenderer3).selectRootElement ?
|
||||
(defaultRenderer as ProceduralRenderer3).selectRootElement(elementOrSelector) :
|
||||
(defaultRenderer as ObjectOrientedRenderer3).querySelector !(elementOrSelector)) :
|
||||
elementOrSelector;
|
||||
if (ngDevMode && !rNode) {
|
||||
if (typeof elementOrSelector === 'string') {
|
||||
|
@ -471,6 +504,15 @@ export function elementHost(elementOrSelector: RElement | string, def: Component
|
|||
throw createError('Host node is required:', elementOrSelector);
|
||||
}
|
||||
}
|
||||
return rNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the host LNode..
|
||||
*
|
||||
* @param rNode Render host element.
|
||||
*/
|
||||
export function hostElement(rNode: RElement | null, def: ComponentDef<any>) {
|
||||
createLNode(
|
||||
0, LNodeFlags.Element, rNode, createViewState(-1, renderer, getTemplateStatic(def.template)));
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '../core';
|
||||
|
||||
import {RendererType2, Type} from '../core';
|
||||
import {resolveRendererType2} from '../view/util';
|
||||
import {componentRefresh, diPublic} from './instructions';
|
||||
|
||||
|
||||
|
@ -108,6 +108,13 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
|
|||
* NOTE: only used with component directives.
|
||||
*/
|
||||
template: ComponentTemplate<T>;
|
||||
|
||||
/**
|
||||
* Renderer type data of the component.
|
||||
*
|
||||
* NOTE: only used with component directives.
|
||||
*/
|
||||
rendererType: RendererType2|null;
|
||||
}
|
||||
|
||||
export interface DirectiveDefArgs<T> {
|
||||
|
@ -125,6 +132,7 @@ export interface ComponentDefArgs<T> extends DirectiveDefArgs<T> {
|
|||
template: ComponentTemplate<T>;
|
||||
refresh?: (this: ComponentDef<T>, directiveIndex: number, elementIndex: number) => void;
|
||||
features?: ComponentDefFeature[];
|
||||
rendererType?: RendererType2;
|
||||
}
|
||||
|
||||
export type DirectiveDefFeature = <T>(directiveDef: DirectiveDef<T>) => void;
|
||||
|
@ -156,6 +164,7 @@ export function defineComponent<T>(componentDefinition: ComponentDefArgs<T>): Co
|
|||
inputs: invertObject(componentDefinition.inputs),
|
||||
outputs: invertObject(componentDefinition.outputs),
|
||||
methods: invertObject(componentDefinition.methods),
|
||||
rendererType: resolveRendererType2(componentDefinition.rendererType) || null,
|
||||
};
|
||||
const feature = componentDefinition.features;
|
||||
feature && feature.forEach((fn) => fn(def));
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {DirectiveDef} from '@angular/core/src/render3/public_interfaces';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
|
||||
import * as viewEngine from '../core';
|
||||
|
@ -15,6 +14,7 @@ import {assertNotNull} from './assert';
|
|||
import {injectElementRefForNode} from './di';
|
||||
import {QueryState} from './interfaces';
|
||||
import {LContainer, LElement, LNode, LNodeFlags, LView} from './l_node';
|
||||
import {DirectiveDef} from './public_interfaces';
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* it will be easy to implement such API.
|
||||
*/
|
||||
|
||||
import {RendererStyleFlags2} from '../core';
|
||||
import {RendererStyleFlags2, RendererType2, ViewEncapsulation} from '../core';
|
||||
import {ComponentDef} from './public_interfaces';
|
||||
|
||||
// TODO: cleanup once the code is merged in angular/angular
|
||||
|
@ -68,11 +68,16 @@ export interface ProceduralRenderer3 {
|
|||
}
|
||||
|
||||
export interface RendererFactory3 {
|
||||
createRenderer(hostElement: RElement, componentDef: ComponentDef<any>): Renderer3;
|
||||
createRenderer(hostElement: RElement|null, rendererType: RendererType2|null): Renderer3;
|
||||
begin?(): void;
|
||||
end?(): void;
|
||||
}
|
||||
|
||||
export const domRendererFactory3: RendererFactory3 = {
|
||||
createRenderer: (hostElement: RElement | null, rendererType: RendererType2 | null):
|
||||
Renderer3 => { return document;}
|
||||
};
|
||||
|
||||
/** Subset of API needed for appending elements and text nodes. */
|
||||
export interface RNode {
|
||||
removeChild(oldChild: RNode): void;
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {T, b, defineComponent, markDirty, t} from '../../src/render3/index';
|
||||
import {ViewEncapsulation} from '../../src/core';
|
||||
import {D, E, T, b, defineComponent, e, markDirty, t} from '../../src/render3/index';
|
||||
import {createRendererType2} from '../../src/view';
|
||||
|
||||
import {getRendererFactory2} from './imported_renderer2';
|
||||
import {containerEl, renderComponent, requestAnimationFrame} from './render_util';
|
||||
|
||||
describe('component', () => {
|
||||
|
@ -60,3 +63,113 @@ describe('component', () => {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
// TODO: add tests with Native once tests are run in real browser (domino doesn't support shadow
|
||||
// root)
|
||||
describe('encapsulation', () => {
|
||||
class WrapperComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: WrapperComponent,
|
||||
tag: 'wrapper',
|
||||
template: function(ctx: WrapperComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
E(0, EncapsulatedComponent.ngComponentDef);
|
||||
{ D(1, EncapsulatedComponent.ngComponentDef.n(), EncapsulatedComponent.ngComponentDef); }
|
||||
e();
|
||||
}
|
||||
EncapsulatedComponent.ngComponentDef.r(1, 0);
|
||||
},
|
||||
factory: () => new WrapperComponent,
|
||||
});
|
||||
}
|
||||
|
||||
class EncapsulatedComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: EncapsulatedComponent,
|
||||
tag: 'encapsulated',
|
||||
template: function(ctx: EncapsulatedComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
T(0, 'foo');
|
||||
E(1, LeafComponent.ngComponentDef);
|
||||
{ D(2, LeafComponent.ngComponentDef.n(), LeafComponent.ngComponentDef); }
|
||||
e();
|
||||
}
|
||||
LeafComponent.ngComponentDef.r(2, 1);
|
||||
},
|
||||
factory: () => new EncapsulatedComponent,
|
||||
rendererType:
|
||||
createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
|
||||
});
|
||||
}
|
||||
|
||||
class LeafComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: LeafComponent,
|
||||
tag: 'leaf',
|
||||
template: function(ctx: LeafComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
E(0, 'span');
|
||||
{ T(1, 'bar'); }
|
||||
e();
|
||||
}
|
||||
},
|
||||
factory: () => new LeafComponent,
|
||||
});
|
||||
}
|
||||
|
||||
it('should encapsulate children, but not host nor grand children', () => {
|
||||
renderComponent(WrapperComponent, getRendererFactory2(document));
|
||||
expect(containerEl.outerHTML)
|
||||
.toEqual(
|
||||
'<div host=""><encapsulated _nghost-c0="">foo<leaf _ngcontent-c0=""><span>bar</span></leaf></encapsulated></div>');
|
||||
});
|
||||
|
||||
it('should encapsulate host', () => {
|
||||
renderComponent(EncapsulatedComponent, getRendererFactory2(document));
|
||||
expect(containerEl.outerHTML)
|
||||
.toEqual(
|
||||
'<div host="" _nghost-c0="">foo<leaf _ngcontent-c0=""><span>bar</span></leaf></div>');
|
||||
});
|
||||
|
||||
it('should encapsulate host and children with different attributes', () => {
|
||||
class WrapperComponentWith {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: WrapperComponent,
|
||||
tag: 'wrapper',
|
||||
template: function(ctx: WrapperComponentWith, cm: boolean) {
|
||||
if (cm) {
|
||||
E(0, LeafComponentwith.ngComponentDef);
|
||||
{ D(1, LeafComponentwith.ngComponentDef.n(), LeafComponentwith.ngComponentDef); }
|
||||
e();
|
||||
}
|
||||
LeafComponentwith.ngComponentDef.r(1, 0);
|
||||
},
|
||||
factory: () => new WrapperComponentWith,
|
||||
rendererType:
|
||||
createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
|
||||
});
|
||||
}
|
||||
|
||||
class LeafComponentwith {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: LeafComponentwith,
|
||||
tag: 'leaf',
|
||||
template: function(ctx: LeafComponentwith, cm: boolean) {
|
||||
if (cm) {
|
||||
E(0, 'span');
|
||||
{ T(1, 'bar'); }
|
||||
e();
|
||||
}
|
||||
},
|
||||
factory: () => new LeafComponentwith,
|
||||
rendererType:
|
||||
createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
|
||||
});
|
||||
}
|
||||
|
||||
renderComponent(WrapperComponentWith, getRendererFactory2(document));
|
||||
expect(containerEl.outerHTML)
|
||||
.toEqual(
|
||||
'<div host="" _nghost-c1=""><leaf _ngcontent-c1="" _nghost-c2=""><span _ngcontent-c2="">bar</span></leaf></div>');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter, NgZone, Renderer2} from '@angular/core';
|
||||
import {EventEmitter, NgZone, RendererFactory2} from '@angular/core';
|
||||
import {EventManager, ɵDomEventsPlugin, ɵDomRendererFactory2, ɵDomSharedStylesHost} from '@angular/platform-browser';
|
||||
|
||||
|
||||
// Adapted renderer: it creates a Renderer2 instance and adapts it to Renderer3
|
||||
// TODO: remove once this code is in angular/angular
|
||||
export class NoopNgZone implements NgZone {
|
||||
|
@ -47,11 +46,9 @@ export class SimpleDomEventsPlugin extends ɵDomEventsPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
export function getRenderer2(document: any): Renderer2 {
|
||||
export function getRendererFactory2(document: any): RendererFactory2 {
|
||||
const fakeNgZone: NgZone = new NoopNgZone();
|
||||
const eventManager =
|
||||
new EventManager([new SimpleDomEventsPlugin(document, fakeNgZone)], fakeNgZone);
|
||||
const rendererFactory2 =
|
||||
new ɵDomRendererFactory2(eventManager, new ɵDomSharedStylesHost(document));
|
||||
return rendererFactory2.createRenderer(null, null);
|
||||
return new ɵDomRendererFactory2(eventManager, new ɵDomSharedStylesHost(document));
|
||||
}
|
||||
|
|
|
@ -9,19 +9,17 @@
|
|||
import {ComponentTemplate, ComponentType, PublicFeature, defineComponent, renderComponent as _renderComponent} from '../../src/render3/index';
|
||||
import {NG_HOST_SYMBOL, createLNode, createViewState, renderTemplate} from '../../src/render3/instructions';
|
||||
import {LElement, LNodeFlags} from '../../src/render3/l_node';
|
||||
import {RElement, RText, Renderer3} from '../../src/render3/renderer';
|
||||
import {getRenderer2} from './imported_renderer2';
|
||||
import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/renderer';
|
||||
import {getRendererFactory2} from './imported_renderer2';
|
||||
|
||||
export const document = ((global || window) as any).document;
|
||||
export let containerEl: HTMLElement = null !;
|
||||
let host: LElement;
|
||||
let activeRenderer: Renderer3 =
|
||||
(typeof process !== 'undefined' && process.argv[3] && process.argv[3] === '--r=renderer2') ?
|
||||
getRenderer2(document) :
|
||||
document;
|
||||
let host: LElement|null;
|
||||
const isRenderer2 = process.argv[3] && process.argv[3] === '--r=renderer2';
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log(
|
||||
`Running tests with ${activeRenderer === document ? 'document' : 'Renderer2'} renderer...`);
|
||||
console.log(`Running tests with ${!isRenderer2 ? 'document' : 'Renderer2'} renderer...`);
|
||||
const testRendererFactory: RendererFactory3 =
|
||||
isRenderer2 ? getRendererFactory2(document) : domRendererFactory3;
|
||||
|
||||
export const requestAnimationFrame:
|
||||
{(fn: () => void): void; flush(): void; queue: (() => void)[];} = function(fn: () => void) {
|
||||
|
@ -37,20 +35,22 @@ export function resetDOM() {
|
|||
requestAnimationFrame.queue = [];
|
||||
containerEl = document.createElement('div');
|
||||
containerEl.setAttribute('host', '');
|
||||
host = createLNode(
|
||||
null, LNodeFlags.Element, containerEl, createViewState(-1, activeRenderer, null !));
|
||||
host = null;
|
||||
// TODO: assert that the global state is clean (e.g. ngData, previousOrParentNode, etc)
|
||||
}
|
||||
|
||||
export function renderToHtml(template: ComponentTemplate<any>, ctx: any) {
|
||||
renderTemplate(host, template, ctx);
|
||||
return toHtml(host.native);
|
||||
export function renderToHtml(
|
||||
template: ComponentTemplate<any>, ctx: any, providedRendererFactory?: RendererFactory3) {
|
||||
host = renderTemplate(
|
||||
containerEl, template, ctx, providedRendererFactory || testRendererFactory, host);
|
||||
return toHtml(containerEl);
|
||||
}
|
||||
|
||||
beforeEach(resetDOM);
|
||||
|
||||
export function renderComponent<T>(type: ComponentType<T>): T {
|
||||
return _renderComponent(type, {renderer: activeRenderer, host: containerEl});
|
||||
export function renderComponent<T>(type: ComponentType<T>, rendererFactory?: RendererFactory3): T {
|
||||
return _renderComponent(
|
||||
type, {rendererFactory: rendererFactory || testRendererFactory, host: containerEl});
|
||||
}
|
||||
|
||||
export function toHtml<T>(componentOrElement: T | RElement): string {
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {RendererType2} from '@angular/core';
|
||||
|
||||
import {D, E, e} from '../../src/render3';
|
||||
import {T, defineComponent, detectChanges} from '../../src/render3/index';
|
||||
|
||||
import {getRendererFactory2} from './imported_renderer2';
|
||||
import {document, renderComponent, renderToHtml, resetDOM} from './render_util';
|
||||
|
||||
describe('renderer factory lifecycle', () => {
|
||||
let logs: string[] = [];
|
||||
let rendererFactory = getRendererFactory2(document);
|
||||
const createRender = rendererFactory.createRenderer;
|
||||
rendererFactory.createRenderer = (hostElement: any, type: RendererType2 | null) => {
|
||||
logs.push('create');
|
||||
return createRender.apply(rendererFactory, [hostElement, type]);
|
||||
};
|
||||
rendererFactory.begin = () => logs.push('begin');
|
||||
rendererFactory.end = () => logs.push('end');
|
||||
|
||||
class SomeComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SomeComponent,
|
||||
tag: 'some-component',
|
||||
template: function(ctx: SomeComponent, cm: boolean) {
|
||||
logs.push('component');
|
||||
if (cm) {
|
||||
T(0, 'foo');
|
||||
}
|
||||
},
|
||||
factory: () => new SomeComponent
|
||||
});
|
||||
}
|
||||
|
||||
class SomeComponentWhichThrows {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SomeComponentWhichThrows,
|
||||
tag: 'some-component-with-Error',
|
||||
template: function(ctx: SomeComponentWhichThrows, cm: boolean) {
|
||||
throw(new Error('SomeComponentWhichThrows threw'));
|
||||
},
|
||||
factory: () => new SomeComponentWhichThrows
|
||||
});
|
||||
}
|
||||
|
||||
function Template(ctx: any, cm: boolean) {
|
||||
logs.push('function');
|
||||
if (cm) {
|
||||
T(0, 'bar');
|
||||
}
|
||||
}
|
||||
|
||||
function TemplateWithComponent(ctx: any, cm: boolean) {
|
||||
logs.push('function_with_component');
|
||||
if (cm) {
|
||||
T(0, 'bar');
|
||||
E(1, SomeComponent.ngComponentDef);
|
||||
{ D(2, SomeComponent.ngComponentDef.n(), SomeComponent.ngComponentDef); }
|
||||
e();
|
||||
}
|
||||
SomeComponent.ngComponentDef.r(2, 1);
|
||||
}
|
||||
|
||||
beforeEach(() => { logs = []; });
|
||||
|
||||
it('should work with a component', () => {
|
||||
const component = renderComponent(SomeComponent, rendererFactory);
|
||||
expect(logs).toEqual(['create', 'create', 'begin', 'component', 'end']);
|
||||
|
||||
logs = [];
|
||||
detectChanges(component);
|
||||
expect(logs).toEqual(['begin', 'component', 'end']);
|
||||
});
|
||||
|
||||
it('should work with a component which throws', () => {
|
||||
expect(() => renderComponent(SomeComponentWhichThrows, rendererFactory)).toThrow();
|
||||
expect(logs).toEqual(['create', 'create', 'begin', 'end']);
|
||||
});
|
||||
|
||||
it('should work with a template', () => {
|
||||
renderToHtml(Template, {}, rendererFactory);
|
||||
expect(logs).toEqual(['create', 'begin', 'function', 'end']);
|
||||
|
||||
logs = [];
|
||||
renderToHtml(Template, {});
|
||||
expect(logs).toEqual(['begin', 'function', 'end']);
|
||||
});
|
||||
|
||||
it('should work with a template which contains a component', () => {
|
||||
renderToHtml(TemplateWithComponent, {}, rendererFactory);
|
||||
expect(logs).toEqual(
|
||||
['create', 'begin', 'function_with_component', 'create', 'component', 'end']);
|
||||
|
||||
logs = [];
|
||||
renderToHtml(TemplateWithComponent, {});
|
||||
expect(logs).toEqual(['begin', 'function_with_component', 'component', 'end']);
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue