Compare commits
10 Commits
6.0.0-beta
...
rollup
Author | SHA1 | Date | |
---|---|---|---|
5269ce287e | |||
3e03dbe576 | |||
33b338120c | |||
811679a583 | |||
2c33d17609 | |||
9c99e6a838 | |||
98174758ad | |||
97b928053d | |||
1fe55e252c | |||
53ed4b4648 |
@ -18,14 +18,14 @@
|
||||
"hello_world__render3__closure": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"bundle": 7065
|
||||
"bundle": 7674
|
||||
}
|
||||
}
|
||||
},
|
||||
"hello_world__render3__rollup": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"bundle": 56550
|
||||
"bundle": 58662
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ export class HelloWorld {
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef: ComponentDef<HelloWorld> = defineComponent({
|
||||
type: HelloWorld,
|
||||
tag: 'hello-world',
|
||||
template: function (ctx: HelloWorld, cm: boolean) {
|
||||
if (cm) {
|
||||
|
@ -14,6 +14,7 @@ export class HelloWorld {
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef: ComponentDef<HelloWorld> = defineComponent({
|
||||
type: HelloWorld,
|
||||
tag: 'hello-world',
|
||||
template: function (ctx: HelloWorld, cm: boolean) {
|
||||
if (cm) {
|
||||
|
@ -16,6 +16,7 @@ export class LargeTableComponent {
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef: ComponentDef<LargeTableComponent> = defineComponent({
|
||||
type: LargeTableComponent,
|
||||
tag: 'largetable',
|
||||
template: function(ctx: LargeTableComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ɵC as C, ɵE as E, ɵT as T, ɵV as V, ɵb as b, ɵb1 as b1, ɵcR as cR, ɵcr as cr, ɵdefineComponent as defineComponent, ɵdetectChanges as _detectChanges, ɵe as e, ɵp as p, ɵs as s, ɵt as t, ɵv as v} from '@angular/core';
|
||||
import {ɵC as C, ɵE as E, ɵT as T, ɵV as V, ɵb as b, ɵb1 as b1, ɵcR as cR, ɵcr as cr, ɵdefineComponent as defineComponent, ɵdetectChanges as _detectChanges, ɵe as e, ɵp as p, ɵr as r, ɵs as s, ɵt as t, ɵv as v} from '@angular/core';
|
||||
import {ComponentDef} from '@angular/core/src/render3/interfaces/definition';
|
||||
|
||||
import {TreeNode, buildTree, emptyTree} from '../util';
|
||||
@ -36,6 +36,7 @@ export class TreeComponent {
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef: ComponentDef<TreeComponent> = defineComponent({
|
||||
type: TreeComponent,
|
||||
tag: 'tree',
|
||||
template: function(ctx: TreeComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -58,7 +59,7 @@ export class TreeComponent {
|
||||
}
|
||||
p(0, 'data', b(ctx.data.left));
|
||||
TreeComponent.ngComponentDef.h(1, 0);
|
||||
TreeComponent.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
v();
|
||||
}
|
||||
@ -75,7 +76,7 @@ export class TreeComponent {
|
||||
}
|
||||
p(0, 'data', b(ctx.data.right));
|
||||
TreeComponent.ngComponentDef.h(1, 0);
|
||||
TreeComponent.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
v();
|
||||
}
|
||||
@ -92,6 +93,7 @@ export class TreeFunction {
|
||||
|
||||
/** @nocollapse */
|
||||
static ngComponentDef: ComponentDef<TreeFunction> = defineComponent({
|
||||
type: TreeFunction,
|
||||
tag: 'tree',
|
||||
template: function(ctx: TreeFunction, cm: boolean) {
|
||||
// bit of a hack
|
||||
|
@ -34,5 +34,6 @@ export {
|
||||
s as ɵs,
|
||||
t as ɵt,
|
||||
v as ɵv,
|
||||
r as ɵr,
|
||||
} from './render3/index';
|
||||
// clang-format on
|
||||
|
@ -13,8 +13,8 @@ import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_facto
|
||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef} from '../linker/view_ref';
|
||||
|
||||
import {assertNotNull} from './assert';
|
||||
import {NG_HOST_SYMBOL, createError, createLView, directiveCreate, enterView, hostElement, leaveView, locateHostElement, renderComponentOrTemplate} from './instructions';
|
||||
import {ComponentDef, ComponentType, TypedComponentDef} from './interfaces/definition';
|
||||
import {NG_HOST_SYMBOL, createError, createLView, createTView, directiveCreate, enterView, hostElement, leaveView, locateHostElement, renderComponentOrTemplate} from './instructions';
|
||||
import {ComponentDef, ComponentType} from './interfaces/definition';
|
||||
import {LElementNode} from './interfaces/node';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
import {notImplemented, stringify} from './util';
|
||||
@ -166,13 +166,13 @@ export const NULL_INJECTOR: Injector = {
|
||||
export function renderComponent<T>(
|
||||
componentType: ComponentType<T>, opts: CreateComponentOptions = {}): T {
|
||||
const rendererFactory = opts.rendererFactory || domRendererFactory3;
|
||||
const componentDef = componentType.ngComponentDef as TypedComponentDef<T>;
|
||||
const componentDef = componentType.ngComponentDef as ComponentDef<T>;
|
||||
if (componentDef.type != componentType) componentDef.type = componentType;
|
||||
let component: T;
|
||||
const hostNode = locateHostElement(rendererFactory, opts.host || componentDef.tag);
|
||||
const oldView = enterView(
|
||||
createLView(
|
||||
-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), {data: []}),
|
||||
-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), createTView()),
|
||||
null !);
|
||||
try {
|
||||
// Create element node at index 0 in data array
|
||||
|
@ -13,8 +13,7 @@ import {Type} from '../type';
|
||||
import {resolveRendererType2} from '../view/util';
|
||||
|
||||
import {diPublic} from './di';
|
||||
import {componentRefresh} from './instructions';
|
||||
import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs, TypedDirectiveDef} from './interfaces/definition';
|
||||
import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs} from './interfaces/definition';
|
||||
|
||||
|
||||
|
||||
@ -34,22 +33,26 @@ import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs, TypedDir
|
||||
* ```
|
||||
*/
|
||||
export function defineComponent<T>(componentDefinition: ComponentDefArgs<T>): ComponentDef<T> {
|
||||
const type = componentDefinition.type;
|
||||
const def = <ComponentDef<any>>{
|
||||
type: type,
|
||||
diPublic: null,
|
||||
n: componentDefinition.factory,
|
||||
tag: (componentDefinition as ComponentDefArgs<T>).tag || null !,
|
||||
template: (componentDefinition as ComponentDefArgs<T>).template || null !,
|
||||
r: componentDefinition.refresh || (componentDefinition.template ?
|
||||
function(d: number, e: number) {
|
||||
componentRefresh(d, e, componentDefinition.template);
|
||||
} :
|
||||
noop),
|
||||
h: componentDefinition.hostBindings || noop,
|
||||
inputs: invertObject(componentDefinition.inputs),
|
||||
outputs: invertObject(componentDefinition.outputs),
|
||||
methods: invertObject(componentDefinition.methods),
|
||||
rendererType: resolveRendererType2(componentDefinition.rendererType) || null,
|
||||
exportAs: componentDefinition.exportAs,
|
||||
onInit: type.prototype.ngOnInit || null,
|
||||
doCheck: type.prototype.ngDoCheck || null,
|
||||
afterContentInit: type.prototype.ngAfterContentInit || null,
|
||||
afterContentChecked: type.prototype.ngAfterContentChecked || null,
|
||||
afterViewInit: type.prototype.ngAfterViewInit || null,
|
||||
afterViewChecked: type.prototype.ngAfterViewChecked || null,
|
||||
onDestroy: type.prototype.ngOnDestroy || null
|
||||
};
|
||||
const feature = componentDefinition.features;
|
||||
feature && feature.forEach((fn) => fn(def));
|
||||
@ -96,7 +99,7 @@ export function NgOnChangesFeature<T>(type: Type<T>): (definition: DirectiveDef<
|
||||
}
|
||||
});
|
||||
}
|
||||
proto.ngDoCheck = (function(delegateDoCheck) {
|
||||
definition.doCheck = (function(delegateDoCheck) {
|
||||
return function(this: OnChangesExpando) {
|
||||
let simpleChanges = this[PRIVATE_PREFIX];
|
||||
if (simpleChanges != null) {
|
||||
@ -109,6 +112,7 @@ export function NgOnChangesFeature<T>(type: Type<T>): (definition: DirectiveDef<
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function PublicFeature<T>(definition: DirectiveDef<T>) {
|
||||
definition.diPublic = diPublic;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import {Type} from '../type';
|
||||
|
||||
import {assertLessThan} from './assert';
|
||||
import {assertPreviousIsParent, getPreviousOrParentNode, getRenderer, renderEmbeddedTemplate} from './instructions';
|
||||
import {ComponentTemplate, DirectiveDef, TypedDirectiveDef} from './interfaces/definition';
|
||||
import {ComponentTemplate, DirectiveDef} from './interfaces/definition';
|
||||
import {LInjector} from './interfaces/injector';
|
||||
import {LContainerNode, LElementNode, LNode, LNodeFlags, LViewNode} from './interfaces/node';
|
||||
import {QueryReadType} from './interfaces/query';
|
||||
@ -158,7 +158,7 @@ function createInjectionError(text: string, token: any) {
|
||||
* @param di The node injector in which a directive will be added
|
||||
* @param def The definition of the directive to be made public
|
||||
*/
|
||||
export function diPublicInInjector(di: LInjector, def: TypedDirectiveDef<any>): void {
|
||||
export function diPublicInInjector(di: LInjector, def: DirectiveDef<any>): void {
|
||||
bloomAdd(di, def.type);
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ export function diPublicInInjector(di: LInjector, def: TypedDirectiveDef<any>):
|
||||
*
|
||||
* @param def The definition of the directive to be made public
|
||||
*/
|
||||
export function diPublic(def: TypedDirectiveDef<any>): void {
|
||||
export function diPublic(def: DirectiveDef<any>): void {
|
||||
diPublicInInjector(getOrCreateNodeInjector(), def);
|
||||
}
|
||||
|
||||
@ -291,7 +291,7 @@ export function getOrCreateInjectable<T>(
|
||||
for (let i = start, ii = start + size; i < ii; i++) {
|
||||
// Get the definition for the directive at this index and, if it is injectable (diPublic),
|
||||
// and matches the given token, return the directive instance.
|
||||
const directiveDef = tData[i] as TypedDirectiveDef<any>;
|
||||
const directiveDef = tData[i] as DirectiveDef<any>;
|
||||
if (directiveDef.diPublic && directiveDef.type == token) {
|
||||
return node.view.data[i];
|
||||
}
|
||||
|
174
packages/core/src/render3/hooks.ts
Normal file
174
packages/core/src/render3/hooks.ts
Normal file
@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @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 {DirectiveDef} from './interfaces/definition';
|
||||
import {LNodeFlags} from './interfaces/node';
|
||||
import {HookData, LView, LifecycleStage, TView} from './interfaces/view';
|
||||
|
||||
|
||||
|
||||
/** Constants used by lifecycle hooks to determine when and how a hook should be called. */
|
||||
export const enum LifecycleHook {
|
||||
ON_INIT = 0b00,
|
||||
ON_CHECK = 0b01,
|
||||
|
||||
/* Mask used to get the type of the lifecycle hook from flags in hook queue */
|
||||
TYPE_MASK = 0b00000000000000000000000000000001,
|
||||
|
||||
/* Shift needed to get directive index from flags in hook queue */
|
||||
INDX_SHIFT = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is the first template pass, any ngOnInit or ngDoCheck hooks will be queued into
|
||||
* TView.initHooks during directiveCreate.
|
||||
*
|
||||
* The directive index and hook type are encoded into one number (1st bit: type, remaining bits:
|
||||
* directive index), then saved in the even indices of the initHooks array. The odd indices
|
||||
* hold the hook functions themselves.
|
||||
*
|
||||
* @param index The index of the directive in LView.data
|
||||
* @param hooks The static hooks map on the directive def
|
||||
* @param tView The current TView
|
||||
*/
|
||||
export function queueInitHooks(
|
||||
index: number, onInit: (() => void) | null, doCheck: (() => void) | null, tView: TView): void {
|
||||
if (tView.firstTemplatePass === true) {
|
||||
if (onInit != null) {
|
||||
(tView.initHooks || (tView.initHooks = [])).push(getInitFlags(index), onInit);
|
||||
}
|
||||
|
||||
if (doCheck != null) {
|
||||
(tView.initHooks || (tView.initHooks = [])).push(getCheckFlags(index), doCheck);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through the directives on a node and queues all their hooks except ngOnInit
|
||||
* and ngDoCheck, which are queued separately in directiveCreate.
|
||||
*/
|
||||
export function queueLifecycleHooks(flags: number, currentView: LView): void {
|
||||
const tView = currentView.tView;
|
||||
if (tView.firstTemplatePass === true) {
|
||||
const size = (flags & LNodeFlags.SIZE_MASK) >> LNodeFlags.SIZE_SHIFT;
|
||||
const start = flags >> LNodeFlags.INDX_SHIFT;
|
||||
|
||||
// It's necessary to loop through the directives at elementEnd() (rather than processing in
|
||||
// directiveCreate) so we can preserve the current hook order. Content, view, and destroy
|
||||
// hooks for projected components and directives must be called *before* their hosts.
|
||||
for (let i = start, end = start + size; i < end; i++) {
|
||||
const def = (tView.data[i] as DirectiveDef<any>);
|
||||
queueContentHooks(def, tView, i);
|
||||
queueViewHooks(def, tView, i);
|
||||
queueDestroyHooks(def, tView, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Queues afterContentInit and afterContentChecked hooks on TView */
|
||||
function queueContentHooks(def: DirectiveDef<any>, tView: TView, i: number): void {
|
||||
if (def.afterContentInit != null) {
|
||||
(tView.contentHooks || (tView.contentHooks = [])).push(getInitFlags(i), def.afterContentInit);
|
||||
}
|
||||
|
||||
if (def.afterContentChecked != null) {
|
||||
(tView.contentHooks || (tView.contentHooks = [
|
||||
])).push(getCheckFlags(i), def.afterContentChecked);
|
||||
}
|
||||
}
|
||||
|
||||
/** Queues afterViewInit and afterViewChecked hooks on TView */
|
||||
function queueViewHooks(def: DirectiveDef<any>, tView: TView, i: number): void {
|
||||
if (def.afterViewInit != null) {
|
||||
(tView.viewHooks || (tView.viewHooks = [])).push(getInitFlags(i), def.afterViewInit);
|
||||
}
|
||||
|
||||
if (def.afterViewChecked != null) {
|
||||
(tView.viewHooks || (tView.viewHooks = [])).push(getCheckFlags(i), def.afterViewChecked);
|
||||
}
|
||||
}
|
||||
|
||||
/** Queues onDestroy hooks on TView */
|
||||
function queueDestroyHooks(def: DirectiveDef<any>, tView: TView, i: number): void {
|
||||
if (def.onDestroy != null) {
|
||||
(tView.destroyHooks || (tView.destroyHooks = [])).push(i, def.onDestroy);
|
||||
}
|
||||
}
|
||||
|
||||
/** Generates flags for init-only hooks */
|
||||
function getInitFlags(index: number): number {
|
||||
return index << LifecycleHook.INDX_SHIFT;
|
||||
}
|
||||
|
||||
/** Generates flags for hooks called every change detection run */
|
||||
function getCheckFlags(index: number): number {
|
||||
return (index << LifecycleHook.INDX_SHIFT) | LifecycleHook.ON_CHECK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onInit and doCheck calls if they haven't already been called.
|
||||
*
|
||||
* @param currentView The current view
|
||||
*/
|
||||
export function executeInitHooks(currentView: LView): void {
|
||||
const initHooks = currentView.tView.initHooks;
|
||||
|
||||
if (currentView.lifecycleStage === LifecycleStage.INIT && initHooks != null) {
|
||||
executeLifecycleHooks(currentView, initHooks);
|
||||
currentView.lifecycleStage = LifecycleStage.CONTENT_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls all afterContentInit and afterContentChecked hooks for the view, then splices
|
||||
* out afterContentInit hooks to prep for the next run in update mode.
|
||||
*
|
||||
* @param currentView The current view
|
||||
*/
|
||||
export function executeContentHooks(currentView: LView): void {
|
||||
const contentHooks = currentView.tView.contentHooks;
|
||||
|
||||
if (currentView.lifecycleStage < LifecycleStage.VIEW_INIT && contentHooks != null) {
|
||||
executeLifecycleHooks(currentView, contentHooks);
|
||||
currentView.lifecycleStage = LifecycleStage.VIEW_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over afterViewInit and afterViewChecked functions and calls them.
|
||||
*
|
||||
* @param currentView The current view
|
||||
*/
|
||||
export function executeViewHooks(currentView: LView): void {
|
||||
const viewHooks = currentView.tView.viewHooks;
|
||||
|
||||
if (viewHooks != null) {
|
||||
executeLifecycleHooks(currentView, viewHooks);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls lifecycle hooks with their contexts, skipping init hooks if it's not
|
||||
* creation mode.
|
||||
*
|
||||
* @param currentView The current view
|
||||
* @param arr The array in which the hooks are found
|
||||
*/
|
||||
function executeLifecycleHooks(currentView: LView, arr: HookData): void {
|
||||
const data = currentView.data;
|
||||
const creationMode = currentView.creationMode;
|
||||
|
||||
for (let i = 0; i < arr.length; i += 2) {
|
||||
const flags = arr[i] as number;
|
||||
const initOnly = (flags & LifecycleHook.TYPE_MASK) === LifecycleHook.ON_INIT;
|
||||
if (initOnly === false || creationMode) {
|
||||
(arr[i | 1] as() => void).call(data[flags >> LifecycleHook.INDX_SHIFT]);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,8 +24,6 @@ export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_REA
|
||||
// clang-format off
|
||||
export {
|
||||
|
||||
LifecycleHook,
|
||||
|
||||
NO_CHANGE as NC,
|
||||
|
||||
bind as b,
|
||||
@ -52,7 +50,6 @@ export {
|
||||
elementStart as E,
|
||||
elementStyle as s,
|
||||
|
||||
lifecycle as l,
|
||||
listener as L,
|
||||
memory as m,
|
||||
|
||||
@ -72,6 +69,9 @@ export {
|
||||
query as Q,
|
||||
queryRefresh as qR,
|
||||
} from './query';
|
||||
|
||||
export {LifecycleHook} from './hooks';
|
||||
|
||||
// clang-format on
|
||||
|
||||
export {
|
||||
|
@ -8,39 +8,22 @@
|
||||
|
||||
import './ng_dev_mode';
|
||||
|
||||
import {ElementRef} from '../linker/element_ref';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull} from './assert';
|
||||
import {LContainer, TContainer} from './interfaces/container';
|
||||
import {LInjector} from './interfaces/injector';
|
||||
import {CssSelector, LProjection} from './interfaces/projection';
|
||||
import {LQuery, QueryReadType} from './interfaces/query';
|
||||
import {LView, TData, TView} from './interfaces/view';
|
||||
import {LView, LifecycleStage, TData, TView} from './interfaces/view';
|
||||
|
||||
import {LContainerNode, LElementNode, LNode, LNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue,} from './interfaces/node';
|
||||
import {assertNodeType, assertNodeOfPossibleTypes} from './node_assert';
|
||||
import {appendChild, insertChild, insertView, processProjectedNode, removeView} from './node_manipulation';
|
||||
import {isNodeMatchingSelector} from './node_selector_matcher';
|
||||
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, TypedDirectiveDef, TypedComponentDef} from './interfaces/definition';
|
||||
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType} from './interfaces/definition';
|
||||
import {RComment, RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3} from './interfaces/renderer';
|
||||
import {isDifferent, stringify} from './util';
|
||||
import {executeViewHooks, executeContentHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks';
|
||||
|
||||
|
||||
/**
|
||||
* Enum used by the lifecycle (l) instruction to determine which lifecycle hook is requesting
|
||||
* processing.
|
||||
*/
|
||||
export const enum LifecycleHook {
|
||||
ON_INIT = 1,
|
||||
ON_DESTROY = 2,
|
||||
ON_CHANGES = 4,
|
||||
AFTER_VIEW_INIT = 8,
|
||||
AFTER_VIEW_CHECKED = 16
|
||||
}
|
||||
|
||||
/**
|
||||
* Directive (D) sets a property on all component instances using this constant as a key and the
|
||||
* component's host node (LElement) as the value. This is used in methods like detectChanges to
|
||||
@ -91,7 +74,7 @@ let tData: TData;
|
||||
/** State of the current view being processed. */
|
||||
let currentView: LView;
|
||||
// The initialization has to be after the `let`, otherwise `createLView` can't see `let`.
|
||||
currentView = createLView(null !, null !, {data: []});
|
||||
currentView = createLView(null !, null !, createTView());
|
||||
|
||||
let currentQuery: LQuery|null;
|
||||
|
||||
@ -112,10 +95,11 @@ let data: any[];
|
||||
let bindingIndex: number;
|
||||
|
||||
/**
|
||||
* When a view is destroyed, listeners need to be released
|
||||
* and onDestroy callbacks need to be called. This cleanup array
|
||||
* stores both listener data (in chunks of 4) and onDestroy data
|
||||
* (in chunks of 2), as they'll be processed at the same time.
|
||||
* When a view is destroyed, listeners need to be released and outputs need to be
|
||||
* unsubscribed. This cleanup array stores both listener data (in chunks of 4)
|
||||
* and output data (in chunks of 2) for a particular view. Combining the arrays
|
||||
* saves on memory (70 bytes per array) and on a few bytes of code size (for two
|
||||
* separate for loops).
|
||||
*
|
||||
* If it's a listener being stored:
|
||||
* 1st index is: event name to remove
|
||||
@ -123,15 +107,12 @@ let bindingIndex: number;
|
||||
* 3rd index is: listener function
|
||||
* 4th index is: useCapture boolean
|
||||
*
|
||||
* If it's an onDestroy function:
|
||||
* 1st index is: onDestroy function
|
||||
* If it's an output subscription:
|
||||
* 1st index is: unsubscribe function
|
||||
* 2nd index is: context for function
|
||||
*/
|
||||
let cleanup: any[]|null;
|
||||
|
||||
/** Index in the data array at which view hooks begin to be stored. */
|
||||
let viewHookStartIndex: number|null;
|
||||
|
||||
/**
|
||||
* Swap the current state with a new state.
|
||||
*
|
||||
@ -151,7 +132,6 @@ export function enterView(newView: LView, host: LElementNode | LViewNode | null)
|
||||
tData = newView.tView.data;
|
||||
creationMode = newView.creationMode;
|
||||
|
||||
viewHookStartIndex = newView.viewHookStartIndex;
|
||||
cleanup = newView.cleanup;
|
||||
renderer = newView.renderer;
|
||||
|
||||
@ -161,6 +141,8 @@ export function enterView(newView: LView, host: LElementNode | LViewNode | null)
|
||||
}
|
||||
|
||||
currentView = newView;
|
||||
currentQuery = newView.query;
|
||||
|
||||
return oldView !;
|
||||
}
|
||||
|
||||
@ -169,7 +151,10 @@ export function enterView(newView: LView, host: LElementNode | LViewNode | null)
|
||||
* the direction of traversal (up or down the view tree) a bit clearer.
|
||||
*/
|
||||
export function leaveView(newView: LView): void {
|
||||
executeViewHooks();
|
||||
executeViewHooks(currentView);
|
||||
currentView.creationMode = false;
|
||||
currentView.lifecycleStage = LifecycleStage.INIT;
|
||||
currentView.tView.firstTemplatePass = false;
|
||||
enterView(newView, null);
|
||||
}
|
||||
|
||||
@ -189,10 +174,11 @@ export function createLView(
|
||||
next: null,
|
||||
bindingStartIndex: null,
|
||||
creationMode: true,
|
||||
viewHookStartIndex: null,
|
||||
template: template,
|
||||
context: context,
|
||||
dynamicViewCount: 0,
|
||||
lifecycleStage: LifecycleStage.INIT,
|
||||
query: null,
|
||||
};
|
||||
|
||||
return newView;
|
||||
@ -320,7 +306,7 @@ export function renderEmbeddedTemplate<T>(
|
||||
previousOrParentNode = null !;
|
||||
let cm: boolean = false;
|
||||
if (viewNode == null) {
|
||||
const view = createLView(-1, renderer, {data: []}, template, context);
|
||||
const view = createLView(-1, renderer, createTView(), template, context);
|
||||
viewNode = createLNode(null, LNodeFlags.View, null, view);
|
||||
cm = true;
|
||||
}
|
||||
@ -347,14 +333,13 @@ export function renderComponentOrTemplate<T>(
|
||||
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);
|
||||
// so to refresh the component, refresh() needs to be called with (1, 0)
|
||||
componentRefresh(1, 0);
|
||||
}
|
||||
} finally {
|
||||
if (rendererFactory.end) {
|
||||
rendererFactory.end();
|
||||
}
|
||||
hostView.creationMode = false;
|
||||
leaveView(oldView);
|
||||
}
|
||||
}
|
||||
@ -430,8 +415,6 @@ export function elementStart(
|
||||
if (hostComponentDef) {
|
||||
// TODO(mhevery): This assumes that the directives come in correct order, which
|
||||
// is not guaranteed. Must be refactored to take it into account.
|
||||
(hostComponentDef as TypedComponentDef<any>).type =
|
||||
nameOrComponentType as ComponentType<any>;
|
||||
directiveCreate(++index, hostComponentDef.n(), hostComponentDef, queryName);
|
||||
}
|
||||
hack_declareDirectives(index, directiveTypes, localRefs);
|
||||
@ -459,7 +442,6 @@ function hack_declareDirectives(
|
||||
// TODO(misko): refactor this to store the `DirectiveDef` in `TView.data`.
|
||||
const directiveType = directiveTypes[i];
|
||||
const directiveDef = directiveType.ngDirectiveDef;
|
||||
(directiveDef as TypedDirectiveDef<any>).type = directiveType;
|
||||
directiveCreate(
|
||||
++index, directiveDef.n(), directiveDef, hack_findQueryName(directiveDef, localRefs));
|
||||
}
|
||||
@ -494,7 +476,19 @@ function hack_findQueryName(
|
||||
* @returns TView
|
||||
*/
|
||||
function getOrCreateTView(template: ComponentTemplate<any>): TView {
|
||||
return template.ngPrivateData || (template.ngPrivateData = { data: [] } as never);
|
||||
return template.ngPrivateData || (template.ngPrivateData = createTView() as never);
|
||||
}
|
||||
|
||||
/** Creates a TView instance */
|
||||
export function createTView(): TView {
|
||||
return {
|
||||
data: [],
|
||||
firstTemplatePass: true,
|
||||
initHooks: null,
|
||||
contentHooks: null,
|
||||
viewHooks: null,
|
||||
destroyHooks: null
|
||||
};
|
||||
}
|
||||
|
||||
function setUpAttributes(native: RElement, attrs: string[]): void {
|
||||
@ -613,6 +607,7 @@ export function elementEnd() {
|
||||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Element);
|
||||
const query = previousOrParentNode.query;
|
||||
query && query.addNode(previousOrParentNode);
|
||||
queueLifecycleHooks(previousOrParentNode.flags, currentView);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -831,23 +826,22 @@ export function text(index: number, value?: any): void {
|
||||
* @param value Stringified value to write.
|
||||
*/
|
||||
export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
|
||||
// TODO(misko): I don't think index < nodes.length check is needed here.
|
||||
let existingNode = index < data.length && data[index] as LTextNode;
|
||||
if (existingNode && existingNode.native) {
|
||||
ngDevMode && assertDataInRange(index);
|
||||
let existingNode = data[index] as LTextNode;
|
||||
ngDevMode && assertNotNull(existingNode, 'existing node');
|
||||
if (existingNode.native) {
|
||||
// If DOM node exists and value changed, update textContent
|
||||
value !== NO_CHANGE &&
|
||||
((renderer as ProceduralRenderer3).setValue ?
|
||||
(renderer as ProceduralRenderer3).setValue(existingNode.native, stringify(value)) :
|
||||
existingNode.native.textContent = stringify(value));
|
||||
} else if (existingNode) {
|
||||
} else {
|
||||
// Node was created but DOM node creation was delayed. Create and append now.
|
||||
existingNode.native =
|
||||
((renderer as ProceduralRenderer3).createText ?
|
||||
(renderer as ProceduralRenderer3).createText(stringify(value)) :
|
||||
(renderer as ObjectOrientedRenderer3).createTextNode !(stringify(value)));
|
||||
insertChild(existingNode, currentView);
|
||||
} else {
|
||||
text(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -906,6 +900,11 @@ export function directiveCreate<T>(
|
||||
if (tNode && tNode.attrs) {
|
||||
setInputsFromAttrs<T>(instance, directiveDef !.inputs, tNode);
|
||||
}
|
||||
|
||||
// Init hooks are queued now so ngOnInit is called in host components before
|
||||
// any projected components.
|
||||
queueInitHooks(index, directiveDef.onInit, directiveDef.doCheck, currentView.tView);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ -966,76 +965,6 @@ function generateInitialInputs(
|
||||
return initialInputData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a lifecycle hook type and determines when and how the related lifecycle hook
|
||||
* callback should run.
|
||||
*
|
||||
* For the onInit lifecycle hook, it will return whether or not the ngOnInit() function
|
||||
* should run. If so, ngOnInit() will be called outside of this function.
|
||||
*
|
||||
* e.g. l(LifecycleHook.ON_INIT) && ctx.ngOnInit();
|
||||
*
|
||||
* For the onDestroy lifecycle hook, this instruction also accepts an onDestroy
|
||||
* method that should be stored and called internally when the parent view is being
|
||||
* cleaned up.
|
||||
*
|
||||
* e.g. l(LifecycleHook.ON_DESTROY, ctx, ctx.onDestroy);
|
||||
*
|
||||
* @param lifecycle
|
||||
* @param self
|
||||
* @param method
|
||||
*/
|
||||
export function lifecycle(lifecycle: LifecycleHook.ON_DESTROY, self: any, method: Function): void;
|
||||
export function lifecycle(
|
||||
lifecycle: LifecycleHook.AFTER_VIEW_INIT, self: any, method: Function): void;
|
||||
export function lifecycle(
|
||||
lifecycle: LifecycleHook.AFTER_VIEW_CHECKED, self: any, method: Function): void;
|
||||
export function lifecycle(lifecycle: LifecycleHook): boolean;
|
||||
export function lifecycle(lifecycle: LifecycleHook, self?: any, method?: Function): boolean {
|
||||
if (lifecycle === LifecycleHook.ON_INIT) {
|
||||
return creationMode;
|
||||
} else if (lifecycle === LifecycleHook.ON_DESTROY) {
|
||||
(cleanup || (currentView.cleanup = cleanup = [])).push(method, self);
|
||||
} else if (
|
||||
creationMode && (lifecycle === LifecycleHook.AFTER_VIEW_INIT ||
|
||||
lifecycle === LifecycleHook.AFTER_VIEW_CHECKED)) {
|
||||
if (viewHookStartIndex == null) {
|
||||
currentView.viewHookStartIndex = viewHookStartIndex = data.length;
|
||||
}
|
||||
data.push(lifecycle, method, self);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Iterates over view hook functions and calls them. */
|
||||
export function executeViewHooks(): void {
|
||||
if (viewHookStartIndex == null) return;
|
||||
|
||||
// Instead of using splice to remove init hooks after their first run (expensive), we
|
||||
// shift over the AFTER_CHECKED hooks as we call them and truncate once at the end.
|
||||
let checkIndex = viewHookStartIndex as number;
|
||||
let writeIndex = checkIndex;
|
||||
while (checkIndex < data.length) {
|
||||
// Call lifecycle hook with its context
|
||||
data[checkIndex + 1].call(data[checkIndex + 2]);
|
||||
|
||||
if (data[checkIndex] === LifecycleHook.AFTER_VIEW_CHECKED) {
|
||||
// We know if the writeIndex falls behind that there is an init that needs to
|
||||
// be overwritten.
|
||||
if (writeIndex < checkIndex) {
|
||||
data[writeIndex] = data[checkIndex];
|
||||
data[writeIndex + 1] = data[checkIndex + 1];
|
||||
data[writeIndex + 2] = data[checkIndex + 2];
|
||||
}
|
||||
writeIndex += 3;
|
||||
}
|
||||
checkIndex += 3;
|
||||
}
|
||||
|
||||
// Truncate once at the writeIndex
|
||||
data.length = writeIndex;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
//// ViewContainer & View
|
||||
@ -1071,14 +1000,17 @@ export function container(
|
||||
renderParent = currentParent as LElementNode;
|
||||
}
|
||||
|
||||
const node = createLNode(index, LNodeFlags.Container, comment, <LContainer>{
|
||||
const lContainer = <LContainer>{
|
||||
views: [],
|
||||
nextIndex: 0, renderParent,
|
||||
template: template == null ? null : template,
|
||||
next: null,
|
||||
parent: currentView,
|
||||
dynamicViewCount: 0,
|
||||
});
|
||||
query: null
|
||||
};
|
||||
|
||||
const node = createLNode(index, LNodeFlags.Container, comment, lContainer);
|
||||
|
||||
if (node.tNode == null) {
|
||||
// TODO(misko): implement queryName caching
|
||||
@ -1093,8 +1025,13 @@ export function container(
|
||||
|
||||
isParent = false;
|
||||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Container);
|
||||
const query = previousOrParentNode.query;
|
||||
query && query.addNode(previousOrParentNode);
|
||||
const query = node.query;
|
||||
if (query) {
|
||||
// check if a given container node matches
|
||||
query.addNode(node);
|
||||
// prepare place for matching nodes from views inserted into a given container
|
||||
lContainer.query = query.container();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1108,6 +1045,10 @@ export function containerRefreshStart(index: number): void {
|
||||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Container);
|
||||
isParent = true;
|
||||
(previousOrParentNode as LContainerNode).data.nextIndex = 0;
|
||||
|
||||
// We need to execute init hooks here so ngOnInit hooks are called in top level views
|
||||
// before they are called in embedded views (for backwards compatibility).
|
||||
executeInitHooks(currentView);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1171,6 +1112,10 @@ export function viewStart(viewBlockId: number): boolean {
|
||||
// When we create a new LView, we always reset the state of the instructions.
|
||||
const newView =
|
||||
createLView(viewBlockId, renderer, getOrCreateEmbeddedTView(viewBlockId, container));
|
||||
if (lContainer.query) {
|
||||
newView.query = lContainer.query.enterView(lContainer.nextIndex);
|
||||
}
|
||||
|
||||
enterView(newView, createLNode(null, LNodeFlags.View, null, newView));
|
||||
lContainer.nextIndex++;
|
||||
}
|
||||
@ -1194,7 +1139,7 @@ function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TV
|
||||
ngDevMode && assertNodeType(parent, LNodeFlags.Container);
|
||||
const tContainer = (parent !.tNode as TContainerNode).data;
|
||||
if (viewIndex >= tContainer.length || tContainer[viewIndex] == null) {
|
||||
tContainer[viewIndex] = { data: [] } as TView;
|
||||
tContainer[viewIndex] = createTView();
|
||||
}
|
||||
return tContainer[viewIndex];
|
||||
}
|
||||
@ -1215,7 +1160,6 @@ export function viewEnd(): void {
|
||||
|
||||
if (viewIdChanged) {
|
||||
insertView(container, viewNode, containerState.nextIndex - 1);
|
||||
currentView.creationMode = false;
|
||||
}
|
||||
}
|
||||
leaveView(currentView !.parent !);
|
||||
@ -1232,29 +1176,29 @@ export function viewEnd(): void {
|
||||
*
|
||||
* @param directiveIndex
|
||||
* @param elementIndex
|
||||
* @param template
|
||||
*/
|
||||
export const componentRefresh:
|
||||
<T>(directiveIndex: number, elementIndex: number, template: ComponentTemplate<T>) =>
|
||||
void = function<T>(
|
||||
directiveIndex: number, elementIndex: number, template: ComponentTemplate<T>) {
|
||||
ngDevMode && assertDataInRange(elementIndex);
|
||||
const element = data ![elementIndex] as LElementNode;
|
||||
ngDevMode && assertNodeOfPossibleTypes(element, LNodeFlags.Element, LNodeFlags.Container);
|
||||
ngDevMode && assertNotEqual(element.data, null, 'isComponent');
|
||||
ngDevMode && assertDataInRange(directiveIndex);
|
||||
const hostView = element.data !;
|
||||
ngDevMode && assertNotEqual(hostView, null, 'hostView');
|
||||
const directive = data[directiveIndex];
|
||||
const oldView = enterView(hostView, element);
|
||||
try {
|
||||
template(directive, creationMode);
|
||||
} finally {
|
||||
hostView.creationMode = false;
|
||||
refreshDynamicChildren();
|
||||
leaveView(oldView);
|
||||
export function componentRefresh<T>(directiveIndex: number, elementIndex: number): void {
|
||||
executeInitHooks(currentView);
|
||||
executeContentHooks(currentView);
|
||||
const template = (tData[directiveIndex] as ComponentDef<T>).template;
|
||||
if (template != null) {
|
||||
ngDevMode && assertDataInRange(elementIndex);
|
||||
const element = data ![elementIndex] as LElementNode;
|
||||
ngDevMode && assertNodeType(element, LNodeFlags.Element);
|
||||
ngDevMode && assertNotEqual(element.data, null, 'isComponent');
|
||||
ngDevMode && assertDataInRange(directiveIndex);
|
||||
const directive = data[directiveIndex];
|
||||
const hostView = element.data !;
|
||||
ngDevMode && assertNotEqual(hostView, null, 'hostView');
|
||||
const oldView = enterView(hostView, element);
|
||||
try {
|
||||
template(directive, creationMode);
|
||||
} finally {
|
||||
refreshDynamicChildren();
|
||||
leaveView(oldView);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruction to distribute projectable nodes among <ng-content> occurrences in a given template.
|
||||
|
@ -8,9 +8,11 @@
|
||||
|
||||
import {ComponentTemplate} from './definition';
|
||||
import {LElementNode, LViewNode} from './node';
|
||||
import {LQuery} from './query';
|
||||
import {LView, TView} from './view';
|
||||
|
||||
|
||||
|
||||
/** The state associated with an LContainer */
|
||||
export interface LContainer {
|
||||
/**
|
||||
@ -67,12 +69,17 @@ export interface LContainer {
|
||||
*/
|
||||
readonly template: ComponentTemplate<any>|null;
|
||||
|
||||
|
||||
/**
|
||||
* A count of dynamic views rendered into this container. If this is non-zero, the `views` array
|
||||
* will be traversed when refreshing dynamic views on this container.
|
||||
*/
|
||||
dynamicViewCount: number;
|
||||
|
||||
/**
|
||||
* Queries active for this container - all the views inserted to / removed from
|
||||
* this container are reported to queries referenced here.
|
||||
*/
|
||||
query: LQuery|null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,9 @@ export const enum DirectiveDefFlags {ContentQuery = 0b10}
|
||||
* `DirectiveDef` is a compiled version of the Directive used by the renderer instructions.
|
||||
*/
|
||||
export interface DirectiveDef<T> {
|
||||
/** Token representing the directive. Used by DI. */
|
||||
type: Type<T>;
|
||||
|
||||
/** Function that makes a directive public to the DI system. */
|
||||
diPublic: ((def: DirectiveDef<any>) => void)|null;
|
||||
|
||||
@ -64,42 +67,23 @@ export interface DirectiveDef<T> {
|
||||
*/
|
||||
n(): T;
|
||||
|
||||
/**
|
||||
* Refreshes the view of the component. Also calls lifecycle hooks like
|
||||
* ngAfterViewInit, if they are defined on the component.
|
||||
*
|
||||
* NOTE: this property is short (1 char) because it is used in component
|
||||
* templates which is sensitive to size.
|
||||
*
|
||||
* @param directiveIndex index of the directive in the containing template
|
||||
* @param elementIndex index of an host element for a given directive.
|
||||
*/
|
||||
r(directiveIndex: number, elementIndex: number): void;
|
||||
|
||||
/**
|
||||
* Refreshes host bindings on the associated directive. Also calls lifecycle hooks
|
||||
* like ngOnInit and ngDoCheck, if they are defined on the directive.
|
||||
*/
|
||||
// Note: This call must be separate from r() because hooks like ngOnInit need to
|
||||
// be called breadth-first across a view before processing onInits in children
|
||||
// (for backwards compatibility). Child template processing thus needs to be
|
||||
// delayed until all inputs and host bindings in a view have been checked.
|
||||
h(directiveIndex: number, elementIndex: number): void;
|
||||
|
||||
/* The following are lifecycle hooks for this component */
|
||||
onInit: (() => void)|null;
|
||||
doCheck: (() => void)|null;
|
||||
afterContentInit: (() => void)|null;
|
||||
afterContentChecked: (() => void)|null;
|
||||
afterViewInit: (() => void)|null;
|
||||
afterViewChecked: (() => void)|null;
|
||||
onDestroy: (() => void)|null;
|
||||
}
|
||||
|
||||
export interface ComponentDef<T> extends DirectiveDef<T> {
|
||||
/**
|
||||
* Refreshes the view of the component. Also calls lifecycle hooks like
|
||||
* ngAfterViewInit, if they are defined on the component.
|
||||
*
|
||||
* NOTE: this property is short (1 char) because it is used in
|
||||
* component templates which is sensitive to size.
|
||||
*
|
||||
* @param directiveIndex index of the directive in the containing template
|
||||
* @param elementIndex index of an host element for a given component.
|
||||
*/
|
||||
r(directiveIndex: number, elementIndex: number): void;
|
||||
|
||||
/**
|
||||
* The tag name which should be used by the component.
|
||||
*
|
||||
@ -122,31 +106,20 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
|
||||
readonly rendererType: RendererType2|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private: do not export
|
||||
*/
|
||||
export interface TypedDirectiveDef<T> extends DirectiveDef<T> { type: DirectiveType<T>; }
|
||||
|
||||
/**
|
||||
* Private: do not export
|
||||
*/
|
||||
export interface TypedComponentDef<T> extends ComponentDef<T> { type: ComponentType<T>; }
|
||||
|
||||
export interface DirectiveDefArgs<T> {
|
||||
type: Type<T>;
|
||||
factory: () => T;
|
||||
refresh?: (directiveIndex: number, elementIndex: number) => void;
|
||||
inputs?: {[P in keyof T]?: string};
|
||||
outputs?: {[P in keyof T]?: string};
|
||||
methods?: {[P in keyof T]?: string};
|
||||
features?: DirectiveDefFeature[];
|
||||
hostBindings?: (directiveIndex: number, elementIndex: number) => void;
|
||||
exportAs?: string;
|
||||
}
|
||||
|
||||
export interface ComponentDefArgs<T> extends DirectiveDefArgs<T> {
|
||||
tag: string;
|
||||
template: ComponentTemplate<T>;
|
||||
refresh?: (directiveIndex: number, elementIndex: number) => void;
|
||||
hostBindings?: (directiveIndex: number, elementIndex: number) => void;
|
||||
features?: ComponentDefFeature[];
|
||||
rendererType?: RendererType2;
|
||||
}
|
||||
|
@ -8,9 +8,7 @@
|
||||
|
||||
import {QueryList} from '../../linker';
|
||||
import {Type} from '../../type';
|
||||
|
||||
import {LInjector} from './injector';
|
||||
import {LContainerNode, LNode, LViewNode} from './node';
|
||||
import {LNode} from './node';
|
||||
|
||||
|
||||
/** Used for tracking queries (e.g. ViewChild, ContentChild). */
|
||||
@ -25,19 +23,28 @@ export interface LQuery {
|
||||
child(): LQuery|null;
|
||||
|
||||
/**
|
||||
* Notify `LQuery` that a `LNode` has been created.
|
||||
* Notify `LQuery` that a new `LNode` has been created and needs to be added to query results
|
||||
* if matching query predicate.
|
||||
*/
|
||||
addNode(node: LNode): void;
|
||||
|
||||
/**
|
||||
* Notify `LQuery` that an `LViewNode` has been added to `LContainerNode`.
|
||||
* Notify `LQuery` that a `LNode` has been created and needs to be added to query results
|
||||
* if matching query predicate.
|
||||
*/
|
||||
insertView(container: LContainerNode, view: LViewNode, insertIndex: number): void;
|
||||
container(): LQuery|null;
|
||||
|
||||
/**
|
||||
* Notify `LQuery` that an `LViewNode` has been removed from `LContainerNode`.
|
||||
* Notify `LQuery` that a new view was created and is being entered in the creation mode.
|
||||
* This allow queries to prepare space for matching nodes from views.
|
||||
*/
|
||||
removeView(container: LContainerNode, view: LViewNode, removeIndex: number): void;
|
||||
enterView(newViewIndex: number): LQuery|null;
|
||||
|
||||
/**
|
||||
* Notify `LQuery` that an `LViewNode` has been removed from `LContainerNode`. As a result all
|
||||
* the matching nodes from this view should be removed from container's queries.
|
||||
*/
|
||||
removeView(removeIndex: number): void;
|
||||
|
||||
/**
|
||||
* Add additional `QueryList` to track.
|
||||
|
@ -9,6 +9,7 @@
|
||||
import {LContainer} from './container';
|
||||
import {ComponentTemplate, DirectiveDef} from './definition';
|
||||
import {LElementNode, LViewNode, TNode} from './node';
|
||||
import {LQuery} from './query';
|
||||
import {Renderer3} from './renderer';
|
||||
|
||||
|
||||
@ -33,9 +34,6 @@ export interface LView {
|
||||
*/
|
||||
creationMode: boolean;
|
||||
|
||||
/** The index in the data array at which view hooks begin to be stored. */
|
||||
viewHookStartIndex: number|null;
|
||||
|
||||
/**
|
||||
* The parent view is needed when we exit the view and must restore the previous
|
||||
* `LView`. Without this, the render method would have to keep a stack of
|
||||
@ -72,9 +70,9 @@ export interface LView {
|
||||
bindingStartIndex: number|null;
|
||||
|
||||
/**
|
||||
* When a view is destroyed, listeners need to be released and onDestroy callbacks
|
||||
* need to be called. This cleanup array stores both listener data (in chunks of 4)
|
||||
* and onDestroy data (in chunks of 2) for a particular view. Combining the arrays
|
||||
* When a view is destroyed, listeners need to be released and outputs need to be
|
||||
* unsubscribed. This cleanup array stores both listener data (in chunks of 4)
|
||||
* and output data (in chunks of 2) for a particular view. Combining the arrays
|
||||
* saves on memory (70 bytes per array) and on a few bytes of code size (for two
|
||||
* separate for loops).
|
||||
*
|
||||
@ -84,12 +82,29 @@ export interface LView {
|
||||
* 3rd index is: listener function
|
||||
* 4th index is: useCapture boolean
|
||||
*
|
||||
* If it's an onDestroy function:
|
||||
* 1st index is: onDestroy function
|
||||
* 2nd index is; context for function
|
||||
* If it's an output subscription:
|
||||
* 1st index is: unsubscribe function
|
||||
* 2nd index is: context for function
|
||||
*/
|
||||
cleanup: any[]|null;
|
||||
|
||||
/**
|
||||
* This number tracks the next lifecycle hook that needs to be run.
|
||||
*
|
||||
* If lifecycleStage === LifecycleStage.ON_INIT, the init hooks haven't yet been run
|
||||
* and should be executed by the first r() instruction that runs OR the first
|
||||
* cR() instruction that runs (so inits are run for the top level view before any
|
||||
* embedded views).
|
||||
*
|
||||
* If lifecycleStage === LifecycleStage.CONTENT_INIT, the init hooks have been run, but
|
||||
* the content hooks have not yet been run. They should be executed on the first
|
||||
* r() instruction that runs.
|
||||
*
|
||||
* If lifecycleStage === LifecycleStage.VIEW_INIT, both the init hooks and content hooks
|
||||
* have already been run.
|
||||
*/
|
||||
lifecycleStage: LifecycleStage;
|
||||
|
||||
/**
|
||||
* The first LView or LContainer beneath this LView in the hierarchy.
|
||||
*
|
||||
@ -156,6 +171,11 @@ export interface LView {
|
||||
* after refreshing the view itself.
|
||||
*/
|
||||
dynamicViewCount: number;
|
||||
|
||||
/**
|
||||
* Queries active for this view - nodes from a view are reported to those queries
|
||||
*/
|
||||
query: LQuery|null;
|
||||
}
|
||||
|
||||
/** Interface necessary to work with view tree traversal */
|
||||
@ -172,7 +192,67 @@ export interface LViewOrLContainer {
|
||||
*
|
||||
* Stored on the template function as ngPrivateData.
|
||||
*/
|
||||
export interface TView { data: TData; }
|
||||
export interface TView {
|
||||
/** Static data equivalent of LView.data[]. Contains TNodes and directive defs. */
|
||||
data: TData;
|
||||
|
||||
/** Whether or not this template has been processed. */
|
||||
firstTemplatePass: boolean;
|
||||
|
||||
/**
|
||||
* Array of ngOnInit and ngDoCheck hooks that should be executed for this view.
|
||||
*
|
||||
* Even indices: Flags (1st bit: hook type, remaining: directive index)
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
initHooks: HookData|null;
|
||||
|
||||
/**
|
||||
* Array of ngAfterContentInit and ngAfterContentChecked hooks that should be executed for
|
||||
* this view.
|
||||
*
|
||||
* Even indices: Flags (1st bit: hook type, remaining: directive index)
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
contentHooks: HookData|null;
|
||||
|
||||
/**
|
||||
* Array of ngAfterViewInit and ngAfterViewChecked hooks that should be executed for
|
||||
* this view.
|
||||
*
|
||||
* Even indices: Flags (1st bit: hook type, remaining: directive index)
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
viewHooks: HookData|null;
|
||||
|
||||
/**
|
||||
* Array of ngOnDestroy hooks that should be executed when this view is destroyed.
|
||||
*
|
||||
* Even indices: Directive index
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
destroyHooks: HookData|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of hooks that should be executed for a view and their directive indices.
|
||||
*
|
||||
* Even indices: Flags (1st bit: hook type, remaining: directive index)
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
export type HookData = (number | (() => void))[];
|
||||
|
||||
/** Possible values of LView.lifecycleStage, used to determine which hooks to run. */
|
||||
export const enum LifecycleStage {
|
||||
/* Init hooks need to be run, if any. */
|
||||
INIT = 1,
|
||||
|
||||
/* Content hooks need to be run, if any. Init hooks have already run. */
|
||||
CONTENT_INIT = 2,
|
||||
|
||||
/* View hooks need to be run, if any. Any init hooks/content hooks have ran. */
|
||||
VIEW_INIT = 3
|
||||
}
|
||||
|
||||
/**
|
||||
* Static data that corresponds to the instance-specific data array on an LView.
|
||||
|
@ -11,7 +11,7 @@ import {LContainer, unusedValueExportToPlacateAjd as unused1} from './interfaces
|
||||
import {LContainerNode, LElementNode, LNode, LNodeFlags, LProjectionNode, LTextNode, LViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||
import {LProjection, unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
|
||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
||||
import {LView, LViewOrLContainer, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {HookData, LView, LViewOrLContainer, TView, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {assertNodeType} from './node_assert';
|
||||
|
||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
||||
@ -222,8 +222,6 @@ export function insertView(
|
||||
container, newView, true, findBeforeNode(index, state, container.native));
|
||||
}
|
||||
|
||||
// Notify query that view has been inserted
|
||||
container.query && container.query.insertView(container, newView, index);
|
||||
return newView;
|
||||
}
|
||||
|
||||
@ -248,7 +246,7 @@ export function removeView(container: LContainerNode, removeIndex: number): LVie
|
||||
destroyViewTree(viewNode.data);
|
||||
addRemoveViewFromContainer(container, viewNode, false);
|
||||
// Notify query that view has been removed
|
||||
container.query && container.query.removeView(container, viewNode, removeIndex);
|
||||
container.data.query && container.data.query.removeView(removeIndex);
|
||||
return viewNode;
|
||||
}
|
||||
|
||||
@ -295,17 +293,36 @@ export function getParentState(state: LViewOrLContainer, rootView: LView): LView
|
||||
* @param view The LView to clean up
|
||||
*/
|
||||
function cleanUpView(view: LView): void {
|
||||
if (!view.cleanup) return;
|
||||
removeListeners(view);
|
||||
executeOnDestroys(view);
|
||||
}
|
||||
|
||||
/** Removes listeners and unsubscribes from output subscriptions */
|
||||
function removeListeners(view: LView): void {
|
||||
const cleanup = view.cleanup !;
|
||||
for (let i = 0; i < cleanup.length - 1; i += 2) {
|
||||
if (typeof cleanup[i] === 'string') {
|
||||
cleanup ![i + 1].removeEventListener(cleanup[i], cleanup[i + 2], cleanup[i + 3]);
|
||||
i += 2;
|
||||
} else {
|
||||
cleanup[i].call(cleanup[i + 1]);
|
||||
if (cleanup != null) {
|
||||
for (let i = 0; i < cleanup.length - 1; i += 2) {
|
||||
if (typeof cleanup[i] === 'string') {
|
||||
cleanup ![i + 1].removeEventListener(cleanup[i], cleanup[i + 2], cleanup[i + 3]);
|
||||
i += 2;
|
||||
} else {
|
||||
cleanup[i].call(cleanup[i + 1]);
|
||||
}
|
||||
}
|
||||
view.cleanup = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls onDestroy hooks for this view */
|
||||
function executeOnDestroys(view: LView): void {
|
||||
const tView = view.tView;
|
||||
let destroyHooks: HookData|null;
|
||||
if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
|
||||
for (let i = 0; i < destroyHooks.length; i += 2) {
|
||||
const instance = view.data[destroyHooks[i] as number];
|
||||
(destroyHooks[i | 1] as() => void).call(instance);
|
||||
}
|
||||
}
|
||||
view.cleanup = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,37 +10,21 @@
|
||||
// correctly implementing its interfaces for backwards compatibility.
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
|
||||
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
|
||||
import {QueryList as viewEngine_QueryList} from '../linker/query_list';
|
||||
import {TemplateRef as viewEngine_TemplateRef} from '../linker/template_ref';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {assertNotNull} from './assert';
|
||||
import {assertEqual, assertNotNull} from './assert';
|
||||
import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di';
|
||||
import {assertPreviousIsParent, getCurrentQuery} from './instructions';
|
||||
import {DirectiveDef, TypedDirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||
import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||
import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||
import {LContainerNode, LElementNode, LNode, LNodeFlags, LViewNode, TNode, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||
import {LContainerNode, LElementNode, LNode, LNodeFlags, TNode, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||
import {LQuery, QueryReadType, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
||||
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||
import {flatten} from './util';
|
||||
|
||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4;
|
||||
|
||||
|
||||
export function query<T>(
|
||||
predicate: Type<any>| string[], descend?: boolean,
|
||||
read?: QueryReadType<T>| Type<T>): QueryList<T> {
|
||||
ngDevMode && assertPreviousIsParent();
|
||||
const queryList = new QueryList<T>();
|
||||
const query = getCurrentQuery(LQuery_);
|
||||
query.track(queryList, predicate, descend, read);
|
||||
return queryList;
|
||||
}
|
||||
|
||||
export function queryRefresh(query: QueryList<any>): boolean {
|
||||
return (query as any)._refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate which determines if a given element/directive should be included in the query
|
||||
*/
|
||||
@ -112,17 +96,90 @@ export class LQuery_ implements LQuery {
|
||||
}
|
||||
}
|
||||
|
||||
container(): LQuery|null {
|
||||
let result: QueryPredicate<any>|null = null;
|
||||
let predicate = this.deep;
|
||||
|
||||
while (predicate) {
|
||||
const containerValues: any[] = []; // prepare room for views
|
||||
predicate.values.push(containerValues);
|
||||
const clonedPredicate: QueryPredicate<any> = {
|
||||
next: null,
|
||||
list: predicate.list,
|
||||
type: predicate.type,
|
||||
selector: predicate.selector,
|
||||
read: predicate.read,
|
||||
values: containerValues
|
||||
};
|
||||
clonedPredicate.next = result;
|
||||
result = clonedPredicate;
|
||||
predicate = predicate.next;
|
||||
}
|
||||
|
||||
return result ? new LQuery_(result) : null;
|
||||
}
|
||||
|
||||
enterView(index: number): LQuery|null {
|
||||
let result: QueryPredicate<any>|null = null;
|
||||
let predicate = this.deep;
|
||||
|
||||
while (predicate) {
|
||||
const viewValues: any[] = []; // prepare room for view nodes
|
||||
predicate.values.splice(index, 0, viewValues);
|
||||
const clonedPredicate: QueryPredicate<any> = {
|
||||
next: null,
|
||||
list: predicate.list,
|
||||
type: predicate.type,
|
||||
selector: predicate.selector,
|
||||
read: predicate.read,
|
||||
values: viewValues
|
||||
};
|
||||
clonedPredicate.next = result;
|
||||
result = clonedPredicate;
|
||||
predicate = predicate.next;
|
||||
}
|
||||
|
||||
return result ? new LQuery_(result) : null;
|
||||
}
|
||||
|
||||
addNode(node: LNode): void {
|
||||
add(this.shallow, node);
|
||||
add(this.deep, node);
|
||||
}
|
||||
|
||||
insertView(container: LContainerNode, view: LViewNode, index: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
removeView(index: number): void {
|
||||
let predicate = this.deep;
|
||||
while (predicate) {
|
||||
const removed = predicate.values.splice(index, 1);
|
||||
|
||||
// mark a query as dirty only when removed view had matching modes
|
||||
ngDevMode && assertEqual(removed.length, 1, 'removed.length');
|
||||
if (removed[0].length) {
|
||||
predicate.list.setDirty();
|
||||
}
|
||||
|
||||
predicate = predicate.next;
|
||||
}
|
||||
}
|
||||
|
||||
removeView(container: LContainerNode, view: LViewNode, index: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
/**
|
||||
* Clone LQuery by taking all the deep query predicates and cloning those using a provided clone
|
||||
* function.
|
||||
* Shallow predicates are ignored.
|
||||
*/
|
||||
private _clonePredicates(
|
||||
predicateCloneFn: (predicate: QueryPredicate<any>) => QueryPredicate<any>): LQuery|null {
|
||||
let result: QueryPredicate<any>|null = null;
|
||||
let predicate = this.deep;
|
||||
|
||||
while (predicate) {
|
||||
const clonedPredicate = predicateCloneFn(predicate);
|
||||
clonedPredicate.next = result;
|
||||
result = clonedPredicate;
|
||||
predicate = predicate.next;
|
||||
}
|
||||
|
||||
return result ? new LQuery_(result) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +216,7 @@ function geIdxOfMatchingDirective(node: LNode, type: Type<any>): number|null {
|
||||
for (let i = flags >> LNodeFlags.INDX_SHIFT,
|
||||
ii = i + ((flags & LNodeFlags.SIZE_MASK) >> LNodeFlags.SIZE_SHIFT);
|
||||
i < ii; i++) {
|
||||
const def = tData[i] as TypedDirectiveDef<any>;
|
||||
const def = tData[i] as DirectiveDef<any>;
|
||||
if (def.diPublic && def.type === type) {
|
||||
return i;
|
||||
}
|
||||
@ -191,10 +248,10 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
||||
if (predicate.read !== null) {
|
||||
const requestedRead = readFromNodeInjector(nodeInjector, node, predicate.read);
|
||||
if (requestedRead !== null) {
|
||||
predicate.values.push(requestedRead);
|
||||
addMatch(predicate, requestedRead);
|
||||
}
|
||||
} else {
|
||||
predicate.values.push(node.view.data[directiveIdx]);
|
||||
addMatch(predicate, node.view.data[directiveIdx]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -207,10 +264,10 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
||||
if (predicate.read !== null) {
|
||||
const result = readFromNodeInjector(nodeInjector, node, predicate.read !, directiveIdx);
|
||||
if (result !== null) {
|
||||
predicate.values.push(result);
|
||||
addMatch(predicate, result);
|
||||
}
|
||||
} else {
|
||||
predicate.values.push(node.view.data[directiveIdx]);
|
||||
addMatch(predicate, node.view.data[directiveIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,27 +276,31 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
||||
}
|
||||
}
|
||||
|
||||
function addMatch(predicate: QueryPredicate<any>, matchingValue: any): void {
|
||||
predicate.values.push(matchingValue);
|
||||
predicate.list.setDirty();
|
||||
}
|
||||
|
||||
function createPredicate<T>(
|
||||
previous: QueryPredicate<any>| null, queryList: QueryList<T>, predicate: Type<T>| string[],
|
||||
read: QueryReadType<T>| Type<T>| null): QueryPredicate<T> {
|
||||
const isArray = Array.isArray(predicate);
|
||||
const values = <any>[];
|
||||
if ((queryList as any as QueryList_<T>)._valuesTree === null) {
|
||||
(queryList as any as QueryList_<T>)._valuesTree = values;
|
||||
}
|
||||
return {
|
||||
next: previous,
|
||||
list: queryList,
|
||||
type: isArray ? null : predicate as Type<T>,
|
||||
selector: isArray ? predicate as string[] : null,
|
||||
read: read,
|
||||
values: values
|
||||
values: (queryList as any as QueryList_<T>)._valuesTree
|
||||
};
|
||||
}
|
||||
|
||||
class QueryList_<T>/* implements viewEngine_QueryList<T> */ {
|
||||
dirty: boolean = false;
|
||||
changes: Observable<T>;
|
||||
readonly dirty = true;
|
||||
readonly changes: Observable<T>;
|
||||
private _values: T[]|null = null;
|
||||
/** @internal */
|
||||
_valuesTree: any[] = [];
|
||||
|
||||
get length(): number {
|
||||
ngDevMode && assertNotNull(this._values, 'refreshed');
|
||||
@ -258,21 +319,6 @@ class QueryList_<T>/* implements viewEngine_QueryList<T> */ {
|
||||
return values.length ? values[values.length - 1] : null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_valuesTree: any[]|null = null;
|
||||
/** @internal */
|
||||
_values: T[]|null = null;
|
||||
|
||||
/** @internal */
|
||||
_refresh(): boolean {
|
||||
// TODO(misko): needs more logic to flatten tree.
|
||||
if (this._values === null) {
|
||||
this._values = this._valuesTree;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
map<U>(fn: (item: T, index: number, array: T[]) => U): U[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
@ -295,10 +341,16 @@ class QueryList_<T>/* implements viewEngine_QueryList<T> */ {
|
||||
ngDevMode && assertNotNull(this._values, 'refreshed');
|
||||
return this._values !;
|
||||
}
|
||||
toString(): string { throw new Error('Method not implemented.'); }
|
||||
reset(res: (any[]|T)[]): void { throw new Error('Method not implemented.'); }
|
||||
toString(): string {
|
||||
ngDevMode && assertNotNull(this._values, 'refreshed');
|
||||
return this._values !.toString();
|
||||
}
|
||||
reset(res: (any[]|T)[]): void {
|
||||
this._values = flatten(res);
|
||||
(this as{dirty: boolean}).dirty = false;
|
||||
}
|
||||
notifyOnChanges(): void { throw new Error('Method not implemented.'); }
|
||||
setDirty(): void { throw new Error('Method not implemented.'); }
|
||||
setDirty(): void { (this as{dirty: boolean}).dirty = true; }
|
||||
destroy(): void { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
|
||||
@ -306,3 +358,27 @@ class QueryList_<T>/* implements viewEngine_QueryList<T> */ {
|
||||
// it can't be implemented only extended.
|
||||
export type QueryList<T> = viewEngine_QueryList<T>;
|
||||
export const QueryList: typeof viewEngine_QueryList = QueryList_ as any;
|
||||
|
||||
export function query<T>(
|
||||
predicate: Type<any>| string[], descend?: boolean,
|
||||
read?: QueryReadType<T>| Type<T>): QueryList<T> {
|
||||
ngDevMode && assertPreviousIsParent();
|
||||
const queryList = new QueryList<T>();
|
||||
const query = getCurrentQuery(LQuery_);
|
||||
query.track(queryList, predicate, descend, read);
|
||||
return queryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes a query by combining matches from all active views and removing matches from deleted
|
||||
* views.
|
||||
* Returns true if a query got dirty during change detection, false otherwise.
|
||||
*/
|
||||
export function queryRefresh(query: QueryList<any>): boolean {
|
||||
const queryImpl = (query as any as QueryList_<any>);
|
||||
if (query.dirty) {
|
||||
query.reset(queryImpl._valuesTree);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -31,3 +31,28 @@ export function stringify(value: any): string {
|
||||
export function notImplemented(): Error {
|
||||
return new Error('NotImplemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens an array in non-recursive way. Input arrays are not modified.
|
||||
*/
|
||||
export function flatten(list: any[]): any[] {
|
||||
const result: any[] = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < list.length) {
|
||||
const item = list[i];
|
||||
if (Array.isArray(item)) {
|
||||
if (item.length > 0) {
|
||||
list = item.concat(list.slice(i + 1));
|
||||
i = 0;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
result.push(item);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ describe('iv perf test', () => {
|
||||
it(`${iteration}. create ${count} divs in Render3`, () => {
|
||||
class Component {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Component,
|
||||
tag: 'div',
|
||||
template: function Template(ctx: any, cm: any) {
|
||||
if (cm) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {NgForOfContext} from '@angular/common';
|
||||
|
||||
import {C, E, T, b, cR, cr, defineComponent, e, p, t} from '../../src/render3/index';
|
||||
import {C, E, T, b, cR, cr, defineComponent, e, p, r, t} from '../../src/render3/index';
|
||||
|
||||
import {NgForOf} from './common_with_def';
|
||||
import {renderComponent, toHtml} from './render_util';
|
||||
@ -20,6 +20,7 @@ describe('@angular/common integration', () => {
|
||||
items: string[] = ['first', 'second'];
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyApp,
|
||||
factory: () => new MyApp(),
|
||||
tag: 'my-app',
|
||||
// <ul>
|
||||
@ -33,7 +34,7 @@ describe('@angular/common integration', () => {
|
||||
}
|
||||
p(1, 'ngForOf', b(myApp.items));
|
||||
cR(1);
|
||||
NgForOf.ngDirectiveDef.r(2, 0);
|
||||
r(2, 0);
|
||||
cr();
|
||||
|
||||
function liTemplate(row: NgForOfContext<string>, cm: boolean) {
|
||||
|
@ -15,13 +15,11 @@ import {DirectiveType, InjectFlags, NgOnChangesFeature, defineDirective, inject,
|
||||
export const NgForOf: DirectiveType<NgForOfDef<any>> = NgForOfDef as any;
|
||||
|
||||
NgForOf.ngDirectiveDef = defineDirective({
|
||||
type: NgForOfDef,
|
||||
factory: () => new NgForOfDef(
|
||||
injectViewContainerRef(), injectTemplateRef(),
|
||||
inject(IterableDiffers, InjectFlags.Default, defaultIterableDiffers)),
|
||||
features: [NgOnChangesFeature(NgForOf)],
|
||||
refresh: (directiveIndex: number, elementIndex: number) => {
|
||||
m<NgForOfDef<any>>(directiveIndex).ngDoCheck();
|
||||
},
|
||||
inputs: {
|
||||
ngForOf: 'ngForOf',
|
||||
ngForTrackBy: 'ngForTrackBy',
|
||||
|
@ -27,6 +27,7 @@ describe('compiler specification', () => {
|
||||
class MyComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'my-component',
|
||||
factory: () => new MyComponent(),
|
||||
template: function(ctx: MyComponent, cm: boolean) {
|
||||
@ -59,6 +60,7 @@ describe('compiler specification', () => {
|
||||
constructor() { log.push('ChildComponent'); }
|
||||
// NORMATIVE
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: ChildComponent,
|
||||
tag: `child`,
|
||||
factory: () => new ChildComponent(),
|
||||
template: function(ctx: ChildComponent, cm: boolean) {
|
||||
@ -77,6 +79,7 @@ describe('compiler specification', () => {
|
||||
constructor() { log.push('SomeDirective'); }
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = r3.defineDirective({
|
||||
type: SomeDirective,
|
||||
factory: () => new SomeDirective(),
|
||||
});
|
||||
// /NORMATIVE
|
||||
@ -86,6 +89,7 @@ describe('compiler specification', () => {
|
||||
class MyComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'my-component',
|
||||
factory: () => new MyComponent(),
|
||||
template: function(ctx: MyComponent, cm: boolean) {
|
||||
@ -94,8 +98,8 @@ describe('compiler specification', () => {
|
||||
r3.e();
|
||||
r3.T(3, '!');
|
||||
}
|
||||
ChildComponent.ngComponentDef.r(1, 0);
|
||||
SomeDirective.ngDirectiveDef.r(2, 0);
|
||||
r3.r(1, 0);
|
||||
r3.r(2, 0);
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
@ -119,6 +123,7 @@ describe('compiler specification', () => {
|
||||
constructor(template: TemplateRef<any>) { log.push('ifDirective'); }
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = r3.defineDirective({
|
||||
type: IfDirective,
|
||||
factory: () => new IfDirective(r3.injectTemplateRef()),
|
||||
});
|
||||
// /NORMATIVE
|
||||
@ -130,6 +135,7 @@ describe('compiler specification', () => {
|
||||
salutation = 'Hello';
|
||||
// NORMATIVE
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'my-component',
|
||||
factory: () => new MyComponent(),
|
||||
template: function(ctx: MyComponent, cm: boolean) {
|
||||
@ -140,7 +146,7 @@ describe('compiler specification', () => {
|
||||
}
|
||||
let foo = r3.m<any>(1);
|
||||
r3.cR(2);
|
||||
IfDirective.ngDirectiveDef.r(3, 2);
|
||||
r3.r(3, 2);
|
||||
r3.cr();
|
||||
|
||||
function C1(ctx1: any, cm: boolean) {
|
||||
@ -169,6 +175,7 @@ describe('compiler specification', () => {
|
||||
@Component({selector: 'simple', template: `<div><ng-content></ng-content></div>`})
|
||||
class SimpleComponent {
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: SimpleComponent,
|
||||
tag: 'simple',
|
||||
factory: () => new SimpleComponent(),
|
||||
template: function(ctx: SimpleComponent, cm: boolean) {
|
||||
@ -190,6 +197,7 @@ describe('compiler specification', () => {
|
||||
})
|
||||
class ComplexComponent {
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: ComplexComponent,
|
||||
tag: 'complex',
|
||||
factory: () => new ComplexComponent(),
|
||||
template: function(ctx: ComplexComponent, cm: boolean) {
|
||||
@ -215,6 +223,7 @@ describe('compiler specification', () => {
|
||||
})
|
||||
class MyApp {
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: MyApp,
|
||||
tag: 'my-app',
|
||||
factory: () => new MyApp(),
|
||||
template: function(ctx: MyApp, cm: boolean) {
|
||||
@ -236,6 +245,7 @@ describe('compiler specification', () => {
|
||||
class MyComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'my-component',
|
||||
factory: () => new MyComponent,
|
||||
template: function(ctx: MyComponent, cm: boolean) {
|
||||
@ -304,14 +314,12 @@ describe('compiler specification', () => {
|
||||
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = r3.defineDirective({
|
||||
type: ForOfDirective,
|
||||
factory: function ForOfDirective_Factory() {
|
||||
return new ForOfDirective(r3.injectViewContainerRef(), r3.injectTemplateRef());
|
||||
},
|
||||
// TODO(chuckj): Enable when ngForOf enabling lands.
|
||||
// features: [NgOnChangesFeature(NgForOf)],
|
||||
refresh: function ForOfDirective_Refresh(directiveIndex: number, elementIndex: number) {
|
||||
r3.m<ForOfDirective>(directiveIndex).ngDoCheck();
|
||||
},
|
||||
inputs: {forOf: 'forOf'}
|
||||
});
|
||||
// /NORMATIVE
|
||||
@ -333,6 +341,7 @@ describe('compiler specification', () => {
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'my-component',
|
||||
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||
template: function MyComponentTemplate(ctx: MyComponent, cm: boolean) {
|
||||
@ -343,7 +352,7 @@ describe('compiler specification', () => {
|
||||
}
|
||||
r3.p(1, 'forOf', r3.b(ctx.items));
|
||||
r3.cR(1);
|
||||
ForOfDirective.ngDirectiveDef.r(2, 1);
|
||||
r3.r(2, 1);
|
||||
r3.cr();
|
||||
|
||||
function MyComponent_ForOfDirective_Template_1(ctx1: any, cm: boolean) {
|
||||
@ -397,6 +406,7 @@ describe('compiler specification', () => {
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = r3.defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'my-component',
|
||||
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||
template: function MyComponent_Template(ctx: MyComponent, cm: boolean) {
|
||||
@ -407,7 +417,7 @@ describe('compiler specification', () => {
|
||||
}
|
||||
r3.p(1, 'forOf', r3.b(ctx.items));
|
||||
r3.cR(1);
|
||||
ForOfDirective.ngDirectiveDef.r(2, 1);
|
||||
r3.r(2, 1);
|
||||
r3.cr();
|
||||
|
||||
function MyComponent_ForOfDirective_Template_1(ctx1: any, cm: boolean) {
|
||||
@ -423,7 +433,7 @@ describe('compiler specification', () => {
|
||||
r3.t(1, r3.b1('', l0_item.name, ''));
|
||||
r3.p(4, 'forOf', r3.b(ctx.items));
|
||||
r3.cR(3);
|
||||
ForOfDirective.ngDirectiveDef.r(4, 3);
|
||||
r3.r(4, 3);
|
||||
r3.cr();
|
||||
|
||||
function MyComponent_ForOfDirective_ForOfDirective_Template_3(
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {ViewEncapsulation} from '../../src/core';
|
||||
import {E, T, b, defineComponent, e, markDirty, t} from '../../src/render3/index';
|
||||
import {E, T, b, defineComponent, e, markDirty, r, t} from '../../src/render3/index';
|
||||
import {createRendererType2} from '../../src/view/index';
|
||||
|
||||
import {getRendererFactory2} from './imported_renderer2';
|
||||
@ -20,6 +20,7 @@ describe('component', () => {
|
||||
increment() { this.count++; }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: CounterComponent,
|
||||
tag: 'counter',
|
||||
template: function(ctx: CounterComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -63,6 +64,7 @@ describe('component', () => {
|
||||
describe('encapsulation', () => {
|
||||
class WrapperComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: WrapperComponent,
|
||||
tag: 'wrapper',
|
||||
template: function(ctx: WrapperComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -70,7 +72,7 @@ describe('encapsulation', () => {
|
||||
e();
|
||||
}
|
||||
EncapsulatedComponent.ngComponentDef.h(1, 0);
|
||||
EncapsulatedComponent.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
},
|
||||
factory: () => new WrapperComponent,
|
||||
});
|
||||
@ -78,6 +80,7 @@ describe('encapsulation', () => {
|
||||
|
||||
class EncapsulatedComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: EncapsulatedComponent,
|
||||
tag: 'encapsulated',
|
||||
template: function(ctx: EncapsulatedComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -86,7 +89,7 @@ describe('encapsulation', () => {
|
||||
e();
|
||||
}
|
||||
LeafComponent.ngComponentDef.h(2, 1);
|
||||
LeafComponent.ngComponentDef.r(2, 1);
|
||||
r(2, 1);
|
||||
},
|
||||
factory: () => new EncapsulatedComponent,
|
||||
rendererType:
|
||||
@ -96,6 +99,7 @@ describe('encapsulation', () => {
|
||||
|
||||
class LeafComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: LeafComponent,
|
||||
tag: 'leaf',
|
||||
template: function(ctx: LeafComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -125,6 +129,7 @@ describe('encapsulation', () => {
|
||||
it('should encapsulate host and children with different attributes', () => {
|
||||
class WrapperComponentWith {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: WrapperComponentWith,
|
||||
tag: 'wrapper',
|
||||
template: function(ctx: WrapperComponentWith, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -132,7 +137,7 @@ describe('encapsulation', () => {
|
||||
e();
|
||||
}
|
||||
LeafComponentwith.ngComponentDef.h(1, 0);
|
||||
LeafComponentwith.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
},
|
||||
factory: () => new WrapperComponentWith,
|
||||
rendererType:
|
||||
@ -142,6 +147,7 @@ describe('encapsulation', () => {
|
||||
|
||||
class LeafComponentwith {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: LeafComponentwith,
|
||||
tag: 'leaf',
|
||||
template: function(ctx: LeafComponentwith, cm: boolean) {
|
||||
if (cm) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {C, E, P, T, V, cR, cr, detectChanges, e, m, pD, v} from '../../src/render3/index';
|
||||
import {C, E, P, T, V, cR, cr, detectChanges, e, m, pD, r, v} from '../../src/render3/index';
|
||||
|
||||
import {createComponent, renderComponent, toHtml} from './render_util';
|
||||
|
||||
@ -35,7 +35,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div>content</div></child>');
|
||||
@ -55,7 +55,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child>content</child>');
|
||||
@ -77,7 +77,7 @@ describe('content projection', () => {
|
||||
{ P(3, 0); }
|
||||
e();
|
||||
GrandChild.ngComponentDef.h(2, 1);
|
||||
GrandChild.ngComponentDef.r(2, 1);
|
||||
r(2, 1);
|
||||
}
|
||||
});
|
||||
const Parent = createComponent('parent', function(ctx: any, cm: boolean) {
|
||||
@ -92,13 +92,55 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent))
|
||||
.toEqual('<child><grand-child><div><b>Hello</b>World!</div></grand-child></child>');
|
||||
});
|
||||
|
||||
it('should project components', () => {
|
||||
|
||||
/** <div><ng-content></ng-content></div> */
|
||||
const Child = createComponent('child', (ctx: any, cm: boolean) => {
|
||||
if (cm) {
|
||||
pD(0);
|
||||
E(1, 'div');
|
||||
{ P(2, 0); }
|
||||
e();
|
||||
}
|
||||
});
|
||||
|
||||
const ProjectedComp = createComponent('projected-comp', (ctx: any, cm: boolean) => {
|
||||
if (cm) {
|
||||
T(0, 'content');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* <child>
|
||||
* <projected-comp></projected-comp>
|
||||
* </child>
|
||||
*/
|
||||
const Parent = createComponent('parent', (ctx: any, cm: boolean) => {
|
||||
if (cm) {
|
||||
E(0, Child);
|
||||
{
|
||||
E(2, ProjectedComp);
|
||||
e();
|
||||
}
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
ProjectedComp.ngComponentDef.h(3, 2);
|
||||
r(3, 2);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent))
|
||||
.toEqual('<child><div><projected-comp>content</projected-comp></div></child>');
|
||||
});
|
||||
|
||||
it('should project content with container.', () => {
|
||||
const Child = createComponent('child', function(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -129,7 +171,7 @@ describe('content projection', () => {
|
||||
}
|
||||
cr();
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div>()</div></child>');
|
||||
@ -165,7 +207,7 @@ describe('content projection', () => {
|
||||
}
|
||||
cr();
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child></child>');
|
||||
@ -214,7 +256,7 @@ describe('content projection', () => {
|
||||
}
|
||||
cr();
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div>(else)</div></child>');
|
||||
@ -272,7 +314,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div><span>content</span></div></child>');
|
||||
@ -325,7 +367,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div>content</div></child>');
|
||||
@ -362,7 +404,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div></div><span>content</span></child>');
|
||||
@ -420,7 +462,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child>content<div></div></child>');
|
||||
@ -469,7 +511,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
@ -515,7 +557,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
@ -561,7 +603,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
@ -607,7 +649,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
@ -654,7 +696,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
@ -701,8 +743,8 @@ describe('content projection', () => {
|
||||
}
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(0, 0);
|
||||
Child.ngComponentDef.r(0, 0);
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
@ -750,7 +792,7 @@ describe('content projection', () => {
|
||||
}
|
||||
e();
|
||||
GrandChild.ngComponentDef.h(2, 1);
|
||||
GrandChild.ngComponentDef.r(2, 1);
|
||||
r(2, 1);
|
||||
}
|
||||
});
|
||||
|
||||
@ -772,7 +814,7 @@ describe('content projection', () => {
|
||||
e();
|
||||
}
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
@ -821,7 +863,7 @@ describe('content projection', () => {
|
||||
}
|
||||
cr();
|
||||
Child.ngComponentDef.h(1, 0);
|
||||
Child.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
});
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><span><div>content</div></span></child>');
|
||||
|
@ -28,6 +28,7 @@ describe('define', () => {
|
||||
}
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: MyDirective,
|
||||
factory: () => new MyDirective(),
|
||||
features: [NgOnChangesFeature(MyDirective)],
|
||||
inputs: {valA: 'valA', valB: 'valB'}
|
||||
@ -41,7 +42,7 @@ describe('define', () => {
|
||||
expect(myDir.log).toEqual(['second']);
|
||||
expect(myDir.valB).toEqual('works');
|
||||
myDir.log.length = 0;
|
||||
myDir.ngDoCheck();
|
||||
MyDirective.ngDirectiveDef.doCheck !.call(myDir);
|
||||
expect(myDir.log).toEqual([
|
||||
'ngOnChanges', 'valA', 'initValue', 'first', 'valB', undefined, 'second', 'ngDoCheck'
|
||||
]);
|
||||
|
@ -11,7 +11,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {defineComponent} from '../../src/render3/definition';
|
||||
import {InjectFlags, bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector} from '../../src/render3/di';
|
||||
import {C, E, PublicFeature, T, V, b, b2, cR, cr, defineDirective, e, inject, injectElementRef, injectTemplateRef, injectViewContainerRef, m, t, v} from '../../src/render3/index';
|
||||
import {createLNode, createLView, enterView, leaveView} from '../../src/render3/instructions';
|
||||
import {createLNode, createLView, createTView, enterView, leaveView} from '../../src/render3/instructions';
|
||||
import {LInjector} from '../../src/render3/interfaces/injector';
|
||||
import {LNodeFlags} from '../../src/render3/interfaces/node';
|
||||
|
||||
@ -22,7 +22,7 @@ describe('di', () => {
|
||||
it('should create directive with no deps', () => {
|
||||
class Directive {
|
||||
value: string = 'Created';
|
||||
static ngDirectiveDef = defineDirective({factory: () => new Directive});
|
||||
static ngDirectiveDef = defineDirective({type: Directive, factory: () => new Directive});
|
||||
}
|
||||
|
||||
function Template(ctx: any, cm: boolean) {
|
||||
@ -42,21 +42,23 @@ describe('di', () => {
|
||||
it('should create directive with inter view dependencies', () => {
|
||||
class DirectiveA {
|
||||
value: string = 'A';
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => new DirectiveA, features: [PublicFeature]});
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{type: DirectiveA, factory: () => new DirectiveA, features: [PublicFeature]});
|
||||
}
|
||||
|
||||
class DirectiveB {
|
||||
value: string = 'B';
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => new DirectiveB, features: [PublicFeature]});
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{type: DirectiveB, factory: () => new DirectiveB, features: [PublicFeature]});
|
||||
}
|
||||
|
||||
class DirectiveC {
|
||||
value: string;
|
||||
constructor(a: DirectiveA, b: DirectiveB) { this.value = a.value + b.value; }
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{factory: () => new DirectiveC(inject(DirectiveA), inject(DirectiveB))});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: DirectiveC,
|
||||
factory: () => new DirectiveC(inject(DirectiveA), inject(DirectiveB))
|
||||
});
|
||||
}
|
||||
|
||||
function Template(ctx: any, cm: boolean) {
|
||||
@ -83,8 +85,11 @@ describe('di', () => {
|
||||
constructor(public elementRef: ElementRef) {
|
||||
this.value = (elementRef.constructor as any).name;
|
||||
}
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{factory: () => new Directive(injectElementRef()), features: [PublicFeature]});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: Directive,
|
||||
factory: () => new Directive(injectElementRef()),
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
class DirectiveSameInstance {
|
||||
@ -92,8 +97,10 @@ describe('di', () => {
|
||||
constructor(elementRef: ElementRef, directive: Directive) {
|
||||
this.value = elementRef === directive.elementRef;
|
||||
}
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{factory: () => new DirectiveSameInstance(injectElementRef(), inject(Directive))});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: DirectiveSameInstance,
|
||||
factory: () => new DirectiveSameInstance(injectElementRef(), inject(Directive))
|
||||
});
|
||||
}
|
||||
|
||||
function Template(ctx: any, cm: boolean) {
|
||||
@ -116,8 +123,11 @@ describe('di', () => {
|
||||
constructor(public templateRef: TemplateRef<any>) {
|
||||
this.value = (templateRef.constructor as any).name;
|
||||
}
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{factory: () => new Directive(injectTemplateRef()), features: [PublicFeature]});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: Directive,
|
||||
factory: () => new Directive(injectTemplateRef()),
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
class DirectiveSameInstance {
|
||||
@ -125,8 +135,10 @@ describe('di', () => {
|
||||
constructor(templateRef: TemplateRef<any>, directive: Directive) {
|
||||
this.value = templateRef === directive.templateRef;
|
||||
}
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{factory: () => new DirectiveSameInstance(injectTemplateRef(), inject(Directive))});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: DirectiveSameInstance,
|
||||
factory: () => new DirectiveSameInstance(injectTemplateRef(), inject(Directive))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -149,8 +161,11 @@ describe('di', () => {
|
||||
constructor(public viewContainerRef: ViewContainerRef) {
|
||||
this.value = (viewContainerRef.constructor as any).name;
|
||||
}
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{factory: () => new Directive(injectViewContainerRef()), features: [PublicFeature]});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: Directive,
|
||||
factory: () => new Directive(injectViewContainerRef()),
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
class DirectiveSameInstance {
|
||||
@ -159,6 +174,7 @@ describe('di', () => {
|
||||
this.value = viewContainerRef === directive.viewContainerRef;
|
||||
}
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: DirectiveSameInstance,
|
||||
factory: () => new DirectiveSameInstance(injectViewContainerRef(), inject(Directive))
|
||||
});
|
||||
}
|
||||
@ -224,7 +240,7 @@ describe('di', () => {
|
||||
constructor(public value: string) {}
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
// type: MyApp,
|
||||
type: MyApp,
|
||||
tag: 'my-app',
|
||||
factory: () => new MyApp(inject(String as any, InjectFlags.Default, 'DefaultValue')),
|
||||
template: () => null
|
||||
@ -237,8 +253,11 @@ describe('di', () => {
|
||||
|
||||
it('should inject from parent view', () => {
|
||||
class ParentDirective {
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => new ParentDirective(), features: [PublicFeature]});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: ParentDirective,
|
||||
factory: () => new ParentDirective(),
|
||||
features: [PublicFeature]
|
||||
});
|
||||
}
|
||||
|
||||
class ChildDirective {
|
||||
@ -247,6 +266,7 @@ describe('di', () => {
|
||||
this.value = (parent.constructor as any).name;
|
||||
}
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: ChildDirective,
|
||||
factory: () => new ChildDirective(inject(ParentDirective)),
|
||||
features: [PublicFeature]
|
||||
});
|
||||
@ -257,8 +277,10 @@ describe('di', () => {
|
||||
constructor(parent: ParentDirective, child: ChildDirective) {
|
||||
this.value = parent === child.parent;
|
||||
}
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{factory: () => new Child2Directive(inject(ParentDirective), inject(ChildDirective))});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: Child2Directive,
|
||||
factory: () => new Child2Directive(inject(ParentDirective), inject(ChildDirective))
|
||||
});
|
||||
}
|
||||
|
||||
function Template(ctx: any, cm: boolean) {
|
||||
@ -290,7 +312,7 @@ describe('di', () => {
|
||||
|
||||
describe('getOrCreateNodeInjector', () => {
|
||||
it('should handle initial undefined state', () => {
|
||||
const contentView = createLView(-1, null !, {data: []});
|
||||
const contentView = createLView(-1, null !, createTView());
|
||||
const oldView = enterView(contentView, null !);
|
||||
try {
|
||||
const parent = createLNode(0, LNodeFlags.Element, null, null);
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {E, b, defineDirective, e, m, p} from '../../src/render3/index';
|
||||
import {E, b, defineDirective, e, m, p, r} from '../../src/render3/index';
|
||||
|
||||
import {renderToHtml} from './render_util';
|
||||
|
||||
@ -20,8 +20,9 @@ describe('directive', () => {
|
||||
class Directive {
|
||||
klass = 'foo';
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: Directive,
|
||||
factory: () => directiveInstance = new Directive,
|
||||
refresh: (directiveIndex: number, elementIndex: number) => {
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
p(elementIndex, 'className', b(m<Directive>(directiveIndex).klass));
|
||||
}
|
||||
});
|
||||
@ -32,7 +33,8 @@ describe('directive', () => {
|
||||
E(0, 'span', null, [Directive]);
|
||||
e();
|
||||
}
|
||||
Directive.ngDirectiveDef.r(1, 0);
|
||||
Directive.ngDirectiveDef.h(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
expect(renderToHtml(Template, {})).toEqual('<span class="foo"></span>');
|
||||
|
@ -42,8 +42,12 @@ describe('exports', () => {
|
||||
class MyComponent {
|
||||
name = 'Nancy';
|
||||
|
||||
static ngComponentDef =
|
||||
defineComponent({tag: 'comp', template: function() {}, factory: () => new MyComponent});
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'comp',
|
||||
template: function() {},
|
||||
factory: () => new MyComponent
|
||||
});
|
||||
}
|
||||
|
||||
expect(renderToHtml(Template, {})).toEqual('<comp></comp>Nancy');
|
||||
@ -55,14 +59,19 @@ describe('exports', () => {
|
||||
let myDir: MyDir;
|
||||
class MyComponent {
|
||||
constructor() { myComponent = this; }
|
||||
static ngComponentDef =
|
||||
defineComponent({tag: 'comp', template: function() {}, factory: () => new MyComponent});
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'comp',
|
||||
template: function() {},
|
||||
factory: () => new MyComponent
|
||||
});
|
||||
}
|
||||
|
||||
class MyDir {
|
||||
myDir: MyComponent;
|
||||
constructor() { myDir = this; }
|
||||
static ngDirectiveDef = defineDirective({factory: () => new MyDir, inputs: {myDir: 'myDir'}});
|
||||
static ngDirectiveDef =
|
||||
defineDirective({type: MyDir, factory: () => new MyDir, inputs: {myDir: 'myDir'}});
|
||||
}
|
||||
|
||||
/** <comp #myComp></comp> <div [myDir]="myComp"></div> */
|
||||
@ -94,7 +103,7 @@ describe('exports', () => {
|
||||
|
||||
class SomeDir {
|
||||
name = 'Drew';
|
||||
static ngDirectiveDef = defineDirective({factory: () => new SomeDir});
|
||||
static ngDirectiveDef = defineDirective({type: SomeDir, factory: () => new SomeDir});
|
||||
}
|
||||
|
||||
expect(renderToHtml(Template, {})).toEqual('<div></div>Drew');
|
||||
@ -175,6 +184,7 @@ describe('exports', () => {
|
||||
constructor() { myComponent = this; }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'comp',
|
||||
template: function(ctx: MyComponent, cm: boolean) {},
|
||||
factory: () => new MyComponent
|
||||
@ -187,7 +197,7 @@ describe('exports', () => {
|
||||
constructor() { myDir = this; }
|
||||
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => new MyDir, inputs: {myDir: 'myDir'}});
|
||||
defineDirective({type: MyDir, factory: () => new MyDir, inputs: {myDir: 'myDir'}});
|
||||
}
|
||||
|
||||
/** <div [myDir]="myComp"></div><comp #myComp></comp> */
|
||||
@ -230,8 +240,12 @@ describe('exports', () => {
|
||||
|
||||
constructor() { myComponent = this; }
|
||||
|
||||
static ngComponentDef =
|
||||
defineComponent({tag: 'comp', template: function() {}, factory: () => new MyComponent});
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'comp',
|
||||
template: function() {},
|
||||
factory: () => new MyComponent
|
||||
});
|
||||
}
|
||||
expect(renderToHtml(Template, {})).toEqual('oneNancy<comp></comp><input value="one">');
|
||||
});
|
||||
|
@ -210,6 +210,7 @@ describe('render3 integration test', () => {
|
||||
value = ' one';
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: TodoComponent,
|
||||
tag: 'todo',
|
||||
template: function TodoTemplate(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -233,7 +234,7 @@ describe('render3 integration test', () => {
|
||||
e();
|
||||
}
|
||||
TodoComponent.ngComponentDef.h(1, 0);
|
||||
TodoComponent.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
expect(renderToHtml(Template, null)).toEqual('<todo><p>Todo one</p></todo>');
|
||||
@ -247,7 +248,7 @@ describe('render3 integration test', () => {
|
||||
T(2, 'two');
|
||||
}
|
||||
TodoComponent.ngComponentDef.h(1, 0);
|
||||
TodoComponent.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
expect(renderToHtml(Template, null)).toEqual('<todo><p>Todo one</p></todo>two');
|
||||
});
|
||||
@ -266,8 +267,8 @@ describe('render3 integration test', () => {
|
||||
}
|
||||
TodoComponent.ngComponentDef.h(1, 0);
|
||||
TodoComponent.ngComponentDef.h(3, 2);
|
||||
TodoComponent.ngComponentDef.r(1, 0);
|
||||
TodoComponent.ngComponentDef.r(3, 2);
|
||||
r(1, 0);
|
||||
r(3, 2);
|
||||
}
|
||||
expect(renderToHtml(Template, null))
|
||||
.toEqual('<todo><p>Todo one</p></todo><todo><p>Todo one</p></todo>');
|
||||
@ -279,6 +280,7 @@ describe('render3 integration test', () => {
|
||||
class TodoComponentHostBinding {
|
||||
title = 'one';
|
||||
static ngComponentDef = defineComponent({
|
||||
type: TodoComponentHostBinding,
|
||||
tag: 'todo',
|
||||
template: function TodoComponentHostBindingTemplate(
|
||||
ctx: TodoComponentHostBinding, cm: boolean) {
|
||||
@ -301,7 +303,7 @@ describe('render3 integration test', () => {
|
||||
e();
|
||||
}
|
||||
TodoComponentHostBinding.ngComponentDef.h(1, 0);
|
||||
TodoComponentHostBinding.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
expect(renderToHtml(Template, {})).toEqual('<todo title="one">one</todo>');
|
||||
@ -314,6 +316,7 @@ describe('render3 integration test', () => {
|
||||
class MyComp {
|
||||
name = 'Bess';
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyComp,
|
||||
tag: 'comp',
|
||||
template: function MyCompTemplate(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -333,7 +336,7 @@ describe('render3 integration test', () => {
|
||||
e();
|
||||
}
|
||||
MyComp.ngComponentDef.h(1, 0);
|
||||
MyComp.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
expect(renderToHtml(Template, null)).toEqual('<comp><p>Bess</p></comp>');
|
||||
@ -348,6 +351,7 @@ describe('render3 integration test', () => {
|
||||
class MyComp {
|
||||
condition: boolean;
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyComp,
|
||||
tag: 'comp',
|
||||
template: function MyCompTemplate(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -379,7 +383,7 @@ describe('render3 integration test', () => {
|
||||
}
|
||||
p(0, 'condition', b(ctx.condition));
|
||||
MyComp.ngComponentDef.h(1, 0);
|
||||
MyComp.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
expect(renderToHtml(Template, {condition: true})).toEqual('<comp><div>text</div></comp>');
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {C, E, L, T, V, cR, cr, defineComponent, e, v} from '../../src/render3/index';
|
||||
import {C, E, L, T, V, cR, cr, defineComponent, e, r, v} from '../../src/render3/index';
|
||||
|
||||
import {containerEl, renderComponent, renderToHtml} from './render_util';
|
||||
|
||||
@ -21,6 +21,7 @@ describe('event listeners', () => {
|
||||
onClick() { this.counter++; }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyComp,
|
||||
tag: 'comp',
|
||||
/** <button (click)="onClick()"> Click me </button> */
|
||||
template: function CompTemplate(ctx: any, cm: boolean) {
|
||||
@ -207,8 +208,8 @@ describe('event listeners', () => {
|
||||
}
|
||||
MyComp.ngComponentDef.h(2, 1);
|
||||
MyComp.ngComponentDef.h(4, 3);
|
||||
MyComp.ngComponentDef.r(2, 1);
|
||||
MyComp.ngComponentDef.r(4, 3);
|
||||
r(2, 1);
|
||||
r(4, 3);
|
||||
v();
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {EventEmitter} from '@angular/core';
|
||||
|
||||
import {C, E, L, LifecycleHook, T, V, b, cR, cr, defineComponent, defineDirective, e, l, p, v} from '../../src/render3/index';
|
||||
import {C, E, L, T, V, b, cR, cr, defineComponent, defineDirective, e, p, r, v} from '../../src/render3/index';
|
||||
|
||||
import {containerEl, renderToHtml} from './render_util';
|
||||
|
||||
@ -20,6 +20,7 @@ describe('outputs', () => {
|
||||
resetStream = new EventEmitter();
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: ButtonToggle,
|
||||
tag: 'button-toggle',
|
||||
template: function(ctx: any, cm: boolean) {},
|
||||
factory: () => buttonToggle = new ButtonToggle(),
|
||||
@ -32,8 +33,11 @@ describe('outputs', () => {
|
||||
class OtherDir {
|
||||
changeStream = new EventEmitter();
|
||||
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{factory: () => otherDir = new OtherDir, outputs: {changeStream: 'change'}});
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: OtherDir,
|
||||
factory: () => otherDir = new OtherDir,
|
||||
outputs: {changeStream: 'change'}
|
||||
});
|
||||
}
|
||||
|
||||
it('should call component output function when event is emitted', () => {
|
||||
@ -45,7 +49,7 @@ describe('outputs', () => {
|
||||
e();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
ButtonToggle.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
@ -71,7 +75,7 @@ describe('outputs', () => {
|
||||
e();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
ButtonToggle.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
@ -95,7 +99,7 @@ describe('outputs', () => {
|
||||
e();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
ButtonToggle.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
const ctx = {counter: 0};
|
||||
@ -129,7 +133,7 @@ describe('outputs', () => {
|
||||
e();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
ButtonToggle.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
v();
|
||||
}
|
||||
}
|
||||
@ -179,7 +183,7 @@ describe('outputs', () => {
|
||||
e();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
ButtonToggle.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
v();
|
||||
}
|
||||
}
|
||||
@ -212,13 +216,10 @@ describe('outputs', () => {
|
||||
ngOnDestroy() { this.events.push('destroy'); }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: DestroyComp,
|
||||
tag: 'destroy-comp',
|
||||
template: function(ctx: any, cm: boolean) {},
|
||||
factory: () => {
|
||||
destroyComp = new DestroyComp();
|
||||
l(LifecycleHook.ON_DESTROY, destroyComp, destroyComp.ngOnDestroy);
|
||||
return destroyComp;
|
||||
}
|
||||
factory: () => destroyComp = new DestroyComp()
|
||||
});
|
||||
}
|
||||
|
||||
@ -251,8 +252,8 @@ describe('outputs', () => {
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(3, 2);
|
||||
DestroyComp.ngComponentDef.h(5, 4);
|
||||
ButtonToggle.ngComponentDef.r(3, 2);
|
||||
DestroyComp.ngComponentDef.r(5, 4);
|
||||
r(3, 2);
|
||||
r(5, 4);
|
||||
v();
|
||||
}
|
||||
}
|
||||
@ -291,8 +292,8 @@ describe('outputs', () => {
|
||||
class MyButton {
|
||||
click = new EventEmitter();
|
||||
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => buttonDir = new MyButton, outputs: {click: 'click'}});
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{type: MyButton, factory: () => buttonDir = new MyButton, outputs: {click: 'click'}});
|
||||
}
|
||||
|
||||
function Template(ctx: any, cm: boolean) {
|
||||
@ -325,7 +326,7 @@ describe('outputs', () => {
|
||||
e();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
ButtonToggle.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
@ -344,8 +345,8 @@ describe('outputs', () => {
|
||||
class OtherDir {
|
||||
change: boolean;
|
||||
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => otherDir = new OtherDir, inputs: {change: 'change'}});
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{type: OtherDir, factory: () => otherDir = new OtherDir, inputs: {change: 'change'}});
|
||||
}
|
||||
|
||||
/** <button-toggle (change)="onChange()" otherDir [change]="change"></button-toggle> */
|
||||
@ -357,7 +358,7 @@ describe('outputs', () => {
|
||||
}
|
||||
p(0, 'change', b(ctx.change));
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
ButtonToggle.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
@ -400,7 +401,7 @@ describe('outputs', () => {
|
||||
e();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
ButtonToggle.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
v();
|
||||
} else {
|
||||
if (V(1)) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {EventEmitter} from '@angular/core';
|
||||
|
||||
import {C, E, L, T, V, b, b1, cR, cr, defineComponent, defineDirective, e, m, p, t, v} from '../../src/render3/index';
|
||||
import {C, E, L, T, V, b, b1, cR, cr, defineComponent, defineDirective, e, m, p, r, t, v} from '../../src/render3/index';
|
||||
import {NO_CHANGE} from '../../src/render3/instructions';
|
||||
|
||||
import {renderToHtml} from './render_util';
|
||||
@ -69,8 +69,8 @@ describe('elementProperty', () => {
|
||||
class MyButton {
|
||||
disabled: boolean;
|
||||
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => button = new MyButton(), inputs: {disabled: 'disabled'}});
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{type: MyButton, factory: () => button = new MyButton(), inputs: {disabled: 'disabled'}});
|
||||
}
|
||||
|
||||
class OtherDir {
|
||||
@ -78,6 +78,7 @@ describe('elementProperty', () => {
|
||||
clickStream = new EventEmitter();
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: OtherDir,
|
||||
factory: () => otherDir = new OtherDir(),
|
||||
inputs: {id: 'id'},
|
||||
outputs: {clickStream: 'click'}
|
||||
@ -140,6 +141,7 @@ describe('elementProperty', () => {
|
||||
id: number;
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Comp,
|
||||
tag: 'comp',
|
||||
template: function(ctx: any, cm: boolean) {},
|
||||
factory: () => comp = new Comp(),
|
||||
@ -155,7 +157,7 @@ describe('elementProperty', () => {
|
||||
}
|
||||
p(0, 'id', b(ctx.id));
|
||||
Comp.ngComponentDef.h(1, 0);
|
||||
Comp.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
}
|
||||
|
||||
expect(renderToHtml(Template, {id: 1})).toEqual(`<comp></comp>`);
|
||||
@ -172,6 +174,7 @@ describe('elementProperty', () => {
|
||||
disabled: boolean;
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: OtherDisabledDir,
|
||||
factory: () => otherDisabledDir = new OtherDisabledDir(),
|
||||
inputs: {disabled: 'disabled'}
|
||||
});
|
||||
@ -231,8 +234,8 @@ describe('elementProperty', () => {
|
||||
class IdDir {
|
||||
idNumber: number;
|
||||
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => idDir = new IdDir(), inputs: {idNumber: 'id'}});
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{type: IdDir, factory: () => idDir = new IdDir(), inputs: {idNumber: 'id'}});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -294,6 +297,7 @@ describe('elementProperty', () => {
|
||||
changeStream = new EventEmitter();
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: MyDir,
|
||||
factory: () => myDir = new MyDir(),
|
||||
inputs: {role: 'role', direction: 'dir'},
|
||||
outputs: {changeStream: 'change'}
|
||||
@ -304,8 +308,8 @@ describe('elementProperty', () => {
|
||||
class MyDirB {
|
||||
roleB: string;
|
||||
|
||||
static ngDirectiveDef =
|
||||
defineDirective({factory: () => dirB = new MyDirB(), inputs: {roleB: 'role'}});
|
||||
static ngDirectiveDef = defineDirective(
|
||||
{type: MyDirB, factory: () => dirB = new MyDirB(), inputs: {roleB: 'role'}});
|
||||
}
|
||||
|
||||
it('should set input property based on attribute if existing', () => {
|
||||
@ -467,6 +471,7 @@ describe('elementProperty', () => {
|
||||
|
||||
class Comp {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Comp,
|
||||
tag: 'comp',
|
||||
template: function(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -497,7 +502,7 @@ describe('elementProperty', () => {
|
||||
e();
|
||||
}
|
||||
Comp.ngComponentDef.h(1, 0);
|
||||
Comp.ngComponentDef.r(1, 0);
|
||||
r(1, 0);
|
||||
v();
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF} from '../../src/render3/di';
|
||||
import {C, E, Q, QueryList, e, m, qR} from '../../src/render3/index';
|
||||
import {QueryReadType} from '../../src/render3/interfaces/query';
|
||||
import {C, E, Q, QueryList, V, cR, cr, detectChanges, e, m, qR, v} from '../../src/render3/index';
|
||||
|
||||
import {createComponent, createDirective, renderComponent} from './render_util';
|
||||
|
||||
@ -511,7 +510,6 @@ describe('query', () => {
|
||||
it('should not add results to query if a requested token cant be read', () => {
|
||||
const Child = createDirective();
|
||||
|
||||
let childInstance, div;
|
||||
/**
|
||||
* <div #foo></div>
|
||||
* class Cmpt {
|
||||
@ -522,7 +520,7 @@ describe('query', () => {
|
||||
let tmp: any;
|
||||
if (cm) {
|
||||
m(0, Q(['foo'], false, Child));
|
||||
div = E(1, 'div', null, null, ['foo', '']);
|
||||
E(1, 'div', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
@ -534,4 +532,299 @@ describe('query', () => {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('view boundaries', () => {
|
||||
|
||||
it('should report results in embedded views', () => {
|
||||
let firstEl;
|
||||
/**
|
||||
* <ng-template [ngIf]="exp">
|
||||
* <div #foo></div>
|
||||
* </ng-template>
|
||||
* class Cmpt {
|
||||
* @ViewChildren('foo') query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||
let tmp: any;
|
||||
if (cm) {
|
||||
m(0, Q(['foo'], true, QUERY_READ_FROM_NODE));
|
||||
C(1);
|
||||
}
|
||||
cR(1);
|
||||
{
|
||||
if (ctx.exp) {
|
||||
let cm1 = V(1);
|
||||
{
|
||||
if (cm1) {
|
||||
firstEl = E(0, 'div', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
}
|
||||
v();
|
||||
}
|
||||
}
|
||||
cr();
|
||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
});
|
||||
|
||||
const cmptInstance = renderComponent(Cmpt);
|
||||
const query = (cmptInstance.query as any);
|
||||
expect(query.length).toBe(0);
|
||||
|
||||
cmptInstance.exp = true;
|
||||
detectChanges(cmptInstance);
|
||||
expect(query.length).toBe(1);
|
||||
expect(query.first.nativeElement).toBe(firstEl);
|
||||
|
||||
cmptInstance.exp = false;
|
||||
detectChanges(cmptInstance);
|
||||
expect(query.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should add results from embedded views in the correct order - views and elements mix',
|
||||
() => {
|
||||
let firstEl, lastEl, viewEl;
|
||||
/**
|
||||
* <span #foo></span>
|
||||
* <ng-template [ngIf]="exp">
|
||||
* <div #foo></div>
|
||||
* </ng-template>
|
||||
* <span #foo></span>
|
||||
* class Cmpt {
|
||||
* @ViewChildren('foo') query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||
let tmp: any;
|
||||
if (cm) {
|
||||
m(0, Q(['foo'], true, QUERY_READ_FROM_NODE));
|
||||
firstEl = E(1, 'b', null, null, ['foo', '']);
|
||||
e();
|
||||
C(2);
|
||||
lastEl = E(3, 'i', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
cR(2);
|
||||
{
|
||||
if (ctx.exp) {
|
||||
let cm1 = V(1);
|
||||
{
|
||||
if (cm1) {
|
||||
viewEl = E(0, 'div', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
}
|
||||
v();
|
||||
}
|
||||
}
|
||||
cr();
|
||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
});
|
||||
|
||||
const cmptInstance = renderComponent(Cmpt);
|
||||
const query = (cmptInstance.query as any);
|
||||
expect(query.length).toBe(2);
|
||||
expect(query.first.nativeElement).toBe(firstEl);
|
||||
expect(query.last.nativeElement).toBe(lastEl);
|
||||
|
||||
cmptInstance.exp = true;
|
||||
detectChanges(cmptInstance);
|
||||
expect(query.length).toBe(3);
|
||||
expect(query.toArray()[0].nativeElement).toBe(firstEl);
|
||||
expect(query.toArray()[1].nativeElement).toBe(viewEl);
|
||||
expect(query.toArray()[2].nativeElement).toBe(lastEl);
|
||||
|
||||
cmptInstance.exp = false;
|
||||
detectChanges(cmptInstance);
|
||||
expect(query.length).toBe(2);
|
||||
expect(query.first.nativeElement).toBe(firstEl);
|
||||
expect(query.last.nativeElement).toBe(lastEl);
|
||||
});
|
||||
|
||||
it('should add results from embedded views in the correct order - views side by side', () => {
|
||||
let firstEl, lastEl;
|
||||
/**
|
||||
* <ng-template [ngIf]="exp1">
|
||||
* <div #foo></div>
|
||||
* </ng-template>
|
||||
* <ng-template [ngIf]="exp2">
|
||||
* <span #foo></span>
|
||||
* </ng-template>
|
||||
* class Cmpt {
|
||||
* @ViewChildren('foo') query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||
let tmp: any;
|
||||
if (cm) {
|
||||
m(0, Q(['foo'], true, QUERY_READ_FROM_NODE));
|
||||
C(1);
|
||||
}
|
||||
cR(1);
|
||||
{
|
||||
if (ctx.exp1) {
|
||||
let cm1 = V(0);
|
||||
{
|
||||
if (cm1) {
|
||||
firstEl = E(0, 'div', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
}
|
||||
v();
|
||||
}
|
||||
if (ctx.exp2) {
|
||||
let cm1 = V(1);
|
||||
{
|
||||
if (cm1) {
|
||||
lastEl = E(0, 'span', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
}
|
||||
v();
|
||||
}
|
||||
}
|
||||
cr();
|
||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
});
|
||||
|
||||
const cmptInstance = renderComponent(Cmpt);
|
||||
const query = (cmptInstance.query as any);
|
||||
expect(query.length).toBe(0);
|
||||
|
||||
cmptInstance.exp2 = true;
|
||||
detectChanges(cmptInstance);
|
||||
expect(query.length).toBe(1);
|
||||
expect(query.last.nativeElement).toBe(lastEl);
|
||||
|
||||
cmptInstance.exp1 = true;
|
||||
detectChanges(cmptInstance);
|
||||
expect(query.length).toBe(2);
|
||||
expect(query.first.nativeElement).toBe(firstEl);
|
||||
expect(query.last.nativeElement).toBe(lastEl);
|
||||
});
|
||||
|
||||
it('should add results from embedded views in the correct order - nested views', () => {
|
||||
let firstEl, lastEl;
|
||||
/**
|
||||
* <ng-template [ngIf]="exp1">
|
||||
* <div #foo></div>
|
||||
* <ng-template [ngIf]="exp2">
|
||||
* <span #foo></span>
|
||||
* </ng-template>
|
||||
* </ng-template>
|
||||
* class Cmpt {
|
||||
* @ViewChildren('foo') query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||
let tmp: any;
|
||||
if (cm) {
|
||||
m(0, Q(['foo'], true, QUERY_READ_FROM_NODE));
|
||||
C(1);
|
||||
}
|
||||
cR(1);
|
||||
{
|
||||
if (ctx.exp1) {
|
||||
let cm1 = V(0);
|
||||
{
|
||||
if (cm1) {
|
||||
firstEl = E(0, 'div', null, null, ['foo', '']);
|
||||
e();
|
||||
C(1);
|
||||
}
|
||||
cR(1);
|
||||
{
|
||||
if (ctx.exp2) {
|
||||
let cm2 = V(0);
|
||||
{
|
||||
if (cm2) {
|
||||
lastEl = E(0, 'span', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
}
|
||||
v();
|
||||
}
|
||||
}
|
||||
cr();
|
||||
}
|
||||
v();
|
||||
}
|
||||
}
|
||||
cr();
|
||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
});
|
||||
|
||||
const cmptInstance = renderComponent(Cmpt);
|
||||
const query = (cmptInstance.query as any);
|
||||
expect(query.length).toBe(0);
|
||||
|
||||
cmptInstance.exp1 = true;
|
||||
detectChanges(cmptInstance);
|
||||
expect(query.length).toBe(1);
|
||||
expect(query.first.nativeElement).toBe(firstEl);
|
||||
|
||||
cmptInstance.exp2 = true;
|
||||
detectChanges(cmptInstance);
|
||||
expect(query.length).toBe(2);
|
||||
expect(query.first.nativeElement).toBe(firstEl);
|
||||
expect(query.last.nativeElement).toBe(lastEl);
|
||||
});
|
||||
|
||||
it('should support combination of deep and shallow queries', () => {
|
||||
/**
|
||||
* <ng-template [ngIf]="exp">
|
||||
* <div #foo></div>
|
||||
* </ng-template>
|
||||
* <span #foo></span>
|
||||
* class Cmpt {
|
||||
* @ViewChildren('foo') query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||
let tmp: any;
|
||||
if (cm) {
|
||||
m(0, Q(['foo'], true));
|
||||
m(1, Q(['foo'], false));
|
||||
C(2);
|
||||
E(3, 'span', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
cR(2);
|
||||
{
|
||||
if (ctx.exp) {
|
||||
let cm1 = V(0);
|
||||
{
|
||||
if (cm1) {
|
||||
E(0, 'div', null, null, ['foo', '']);
|
||||
e();
|
||||
}
|
||||
}
|
||||
v();
|
||||
}
|
||||
}
|
||||
cr();
|
||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.deep = tmp as QueryList<any>);
|
||||
qR(tmp = m<QueryList<any>>(1)) && (ctx.shallow = tmp as QueryList<any>);
|
||||
});
|
||||
|
||||
const cmptInstance = renderComponent(Cmpt);
|
||||
const deep = (cmptInstance.deep as any);
|
||||
const shallow = (cmptInstance.shallow as any);
|
||||
expect(deep.length).toBe(1);
|
||||
expect(shallow.length).toBe(1);
|
||||
|
||||
|
||||
cmptInstance.exp = true;
|
||||
detectChanges(cmptInstance);
|
||||
expect(deep.length).toBe(2);
|
||||
expect(shallow.length).toBe(1);
|
||||
|
||||
cmptInstance.exp = false;
|
||||
detectChanges(cmptInstance);
|
||||
expect(deep.length).toBe(1);
|
||||
expect(shallow.length).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -82,14 +82,20 @@ export function createComponent(
|
||||
name: string, template: ComponentTemplate<any>): ComponentType<any> {
|
||||
return class Component {
|
||||
value: any;
|
||||
static ngComponentDef = defineComponent(
|
||||
{tag: name, factory: () => new Component, template: template, features: [PublicFeature]});
|
||||
static ngComponentDef = defineComponent({
|
||||
type: Component,
|
||||
tag: name,
|
||||
factory: () => new Component,
|
||||
template: template,
|
||||
features: [PublicFeature]
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function createDirective({exportAs}: {exportAs?: string} = {}): DirectiveType<any> {
|
||||
return class Directive {
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: Directive,
|
||||
factory: () => new Directive(),
|
||||
features: [PublicFeature],
|
||||
exportAs: exportAs,
|
||||
|
@ -10,7 +10,7 @@ import {AnimationEvent} from '@angular/animations';
|
||||
import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing';
|
||||
|
||||
import {RendererType2, ViewEncapsulation} from '../../src/core';
|
||||
import {E, L, T, b, defineComponent, detectChanges, e, p} from '../../src/render3/index';
|
||||
import {E, L, T, b, defineComponent, detectChanges, e, p, r} from '../../src/render3/index';
|
||||
import {createRendererType2} from '../../src/view/index';
|
||||
|
||||
import {getAnimationRendererFactory2, getRendererFactory2} from './imported_renderer2';
|
||||
@ -29,6 +29,7 @@ describe('renderer factory lifecycle', () => {
|
||||
|
||||
class SomeComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SomeComponent,
|
||||
tag: 'some-component',
|
||||
template: function(ctx: SomeComponent, cm: boolean) {
|
||||
logs.push('component');
|
||||
@ -42,6 +43,7 @@ describe('renderer factory lifecycle', () => {
|
||||
|
||||
class SomeComponentWhichThrows {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SomeComponentWhichThrows,
|
||||
tag: 'some-component-with-Error',
|
||||
template: function(ctx: SomeComponentWhichThrows, cm: boolean) {
|
||||
throw(new Error('SomeComponentWhichThrows threw'));
|
||||
@ -65,7 +67,7 @@ describe('renderer factory lifecycle', () => {
|
||||
e();
|
||||
}
|
||||
SomeComponent.ngComponentDef.h(2, 1);
|
||||
SomeComponent.ngComponentDef.r(2, 1);
|
||||
r(2, 1);
|
||||
}
|
||||
|
||||
beforeEach(() => { logs = []; });
|
||||
@ -120,6 +122,7 @@ describe('animation renderer factory', () => {
|
||||
|
||||
class SomeComponent {
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SomeComponent,
|
||||
tag: 'some-component',
|
||||
template: function(ctx: SomeComponent, cm: boolean) {
|
||||
if (cm) {
|
||||
@ -136,6 +139,7 @@ describe('animation renderer factory', () => {
|
||||
eventLogs.push(`${event.fromState ? event.fromState : event.toState} - ${event.phaseName}`);
|
||||
}
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SomeComponentWithAnimation,
|
||||
tag: 'some-component',
|
||||
template: function(ctx: SomeComponentWithAnimation, cm: boolean) {
|
||||
if (cm) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {isDifferent} from '../../src/render3/util';
|
||||
import {flatten, isDifferent} from '../../src/render3/util';
|
||||
|
||||
describe('util', () => {
|
||||
|
||||
@ -32,4 +32,20 @@ describe('util', () => {
|
||||
expect(isDifferent(5, NaN)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('flatten', () => {
|
||||
|
||||
it('should flatten an empty array', () => { expect(flatten([])).toEqual([]); });
|
||||
|
||||
it('should flatten a flat array', () => { expect(flatten([1, 2, 3])).toEqual([1, 2, 3]); });
|
||||
|
||||
it('should flatten a nested array', () => {
|
||||
expect(flatten([1, [2], 3])).toEqual([1, 2, 3]);
|
||||
expect(flatten([[1], 2, [3]])).toEqual([1, 2, 3]);
|
||||
expect(flatten([1, [2, [3]], 4])).toEqual([1, 2, 3, 4]);
|
||||
expect(flatten([1, [2, [3]], [4]])).toEqual([1, 2, 3, 4]);
|
||||
expect(flatten([1, [2, [3]], [[[4]]]])).toEqual([1, 2, 3, 4]);
|
||||
expect(flatten([1, [], 2])).toEqual([1, 2]);
|
||||
});
|
||||
});
|
||||
});
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {TemplateRef, ViewContainerRef} from '../../src/core';
|
||||
import {C, T, b, cR, cr, defineComponent, defineDirective, injectTemplateRef, injectViewContainerRef, m, t} from '../../src/render3/index';
|
||||
import {C, T, b, cR, cr, defineComponent, defineDirective, injectTemplateRef, injectViewContainerRef, m, r, t} from '../../src/render3/index';
|
||||
|
||||
import {renderComponent, toHtml} from './render_util';
|
||||
|
||||
@ -16,6 +16,7 @@ describe('ViewContainerRef', () => {
|
||||
constructor(public viewContainer: ViewContainerRef, public template: TemplateRef<any>, ) {}
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: TestDirective,
|
||||
factory: () => new TestDirective(injectViewContainerRef(), injectTemplateRef(), ),
|
||||
});
|
||||
}
|
||||
@ -24,6 +25,7 @@ describe('ViewContainerRef', () => {
|
||||
testDir: TestDirective;
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: TestComponent,
|
||||
tag: 'test-cmp',
|
||||
factory: () => new TestComponent(),
|
||||
template: (cmp: TestComponent, cm: boolean) => {
|
||||
@ -38,7 +40,8 @@ describe('ViewContainerRef', () => {
|
||||
}
|
||||
cR(0);
|
||||
cmp.testDir = m(1) as TestDirective;
|
||||
TestDirective.ngDirectiveDef.r(1, 0);
|
||||
TestDirective.ngDirectiveDef.h(1, 0);
|
||||
r(1, 0);
|
||||
cr();
|
||||
},
|
||||
});
|
||||
|
Reference in New Issue
Block a user