feat(pipe): added the Pipe decorator and the pipe property to View
BREAKING CHANGE:
Instead of configuring pipes via a Pipes object, now you can configure them by providing the pipes property to the View decorator.
@Pipe({
name: 'double'
})
class DoublePipe {
transform(value, args) { return value * 2; }
}
@View({
template: '{{ 10 | double}}'
pipes: [DoublePipe]
})
class CustomComponent {}
Closes #3572
This commit is contained in:
@@ -22,7 +22,8 @@ import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||
import {AppProtoView} from 'angular2/src/core/compiler/view';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {Attribute, View, Component, Directive} from 'angular2/annotations';
|
||||
import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';
|
||||
import {Attribute, View, Component, Directive, Pipe} from 'angular2/annotations';
|
||||
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||
import {internalProtoView} from 'angular2/src/core/compiler/view_ref';
|
||||
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
||||
@@ -38,11 +39,14 @@ import {AppRootUrl} from 'angular2/src/services/app_root_url';
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
// TODO(tbosch): Spys don't support named modules...
|
||||
import {RenderCompiler} from 'angular2/src/render/api';
|
||||
import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding';
|
||||
|
||||
|
||||
|
||||
export function main() {
|
||||
describe('compiler', function() {
|
||||
var directiveResolver, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper,
|
||||
rootProtoView;
|
||||
var directiveResolver, pipeResolver, tplResolver, renderCompiler, protoViewFactory,
|
||||
cmpUrlMapper, rootProtoView;
|
||||
var renderCompileRequests: any[];
|
||||
|
||||
function createCompiler(renderCompileResults:
|
||||
@@ -57,13 +61,14 @@ export function main() {
|
||||
});
|
||||
|
||||
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults);
|
||||
return new Compiler(directiveResolver, new CompilerCache(), tplResolver, cmpUrlMapper,
|
||||
urlResolver, renderCompiler, protoViewFactory,
|
||||
return new Compiler(directiveResolver, pipeResolver, [SomeDefaultPipe], new CompilerCache(),
|
||||
tplResolver, cmpUrlMapper, urlResolver, renderCompiler, protoViewFactory,
|
||||
new AppRootUrl("http://www.app.com"));
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
directiveResolver = new DirectiveResolver();
|
||||
pipeResolver = new PipeResolver();
|
||||
tplResolver = new FakeViewResolver();
|
||||
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
||||
renderCompiler = new SpyRenderCompiler();
|
||||
@@ -304,6 +309,20 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should pass the pipe bindings', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent,
|
||||
new viewAnn.View({template: '<div></div>', pipes: [SomePipe]}));
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
var request = protoViewFactory.requests[1];
|
||||
expect(request[3][0].key.token).toBe(SomeDefaultPipe);
|
||||
expect(request[3][1].key.token).toBe(SomePipe);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should use the protoView of the ProtoViewFactory',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
@@ -399,9 +418,9 @@ export function main() {
|
||||
var reader: any = new SpyDirectiveResolver();
|
||||
|
||||
// create the compiler
|
||||
var compiler =
|
||||
new Compiler(reader, cache, tplResolver, cmpUrlMapper, new UrlResolver(),
|
||||
renderCompiler, protoViewFactory, new AppRootUrl("http://www.app.com"));
|
||||
var compiler = new Compiler(reader, pipeResolver, [], cache, tplResolver, cmpUrlMapper,
|
||||
new UrlResolver(), renderCompiler, protoViewFactory,
|
||||
new AppRootUrl("http://www.app.com"));
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
// the test should have failed if the resolver was called, so we're good
|
||||
@@ -570,7 +589,7 @@ function createProtoView(elementBinders = null, type: renderApi.ViewType = null,
|
||||
type = renderApi.ViewType.COMPONENT;
|
||||
}
|
||||
var pv = new AppProtoView(type, isEmbeddedFragment, new renderApi.RenderProtoViewRef(), null,
|
||||
null, new Map(), null);
|
||||
null, new Map(), null, null);
|
||||
if (isBlank(elementBinders)) {
|
||||
elementBinders = [];
|
||||
}
|
||||
@@ -653,6 +672,14 @@ class DirectiveWithProperties {
|
||||
class DirectiveWithBind {
|
||||
}
|
||||
|
||||
@Pipe({name: 'some-default-pipe'})
|
||||
class SomeDefaultPipe {
|
||||
}
|
||||
|
||||
@Pipe({name: 'some-pipe'})
|
||||
class SomePipe {
|
||||
}
|
||||
|
||||
@Directive({selector: 'directive-with-accts'})
|
||||
class DirectiveWithAttributes {
|
||||
constructor(@Attribute('someAttr') someAttr: String) {}
|
||||
@@ -694,8 +721,8 @@ class FakeProtoViewFactory extends ProtoViewFactory {
|
||||
}
|
||||
|
||||
createAppProtoViews(componentBinding: DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
|
||||
directives: List<DirectiveBinding>): AppProtoView[] {
|
||||
this.requests.push([componentBinding, renderProtoView, directives]);
|
||||
directives: List<DirectiveBinding>, pipes: PipeBinding[]): AppProtoView[] {
|
||||
this.requests.push([componentBinding, renderProtoView, directives, pipes]);
|
||||
return collectEmbeddedPvs(ListWrapper.removeAt(this.results, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,14 +192,17 @@ class OptionallyInjectsTemplateRef {
|
||||
|
||||
@Injectable()
|
||||
class DirectiveNeedsChangeDetectorRef {
|
||||
changeDetectorRef;
|
||||
constructor(cdr: ChangeDetectorRef) { this.changeDetectorRef = cdr; }
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class ComponentNeedsChangeDetectorRef {
|
||||
changeDetectorRef;
|
||||
constructor(cdr: ChangeDetectorRef) { this.changeDetectorRef = cdr; }
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class PipeNeedsChangeDetectorRef {
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
class A_Needs_B {
|
||||
|
||||
@@ -55,15 +55,14 @@ import {
|
||||
SkipSelf,
|
||||
SkipSelfMetadata
|
||||
} from 'angular2/di';
|
||||
|
||||
import {
|
||||
Pipes,
|
||||
defaultPipes,
|
||||
Pipe,
|
||||
PipeTransform,
|
||||
ChangeDetectorRef,
|
||||
ON_PUSH
|
||||
} from 'angular2/src/change_detection/change_detection';
|
||||
|
||||
import {Directive, Component, View, Attribute, Query} from 'angular2/annotations';
|
||||
import {Directive, Component, View, Attribute, Query, Pipe} from 'angular2/annotations';
|
||||
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||
|
||||
import {QueryList} from 'angular2/src/core/compiler/query_list';
|
||||
@@ -98,6 +97,7 @@ export function main() {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('Hello World!');
|
||||
async.done();
|
||||
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -242,12 +242,13 @@ export function main() {
|
||||
it("should support pipes in bindings",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyCompWithPipes, new viewAnn.View({
|
||||
tcb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>',
|
||||
directives: [MyDir]
|
||||
directives: [MyDir],
|
||||
pipes: [DoublePipe]
|
||||
}))
|
||||
|
||||
.createAsync(MyCompWithPipes)
|
||||
.createAsync(MyComp)
|
||||
.then((rootTC) => {
|
||||
rootTC.componentInstance.ctxProp = 'a';
|
||||
rootTC.detectChanges();
|
||||
@@ -1661,21 +1662,6 @@ class PushCmpWithAsyncPipe {
|
||||
resolve(value) { this.completer.resolve(value); }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class PipesWithDouble extends Pipes {
|
||||
constructor(injector: Injector) { super({"double": DoublePipe}, injector); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp-with-pipes',
|
||||
viewBindings: [new Binding(Pipes, {toClass: PipesWithDouble})]
|
||||
})
|
||||
@View({directives: []})
|
||||
@Injectable()
|
||||
class MyCompWithPipes {
|
||||
ctxProp: string = "initial value";
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp'})
|
||||
@View({directives: []})
|
||||
@Injectable()
|
||||
@@ -1754,8 +1740,8 @@ class SomeViewport {
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class DoublePipe implements Pipe {
|
||||
@Pipe({name: 'double'})
|
||||
class DoublePipe implements PipeTransform {
|
||||
onDestroy() {}
|
||||
transform(value, args = null) { return `${value}${value}`; }
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export function main() {
|
||||
varBindings.set('a', 'b');
|
||||
var renderPv = createRenderProtoView([], null, varBindings);
|
||||
var appPvs =
|
||||
protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, []);
|
||||
protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, [], []);
|
||||
expect(appPvs[0].variableBindings.get('a')).toEqual('b');
|
||||
expect(appPvs.length).toBe(1);
|
||||
});
|
||||
|
||||
@@ -318,7 +318,7 @@ function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
|
||||
}
|
||||
var protoChangeDetector = <any>new SpyProtoChangeDetector();
|
||||
protoChangeDetector.spy('instantiate').andReturn(new SpyChangeDetector());
|
||||
var res = new AppProtoView(type, null, null, protoChangeDetector, null, null, 0);
|
||||
var res = new AppProtoView(type, null, null, protoChangeDetector, null, null, 0, null);
|
||||
res.elementBinders = binders;
|
||||
var mappedElementIndices = ListWrapper.createFixedSize(countNestedElementBinders(res));
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
|
||||
@@ -25,7 +25,7 @@ export function main() {
|
||||
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
|
||||
|
||||
function createProtoView() {
|
||||
return new AppProtoView(null, null, null, null, null, null, null);
|
||||
return new AppProtoView(null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
function createView(pv) {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding';
|
||||
import {Pipe} from 'angular2/src/core/annotations_impl/annotations';
|
||||
|
||||
class MyPipe {}
|
||||
|
||||
export function main() {
|
||||
describe("PipeBinding", () => {
|
||||
it('should create a binding out of a type', () => {
|
||||
var binding = PipeBinding.createFromType(MyPipe, new Pipe({name: 'my-pipe'}));
|
||||
expect(binding.name).toEqual('my-pipe');
|
||||
expect(binding.factory()).toBeAnInstanceOf(MyPipe);
|
||||
expect(binding.dependencies.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {PipeTransform} from 'angular2/change_detection';
|
||||
import {Injector, Inject, bind} from 'angular2/di';
|
||||
import {ProtoPipes, Pipes} from 'angular2/src/core/pipes/pipes';
|
||||
import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding';
|
||||
import {Pipe} from 'angular2/src/core/annotations_impl/annotations';
|
||||
|
||||
class PipeA implements PipeTransform {
|
||||
transform(a, b) {}
|
||||
onDestroy() {}
|
||||
}
|
||||
|
||||
class PipeB implements PipeTransform {
|
||||
dep;
|
||||
constructor(@Inject("dep") dep: any) { this.dep = dep; }
|
||||
transform(a, b) {}
|
||||
onDestroy() {}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe("Pipes", () => {
|
||||
var injector;
|
||||
|
||||
beforeEach(
|
||||
() => { injector = Injector.resolveAndCreate([bind('dep').toValue('dependency')]); });
|
||||
|
||||
it('should instantiate a pipe', () => {
|
||||
var proto = new ProtoPipes([PipeBinding.createFromType(PipeA, new Pipe({name: 'a'}))]);
|
||||
var pipes = new Pipes(proto, injector);
|
||||
expect(pipes.get("a")).toBeAnInstanceOf(PipeA);
|
||||
});
|
||||
|
||||
it('should throw when no pipe found', () => {
|
||||
var proto = new ProtoPipes([]);
|
||||
var pipes = new Pipes(proto, injector);
|
||||
expect(() => pipes.get("invalid")).toThrowErrorWith("Cannot find pipe 'invalid'");
|
||||
});
|
||||
|
||||
it('should inject dependencies from the provided injector', () => {
|
||||
var proto = new ProtoPipes([PipeBinding.createFromType(PipeB, new Pipe({name: 'b'}))]);
|
||||
var pipes = new Pipes(proto, injector);
|
||||
expect(pipes.get("b").dep).toEqual("dependency");
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user