feat(view): added support for exportAs, so any directive can be assigned to a variable
This commit is contained in:
@@ -420,11 +420,11 @@ function createProtoView(elementBinders = null) {
|
||||
|
||||
function createComponentElementBinder(directiveResolver, type) {
|
||||
var binding = createDirectiveBinding(directiveResolver, type);
|
||||
return new ElementBinder(0, null, 0, null, binding);
|
||||
return new ElementBinder(0, null, 0, null, null, binding);
|
||||
}
|
||||
|
||||
function createViewportElementBinder(nestedProtoView) {
|
||||
var elBinder = new ElementBinder(0, null, 0, null, null);
|
||||
var elBinder = new ElementBinder(0, null, 0, null, null, null);
|
||||
elBinder.nestedProtoView = nestedProtoView;
|
||||
return elBinder;
|
||||
}
|
||||
|
||||
@@ -422,110 +422,127 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should assign the component instance to a var-',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<p><child-cmp var-alice></child-cmp></p>',
|
||||
directives: [ChildComp]
|
||||
describe("variable bindings", () => {
|
||||
it('should assign a component to a var-',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<p><child-cmp var-alice></child-cmp></p>',
|
||||
directives: [ChildComp]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
|
||||
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
|
||||
it('should assign a directive to a var-',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<p><div [export-dir] #localdir="dir"></div></p>',
|
||||
directives: [ExportDir]
|
||||
}));
|
||||
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
expect(view.rawView.locals.get('localdir')).toBeAnInstanceOf(ExportDir);
|
||||
|
||||
it('should make the assigned component accessible in property bindings',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<p><child-cmp var-alice></child-cmp>{{alice.ctxProp}}</p>',
|
||||
directives: [ChildComp]
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
it('should make the assigned component accessible in property bindings',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<p><child-cmp var-alice></child-cmp>{{alice.ctxProp}}</p>',
|
||||
directives: [ChildComp]
|
||||
}));
|
||||
|
||||
expect(view.rootNodes).toHaveText('hellohello'); // this first one is the
|
||||
// component, the second one is
|
||||
// the text binding
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
it('should assign two component instances each with a var-',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<p><child-cmp var-alice></child-cmp><child-cmp var-bob></p>',
|
||||
directives: [ChildComp]
|
||||
expect(view.rootNodes).toHaveText('hellohello'); // this first one is the
|
||||
// component, the second one is
|
||||
// the text binding
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
it('should assign two component instances each with a var-',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<p><child-cmp var-alice></child-cmp><child-cmp var-bob></p>',
|
||||
directives: [ChildComp]
|
||||
}));
|
||||
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
|
||||
expect(view.rawView.locals.get('bob')).toBeAnInstanceOf(ChildComp);
|
||||
expect(view.rawView.locals.get('alice')).not.toBe(view.rawView.locals.get('bob'));
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
|
||||
expect(view.rawView.locals.get('bob')).toBeAnInstanceOf(ChildComp);
|
||||
expect(view.rawView.locals.get('alice'))
|
||||
.not.toBe(view.rawView.locals.get('bob'));
|
||||
|
||||
it('should assign the component instance to a var- with shorthand syntax',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(
|
||||
MyComp, new viewAnn.View(
|
||||
{template: '<child-cmp #alice></child-cmp>', directives: [ChildComp]}));
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
it('should assign the component instance to a var- with shorthand syntax',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(
|
||||
MyComp,
|
||||
new viewAnn.View(
|
||||
{template: '<child-cmp #alice></child-cmp>', directives: [ChildComp]}));
|
||||
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
|
||||
|
||||
it('should assign the element instance to a user-defined variable',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(
|
||||
MyComp, new viewAnn.View({template: '<p><div var-alice><i>Hello</i></div></p>'}));
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
it('should assign the element instance to a user-defined variable',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(
|
||||
MyComp, new viewAnn.View({template: '<p><div var-alice><i>Hello</i></div></p>'}));
|
||||
|
||||
var value = view.rawView.locals.get('alice');
|
||||
expect(value).not.toBe(null);
|
||||
expect(value.tagName.toLowerCase()).toEqual('div');
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
var value = view.rawView.locals.get('alice');
|
||||
expect(value).not.toBe(null);
|
||||
expect(value.tagName.toLowerCase()).toEqual('div');
|
||||
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
|
||||
it('should assign the element instance to a user-defined variable with camelCase using dash-case',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(
|
||||
MyComp,
|
||||
new viewAnn.View({template: '<p><div var-super-alice><i>Hello</i></div></p>'}));
|
||||
it('should change dash-case to camel-case',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<p><child-cmp var-super-alice></child-cmp></p>',
|
||||
directives: [ChildComp]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
tb.createView(MyComp, {context: ctx})
|
||||
.then((view) => {
|
||||
expect(view.rawView.locals).not.toBe(null);
|
||||
expect(view.rawView.locals.get('superAlice')).toBeAnInstanceOf(ChildComp);
|
||||
|
||||
var value = view.rawView.locals.get('superAlice');
|
||||
expect(value).not.toBe(null);
|
||||
expect(value.tagName.toLowerCase()).toEqual('div');
|
||||
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("ON_PUSH components", () => {
|
||||
it("should use ChangeDetectorRef to manually request a check",
|
||||
@@ -1696,3 +1713,7 @@ class SomeImperativeViewport {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[export-dir]', exportAs: 'dir'})
|
||||
class ExportDir {
|
||||
}
|
||||
@@ -20,9 +20,11 @@ import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {ChangeDetection, ChangeDetectorDefinition} from 'angular2/change_detection';
|
||||
import {
|
||||
ProtoViewFactory,
|
||||
getChangeDetectorDefinitions
|
||||
getChangeDetectorDefinitions,
|
||||
createDirectiveVariableBindings
|
||||
} from 'angular2/src/core/compiler/proto_view_factory';
|
||||
import {Component, Directive} from 'angular2/annotations';
|
||||
import {Key} from 'angular2/di';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
@@ -65,12 +67,85 @@ export function main() {
|
||||
expect(pvs.length).toBe(1);
|
||||
expect(pvs[0].render).toBe(renderPv.render);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("createDirectiveVariableBindings", () => {
|
||||
it("should calculate directive variable bindings", () => {
|
||||
var dvbs = createDirectiveVariableBindings(
|
||||
new renderApi.ElementBinder(
|
||||
{variableBindings: MapWrapper.createFromStringMap({"exportName": "templateName"})}),
|
||||
[
|
||||
directiveBinding(
|
||||
{metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})}),
|
||||
directiveBinding(
|
||||
{metadata: new renderApi.DirectiveMetadata({exportAs: 'otherName'})})
|
||||
]);
|
||||
|
||||
expect(dvbs).toEqual(MapWrapper.createFromStringMap({"templateName": 0}));
|
||||
});
|
||||
|
||||
it("should set exportAs to $implicit for component with exportAs = null", () => {
|
||||
var dvbs = createDirectiveVariableBindings(
|
||||
new renderApi.ElementBinder(
|
||||
{variableBindings: MapWrapper.createFromStringMap({"$implicit": "templateName"})}),
|
||||
[
|
||||
directiveBinding({
|
||||
metadata: new renderApi.DirectiveMetadata(
|
||||
{exportAs: null, type: renderApi.DirectiveMetadata.COMPONENT_TYPE})
|
||||
})
|
||||
]);
|
||||
|
||||
expect(dvbs).toEqual(MapWrapper.createFromStringMap({"templateName": 0}));
|
||||
});
|
||||
|
||||
it("should throw we no directive exported with this name", () => {
|
||||
expect(() => {
|
||||
createDirectiveVariableBindings(
|
||||
new renderApi.ElementBinder({
|
||||
variableBindings:
|
||||
MapWrapper.createFromStringMap({"someInvalidName": "templateName"})
|
||||
}),
|
||||
[
|
||||
directiveBinding(
|
||||
{metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})})
|
||||
]);
|
||||
}).toThrowError(new RegExp("Cannot find directive with exportAs = 'someInvalidName'"));
|
||||
});
|
||||
|
||||
it("should throw when binding to a name exported by two directives", () => {
|
||||
expect(() => {
|
||||
createDirectiveVariableBindings(
|
||||
new renderApi.ElementBinder({
|
||||
variableBindings: MapWrapper.createFromStringMap({"exportName": "templateName"})
|
||||
}),
|
||||
[
|
||||
directiveBinding(
|
||||
{metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})}),
|
||||
directiveBinding(
|
||||
{metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})})
|
||||
]);
|
||||
}).toThrowError(new RegExp("More than one directive have exportAs = 'exportName'"));
|
||||
});
|
||||
|
||||
it("should not throw when not binding to a name exported by two directives", () => {
|
||||
expect(() => {
|
||||
createDirectiveVariableBindings(
|
||||
new renderApi.ElementBinder({variableBindings: MapWrapper.create()}), [
|
||||
directiveBinding(
|
||||
{metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})}),
|
||||
directiveBinding(
|
||||
{metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})})
|
||||
]);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function directiveBinding({metadata}: {metadata?: any} = {}) {
|
||||
return new DirectiveBinding(Key.get("dummy"), null, [], false, [], [], [], metadata);
|
||||
}
|
||||
|
||||
function createRenderProtoView(elementBinders = null, type: number = null) {
|
||||
if (isBlank(type)) {
|
||||
type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE;
|
||||
|
||||
@@ -58,11 +58,11 @@ export function main() {
|
||||
return DirectiveBinding.createFromType(type, annotation);
|
||||
}
|
||||
|
||||
function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); }
|
||||
function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null, null); }
|
||||
|
||||
function createComponentElBinder(nestedProtoView = null) {
|
||||
var binding = createDirectiveBinding(SomeComponent);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding);
|
||||
var binder = new ElementBinder(0, null, 0, null, null, binding);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
@@ -48,11 +48,11 @@ export function main() {
|
||||
return DirectiveBinding.createFromType(type, annotation);
|
||||
}
|
||||
|
||||
function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); }
|
||||
function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null, null); }
|
||||
|
||||
function createComponentElBinder(nestedProtoView = null) {
|
||||
var binding = createDirectiveBinding(SomeComponent);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding);
|
||||
var binder = new ElementBinder(0, null, 0, null, null, binding);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user