feat(ivy): add canonical example of a pipe. (#21834)

PR Close #21834
This commit is contained in:
Misko Hevery 2018-01-27 13:07:03 -08:00 committed by Alex Rickabaugh
parent f816666ede
commit 743d8bc845
6 changed files with 243 additions and 5 deletions

View File

@ -783,13 +783,36 @@ export interface PipeDecorator {
* @stable
*/
export interface Pipe {
/**
* Name of the pipe.
*
* The pipe name is used in template bindings. For example if a pipe is named
* `myPipe` then it would be used in the template binding expression like
* so: `{{ exp | myPipe }}`.
*/
name: string;
/**
* If Pipe is pure (its output depends only on its input.)
*
* Normally pipe's `translate` method is invoked on each change detection
* cycle. If the cost of invoking the `translated` method is non-trivial and
* if the output of the pipe depends only on its input, then declaring a pipe
* as pure would short circuit the invocation of the `translate` method and
* invoke the method only when the inputs to the pipe change.
*/
pure?: boolean;
}
/**
* Pipe decorator and metadata.
*
* Use the `@Pipe` annotation to declare that a given class is a pipe. A pipe
* class must also implement {@link PipeTransform} interface.
*
* To use the pipe include a reference to the pipe class in
* {@link NgModule#declarations}.
*
* @stable
* @Annotation
*/

View File

@ -7,13 +7,14 @@
*/
import {SimpleChange} from '../change_detection/change_detection_util';
import {PipeTransform} from '../change_detection/pipe_transform';
import {OnChanges, SimpleChanges} from '../metadata/lifecycle_hooks';
import {RendererType2} from '../render/api';
import {Type} from '../type';
import {resolveRendererType2} from '../view/util';
import {diPublic} from './di';
import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs} from './interfaces/definition';
import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs, PipeDef, PipeType} from './interfaces/definition';
@ -26,7 +27,7 @@ import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs} from './
* class MyDirective {
* // Generated by Angular Template Compiler
* // [Symbol] syntax will not be supported by TypeScript until v2.7
* static [COMPONENT_DEF_SYMBOL] = defineComponent({
* static ngComponentDef = defineComponent({
* ...
* });
* }
@ -147,7 +148,7 @@ function invertObject(obj: any): any {
* class MyDirective {
* // Generated by Angular Template Compiler
* // [Symbol] syntax will not be supported by TypeScript until v2.7
* static [DIRECTIVE_DEF_SYMBOL] = defineDirective({
* static ngDirectiveDef = defineDirective({
* ...
* });
* }
@ -155,3 +156,25 @@ function invertObject(obj: any): any {
*/
export const defineDirective = defineComponent as<T>(directiveDefinition: DirectiveDefArgs<T>) =>
DirectiveDef<T>;
/**
* Create a pipe definition object.
*
* # Example
* ```
* class MyPipe implements PipeTransform {
* // Generated by Angular Template Compiler
* static ngPipeDef = definePipe({
* ...
* });
* }
* ```
* @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.
*/
export function definePipe<T>(
{type, factory, pure}: {type: Type<T>, factory: () => PipeTransform, pure?: boolean}):
PipeDef<T> {
throw new Error('TODO: implement!');
}

View File

@ -7,7 +7,7 @@
*/
import {createComponentRef, detectChanges, getHostElement, markDirty, renderComponent} from './component';
import {NgOnChangesFeature, PublicFeature, defineComponent, defineDirective} from './definition';
import {NgOnChangesFeature, PublicFeature, defineComponent, defineDirective, definePipe} from './definition';
import {InjectFlags} from './di';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveType} from './interfaces/definition';
@ -63,6 +63,15 @@ export {
viewEnd as v,
} from './instructions';
export {
pipe as Pp,
pipeBind1 as pb1,
pipeBind2 as pb2,
pipeBind3 as pb3,
pipeBind4 as pb4,
pipeBindV as pbV,
} from './pipe';
export {
QueryList,
@ -83,6 +92,7 @@ export {
PublicFeature,
defineComponent,
defineDirective,
definePipe,
};
export {createComponentRef, detectChanges, getHostElement, markDirty, renderComponent};
export {CssSelector} from './interfaces/projection';

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {PipeTransform} from '../../change_detection/pipe_transform';
import {RendererType2} from '../../render/api';
import {Type} from '../../type';
import {resolveRendererType2} from '../../view/util';
@ -23,6 +24,8 @@ export interface DirectiveType<T> extends Type<T> { ngDirectiveDef: DirectiveDef
export const enum DirectiveDefFlags {ContentQuery = 0b10}
export interface PipeType<T> extends Type<T> { ngPipeDef: PipeDef<T>; }
/**
* `DirectiveDef` is a compiled version of the Directive used by the renderer instructions.
*/
@ -109,6 +112,31 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
readonly rendererType: RendererType2|null;
}
/**
*
*/
export interface PipeDef<T> {
/**
* factory function used to create a new directive instance.
*
* NOTE: this property is short (1 char) because it is used in
* component templates which is sensitive to size.
*/
n: () => PipeTransform;
/**
* Whether or not the pipe is pure.
*
* Pure pipes result only depends on the pipe input and not on internal
* state of the pipe.
*/
pure: boolean;
/* The following are lifecycle hooks for this pipe */
onDestroy: (() => void)|null;
}
export interface DirectiveDefArgs<T> {
type: Type<T>;
factory: () => T | [T];

View File

@ -0,0 +1,91 @@
/**
* @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 {PipeDef} from './interfaces/definition';
/**
* Create a pipe.
*
* @param index Pipe index where the pipe will be stored.
* @param pipeDef Pipe definition object for registering life cycle hooks.
* @param pipe A Pipe instance.
*/
export function pipe<T>(index: number, pipeDef: PipeDef<T>, pipe: T): void {
throw new Error('TODO: implement!');
}
/**
* Invokes a pure pipe with 4 arguments.
*
* This instruction acts as a guard to {@link PipeTransform#transform} invoking
* the pipe only when an input to the pipe changes.
*
* @param index Pipe index where the pipe was stored on creation.
* @param v1 1st argument to {@link PipeTransform#transform}.
*/
export function pipeBind1(index: number, v1: any): any {
throw new Error('TODO: implement!');
}
/**
* Invokes a pure pipe with 4 arguments.
*
* This instruction acts as a guard to {@link PipeTransform#transform} invoking
* the pipe only when an input to the pipe changes.
*
* @param index Pipe index where the pipe was stored on creation.
* @param v1 1st argument to {@link PipeTransform#transform}.
* @param v2 2nd argument to {@link PipeTransform#transform}.
*/
export function pipeBind2(index: number, v1: any, v2: any): any {
throw new Error('TODO: implement!');
}
/**
* Invokes a pure pipe with 4 arguments.
*
* This instruction acts as a guard to {@link PipeTransform#transform} invoking
* the pipe only when an input to the pipe changes.
*
* @param index Pipe index where the pipe was stored on creation.
* @param v1 1st argument to {@link PipeTransform#transform}.
* @param v2 2nd argument to {@link PipeTransform#transform}.
* @param v3 4rd argument to {@link PipeTransform#transform}.
*/
export function pipeBind3(index: number, v1: any, v2: any, v3: any): any {
throw new Error('TODO: implement!');
}
/**
* Invokes a pure pipe with 4 arguments.
*
* This instruction acts as a guard to {@link PipeTransform#transform} invoking
* the pipe only when an input to the pipe changes.
*
* @param index Pipe index where the pipe was stored on creation.
* @param v1 1st argument to {@link PipeTransform#transform}.
* @param v2 2nd argument to {@link PipeTransform#transform}.
* @param v3 3rd argument to {@link PipeTransform#transform}.
* @param v4 4th argument to {@link PipeTransform#transform}.
*/
export function pipeBind4(index: number, v1: any, v2: any, v3: any, v4: any): any {
throw new Error('TODO: implement!');
}
/**
* Invokes a pure pipe with variable number of arguments.
*
* This instruction acts as a guard to {@link PipeTransform#transform} invoking
* the pipe only when an input to the pipe changes.
*
* @param index Pipe index where the pipe was stored on creation.
* @param values Array of arguments to pass to {@link PipeTransform#transform} method.
*/
export function pipeBindV(index: number, values: any[]): any {
throw new Error('TODO: implement!');
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, ContentChild, Directive, Injectable, Input, NgModule, Optional, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef} from '../../src/core';
import {Component, ContentChild, Directive, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef} from '../../src/core';
import * as r3 from '../../src/render3/index';
import {containerEl, renderComponent, requestAnimationFrame, toHtml} from './render_util';
@ -369,6 +369,69 @@ describe('compiler specification', () => {
});
xdescribe('pipes', () => {
@Pipe({
name: 'myPipe',
})
class MyPipe implements PipeTransform,
OnDestroy {
transform(value: any, ...args: any[]) { throw new Error('Method not implemented.'); }
ngOnDestroy(): void { throw new Error('Method not implemented.'); }
// NORMATIVE
static ngPipeDef = r3.definePipe(
{type: MyPipe, factory: function MyPipe_Factory() { return new MyPipe(); }});
// /NORMATIVE
}
@Pipe({
name: 'myPurePipe',
pure: true,
})
class MyPurePipe implements PipeTransform {
transform(value: any, ...args: any[]) { throw new Error('Method not implemented.'); }
// NORMATIVE
static ngPipeDef = r3.definePipe({
type: MyPurePipe,
factory: function MyPurePipe_Factory() { return new MyPurePipe(); },
pure: true
});
// /NORMATIVE
}
@Component({template: `{{name | myPipe:size | myPurePipe:size }}`})
class MyApp {
name = 'World';
size = 0;
// NORMATIVE
static ngComponentDef = r3.defineComponent({
type: MyApp,
tag: 'my-app',
factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: MyApp, cm: boolean) {
if (cm) {
r3.Pp(0, MyPipe_ngPipeDef, MyPipe_ngPipeDef.n());
r3.Pp(1, MyPurePipe_ngPipeDef, MyPurePipe_ngPipeDef.n());
r3.T(2);
}
r3.t(
2, r3.b1('', r3.pb2(1, r3.m<MyPipe>(0).transform(ctx.name, ctx.size), ctx.size), ''));
}
});
// /NORMATIVE
}
// NORMATIVE
const MyPipe_ngPipeDef = MyPipe.ngPipeDef;
const MyPurePipe_ngPipeDef = MyPurePipe.ngPipeDef;
// /NORMATIVE
it('should render pipes', () => {
// TODO(misko): write a test once pipes runtime is implemented.
});
});
describe('local references', () => {
// TODO(misko): currently disabled until local refs are working
xit('should translate DOM structure', () => {