Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(ivy): remove pipe references from the template #23032

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/core/src/render3/component.ts
Expand Up @@ -133,8 +133,9 @@ export function renderComponent<T>(
clean: CLEAN_PROMISE,
};
const rootView = createLView(
-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType), createTView(null),
null, rootContext, componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
-1, rendererFactory.createRenderer(hostNode, componentDef.rendererType),
createTView(null, null), null, rootContext,
componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);

const oldView = enterView(rootView, null !);
let elementNode: LElementNode;
Expand Down
37 changes: 28 additions & 9 deletions packages/core/src/render3/definition.ts
Expand Up @@ -16,7 +16,7 @@ import {Type} from '../type';
import {resolveRendererType2} from '../view/util';

import {diPublic} from './di';
import {ComponentDef, ComponentDefFeature, ComponentTemplate, DirectiveDef, DirectiveDefFeature, DirectiveDefListOrFactory, PipeDef} from './interfaces/definition';
import {ComponentDef, ComponentDefFeature, ComponentTemplate, DirectiveDef, DirectiveDefFeature, DirectiveDefListOrFactory, PipeDef, PipeDefListOrFactory} from './interfaces/definition';
import {CssSelector} from './interfaces/projection';


Expand Down Expand Up @@ -154,6 +154,14 @@ export function defineComponent<T>(componentDefinition: {
* `DirectiveDef`s. The function is necessary to be able to support forward declarations.
*/
directiveDefs?: DirectiveDefListOrFactory | null;

/**
* Registry of pipes that may be found in this component's view.
*
* The property is either an array of `PipeDefs`s or a function which returns the array of
* `PipeDefs`s. The function is necessary to be able to support forward declarations.
*/
pipeDefs?: PipeDefListOrFactory | null;
}): ComponentDef<T> {
const type = componentDefinition.type;
const def = <ComponentDef<any>>{
Expand All @@ -176,6 +184,7 @@ export function defineComponent<T>(componentDefinition: {
onDestroy: type.prototype.ngOnDestroy || null,
onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
directiveDefs: componentDefinition.directiveDefs || null,
pipeDefs: componentDefinition.pipeDefs || null,
selector: componentDefinition.selector
};
const feature = componentDefinition.features;
Expand Down Expand Up @@ -380,15 +389,25 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
* });
* }
* ```
* @param type Pipe class reference. Needed to extract pipe lifecycle hooks.
* @param factory A factory for creating a pipe instance.
* @param pure Whether the pipe is pure.
* @param pipeDef Pipe definition generated by the compiler
*/
export function definePipe<T>(
{type, factory, pure}: {type: Type<T>, factory: () => T, pure?: boolean}): PipeDef<T> {
export function definePipe<T>(pipeDef: {
/** Name of the pipe. Used for matching pipes in template to pipe defs. */
name: string,

/** Pipe class reference. Needed to extract pipe lifecycle hooks. */
type: Type<T>,

/** A factory for creating a pipe instance. */
factory: () => T,

/** Whether the pipe is pure. */
pure?: boolean
}): PipeDef<T> {
return <PipeDef<T>>{
n: factory,
pure: pure !== false,
onDestroy: type.prototype.ngOnDestroy || null
name: pipeDef.name,
n: pipeDef.factory,
pure: pipeDef.pure !== false,
onDestroy: pipeDef.type.prototype.ngOnDestroy || null
};
}
39 changes: 25 additions & 14 deletions packages/core/src/render3/instructions.ts
Expand Up @@ -19,7 +19,7 @@ import {LContainerNode, LElementNode, LNode, LNodeType, TNodeFlags, LProjectionN
import {assertNodeType} from './node_assert';
import {appendChild, insertChild, insertView, appendProjectedNode, removeView, canInsertNativeNode} from './node_manipulation';
import {isNodeMatchingSelector, matchingSelectorIndex} from './node_selector_matcher';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, DirectiveType} from './interfaces/definition';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefList, DirectiveDefListOrFactory, DirectiveType, PipeDef, PipeDefListOrFactory} from './interfaces/definition';
import {RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
import {isDifferent, stringify} from './util';
import {executeHooks, queueLifecycleHooks, queueInitHooks, executeInitHooks} from './hooks';
Expand Down Expand Up @@ -400,20 +400,22 @@ function resetApplicationState() {
* @param context to pass into the template.
* @param providedRendererFactory renderer factory to use
* @param host The host element node to use
* @param directiveRegistry Any directive defs that should be used to match nodes to directives
* @param defs Any directive or pipe defs that should be used for matching
*/
export function renderTemplate<T>(
hostNode: RElement, template: ComponentTemplate<T>, context: T,
providedRendererFactory: RendererFactory3, host: LElementNode | null,
directiveRegistry: DirectiveDefListOrFactory | null = null): LElementNode {
directives?: DirectiveDefListOrFactory | null,
pipes?: PipeDefListOrFactory | null): LElementNode {
if (host == null) {
resetApplicationState();
rendererFactory = providedRendererFactory;
const tView = getOrCreateTView(template, directives || null, pipes || null);
host = createLNode(
null, LNodeType.Element, hostNode,
createLView(
-1, providedRendererFactory.createRenderer(null, null),
getOrCreateTView(template, directiveRegistry), null, {}, LViewFlags.CheckAlways));
-1, providedRendererFactory.createRenderer(null, null), tView, null, {},
LViewFlags.CheckAlways));
}
const hostView = host.data !;
ngDevMode && assertNotNull(hostView, 'Host node should have an LView defined in host.data.');
Expand All @@ -432,9 +434,11 @@ export function renderEmbeddedTemplate<T>(
let cm: boolean = false;
if (viewNode == null) {
// TODO: revisit setting currentView when re-writing view containers
const directives = currentView && currentView.tView.directiveRegistry;
const pipes = currentView && currentView.tView.pipeRegistry;

const view = createLView(
-1, renderer, createTView(currentView && currentView.tView.directiveRegistry), template,
context, LViewFlags.CheckAlways);
-1, renderer, createTView(directives, pipes), template, context, LViewFlags.CheckAlways);
viewNode = createLNode(null, LNodeType.View, null, view);
cm = true;
}
Expand Down Expand Up @@ -635,15 +639,20 @@ function findMatchingLocalNames(
* if it doesn't already exist.
*
* @param template The template from which to get static data
* @param directives Directive defs that should be saved on TView
* @param pipes Pipe defs that should be saved on TView
* @returns TView
*/
function getOrCreateTView(
template: ComponentTemplate<any>, defs: DirectiveDefListOrFactory | null): TView {
return template.ngPrivateData || (template.ngPrivateData = createTView(defs) as never);
template: ComponentTemplate<any>, directives: DirectiveDefListOrFactory | null,
pipes: PipeDefListOrFactory | null): TView {
return template.ngPrivateData ||
(template.ngPrivateData = createTView(directives, pipes) as never);
}

/** Creates a TView instance */
export function createTView(defs: DirectiveDefListOrFactory | null): TView {
export function createTView(
defs: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null): TView {
return {
data: [],
directives: null,
Expand All @@ -658,7 +667,8 @@ export function createTView(defs: DirectiveDefListOrFactory | null): TView {
pipeDestroyHooks: null,
hostBindings: null,
components: null,
directiveRegistry: typeof defs === 'function' ? defs() : defs
directiveRegistry: typeof defs === 'function' ? defs() : defs,
pipeRegistry: typeof pipes === 'function' ? pipes() : pipes
};
}

Expand Down Expand Up @@ -720,7 +730,7 @@ export function hostElement(
const node = createLNode(
0, LNodeType.Element, rNode,
createLView(
-1, renderer, getOrCreateTView(def.template, def.directiveDefs), null, null,
-1, renderer, getOrCreateTView(def.template, def.directiveDefs, def.pipeDefs), null, null,
def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways));

if (firstTemplatePass) {
Expand Down Expand Up @@ -1159,7 +1169,7 @@ export function directiveCreate<T>(

function addComponentLogic<T>(
index: number, elementIndex: number, instance: T, def: ComponentDef<any>): void {
const tView = getOrCreateTView(def.template, def.directiveDefs);
const tView = getOrCreateTView(def.template, def.directiveDefs, def.pipeDefs);

// Only component views should be added to the view tree directly. Embedded views are
// accessed through their containers because they may be removed / re-added later.
Expand Down Expand Up @@ -1466,7 +1476,8 @@ function getOrCreateEmbeddedTView(viewIndex: number, parent: LContainerNode): TV
ngDevMode && assertNodeType(parent, LNodeType.Container);
const tContainer = (parent !.tNode as TContainerNode).data;
if (viewIndex >= tContainer.length || tContainer[viewIndex] == null) {
tContainer[viewIndex] = createTView(currentView.tView.directiveRegistry);
const tView = currentView.tView;
tContainer[viewIndex] = createTView(tView.directiveRegistry, tView.pipeRegistry);
}
return tContainer[viewIndex];
}
Expand Down
24 changes: 24 additions & 0 deletions packages/core/src/render3/interfaces/definition.ts
Expand Up @@ -161,6 +161,14 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
* `DirectiveDef`s. The function is necessary to be able to support forward declarations.
*/
directiveDefs: DirectiveDefListOrFactory|null;

/**
* Registry of pipes that may be found in this view.
*
* The property is either an array of `PipeDefs`s or a function which returns the array of
* `PipeDefs`s. The function is necessary to be able to support forward declarations.
*/
pipeDefs: PipeDefListOrFactory|null;
}

/**
Expand All @@ -176,6 +184,13 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
* See: {@link definePipe}
*/
export interface PipeDef<T> {
/**
* Pipe name.
*
* Used to resolve pipe in templates.
*/
name: string;

/**
* factory function used to create a new directive instance.
*
Expand Down Expand Up @@ -208,6 +223,15 @@ export type DirectiveDefListOrFactory = (() => DirectiveDefList) | DirectiveDefL

export type DirectiveDefList = (DirectiveDef<any>| ComponentDef<any>)[];

/**
* Type used for PipeDefs on component definition.
*
* The function is necessary to be able to support forward declarations.
*/
export type PipeDefListOrFactory = (() => PipeDefList) | PipeDefList;

export type PipeDefList = PipeDef<any>[];

// Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types.
export const unusedValueExportToPlacateAjd = 1;
13 changes: 12 additions & 1 deletion packages/core/src/render3/interfaces/view.ts
Expand Up @@ -7,7 +7,7 @@
*/

import {LContainer} from './container';
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDef} from './definition';
import {ComponentTemplate, DirectiveDefList, PipeDef, PipeDefList} from './definition';
import {LElementNode, LViewNode, TNode} from './node';
import {LQueries} from './query';
import {Renderer3} from './renderer';
Expand Down Expand Up @@ -245,6 +245,17 @@ export interface TView {
*/
directiveRegistry: DirectiveDefList|null;

/**
* Full registry of pipes that may be found in this view.
*
* The property is either an array of `PipeDefs`s or a function which returns the array of
* `PipeDefs`s. The function is necessary to be able to support forward declarations.
*
* It's necessary to keep a copy of the full def list on the TView so it's possible
* to render template functions without a host component.
*/
pipeRegistry: PipeDefList|null;

/**
* Array of ngOnInit and ngDoCheck hooks that should be executed for this view in
* creation mode.
Expand Down
31 changes: 28 additions & 3 deletions packages/core/src/render3/pipe.ts
Expand Up @@ -9,10 +9,9 @@
import {PipeTransform} from '../change_detection/pipe_transform';

import {getTView, load, store} from './instructions';
import {PipeDef} from './interfaces/definition';
import {PipeDef, PipeDefList} from './interfaces/definition';
import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunctionV} from './pure_function';


/**
* Create a pipe.
*
Expand All @@ -21,19 +20,45 @@ import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunction
* @param firstInstance (optional) The first instance of the pipe that can be reused for pure pipes.
* @returns T the instance of the pipe.
*/
export function pipe<T>(index: number, pipeDef: PipeDef<T>, firstInstance?: T): T {
export function pipe(index: number, pipeName: string, firstInstance?: any): any {
const tView = getTView();
let pipeDef: PipeDef<any>;

if (tView.firstTemplatePass) {
pipeDef = getPipeDef(pipeName, tView.pipeRegistry);
tView.data[index] = pipeDef;
if (pipeDef.onDestroy) {
(tView.pipeDestroyHooks || (tView.pipeDestroyHooks = [])).push(index, pipeDef.onDestroy);
}
} else {
pipeDef = tView.data[index] as PipeDef<any>;
}

const pipeInstance = pipeDef.pure && firstInstance ? firstInstance : pipeDef.n();
store(index, pipeInstance);
return pipeInstance;
}

/**
* Searches the pipe registry for a pipe with the given name. If one is found,
* returns the pipe. Otherwise, an error is thrown because the pipe cannot be resolved.
*
* @param name Name of pipe to resolve
* @param registry Full list of available pipes
* @returns Matching PipeDef
*/
function getPipeDef(name: string, registry: PipeDefList | null): PipeDef<any> {
if (registry) {
for (let i = 0; i < registry.length; i++) {
const pipeDef = registry[i];
if (name === pipeDef.name) {
return pipeDef;
}
}
}
throw new Error(`Pipe with name '${name}' not found!`);
}

/**
* Invokes a pipe with 1 arguments.
*
Expand Down