feat(compiler): added support for host actions
This commit is contained in:
@ -17,7 +17,7 @@ import {ViewRef, Renderer} from 'angular2/src/render/api';
|
||||
import {QueryList} from 'angular2/src/core/compiler/query_list';
|
||||
|
||||
class DummyDirective extends Directive {
|
||||
constructor({lifecycle, events} = {}) { super({lifecycle: lifecycle, events: events}); }
|
||||
constructor({lifecycle, events, hostActions} = {}) { super({lifecycle: lifecycle, events: events, hostActions:hostActions}); }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@ -97,6 +97,13 @@ class HasEventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
class HasHostAction {
|
||||
hostActionName;
|
||||
constructor() {
|
||||
this.hostActionName = "hostAction";
|
||||
}
|
||||
}
|
||||
|
||||
class NeedsAttribute {
|
||||
typeAttribute;
|
||||
titleAttribute;
|
||||
@ -381,7 +388,7 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('event emitters', () => {
|
||||
it('should return a list of event emitter accessors', () => {
|
||||
it('should return a list of event accessors', () => {
|
||||
var binding = DirectiveBinding.createFromType(
|
||||
HasEventEmitter, new DummyDirective({events: ['emitter']}));
|
||||
|
||||
@ -392,6 +399,18 @@ export function main() {
|
||||
expect(accessor.eventName).toEqual('emitter');
|
||||
expect(accessor.getter(new HasEventEmitter())).toEqual('emitter');
|
||||
});
|
||||
|
||||
it('should return a list of hostAction accessors', () => {
|
||||
var binding = DirectiveBinding.createFromType(
|
||||
HasEventEmitter, new DummyDirective({hostActions: {'hostActionName' : 'onAction'}}));
|
||||
|
||||
var inj = new ProtoElementInjector(null, 0, [binding]);
|
||||
expect(inj.hostActionAccessors.length).toEqual(1);
|
||||
|
||||
var accessor = inj.hostActionAccessors[0][0];
|
||||
expect(accessor.actionExpression).toEqual('onAction');
|
||||
expect(accessor.getter(new HasHostAction())).toEqual('hostAction');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -581,15 +581,37 @@ export function main() {
|
||||
|
||||
expect(listener.msg).toEqual('');
|
||||
|
||||
emitter.fireEvent('fired !');
|
||||
|
||||
PromiseWrapper.setTimeout(() => {
|
||||
ObservableWrapper.subscribe(emitter.event, (_) => {
|
||||
expect(listener.msg).toEqual('fired !');
|
||||
async.done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
emitter.fireEvent('fired !');
|
||||
});
|
||||
}));
|
||||
|
||||
if (DOM.supportsDOMEvents()) {
|
||||
it("should support invoking methods on the host element via hostActions", inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: '<div update-host-actions></div>',
|
||||
directives: [DirectiveUpdatingHostActions]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp, {context: ctx}).then((view) => {
|
||||
var injector = view.rawView.elementInjectors[0];
|
||||
var domElement = view.rootNodes[0];
|
||||
var updateHost = injector.get(DirectiveUpdatingHostActions);
|
||||
|
||||
ObservableWrapper.subscribe(updateHost.setAttr, (_) => {
|
||||
expect(DOM.getOuterHTML(domElement)).toEqual('<div update-host-actions="" class="ng-binding" key="value"></div>');
|
||||
async.done();
|
||||
});
|
||||
|
||||
updateHost.triggerSetAttr('value');
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
it('should support render events', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: '<div listener></div>',
|
||||
@ -671,6 +693,7 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
if (DOM.supportsDOMEvents()) {
|
||||
it('should support preventing default on render events', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
@ -1218,6 +1241,24 @@ class DirectiveUpdatingHostProperties {
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[update-host-actions]',
|
||||
hostActions: {
|
||||
'setAttr': 'setAttribute("key", $action["attrValue"])'
|
||||
}
|
||||
})
|
||||
class DirectiveUpdatingHostActions {
|
||||
setAttr:EventEmitter;
|
||||
|
||||
constructor() {
|
||||
this.setAttr = new EventEmitter();
|
||||
}
|
||||
|
||||
triggerSetAttr(attrValue) {
|
||||
ObservableWrapper.callNext(this.setAttr, {'attrValue': attrValue});
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[listener]',
|
||||
hostListeners: {'event': 'onEvent($event)'}
|
||||
|
@ -31,6 +31,7 @@ import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils
|
||||
export function main() {
|
||||
// TODO(tbosch): add more tests here!
|
||||
|
||||
|
||||
describe('AppViewManagerUtils', () => {
|
||||
|
||||
var metadataReader;
|
||||
@ -71,6 +72,7 @@ export function main() {
|
||||
'isExportingComponent' : false,
|
||||
'isExportingElement' : false,
|
||||
'getEventEmitterAccessors' : [],
|
||||
'getHostActionAccessors' : [],
|
||||
'getComponent' : null,
|
||||
'getDynamicallyLoadedComponent': null,
|
||||
'getHost': host
|
||||
@ -154,11 +156,13 @@ export function main() {
|
||||
var hostView = createView(hostPv);
|
||||
var spyEventAccessor1 = SpyObject.stub({"subscribe" : null});
|
||||
SpyObject.stub(hostView.elementInjectors[0], {
|
||||
'getHostActionAccessors': [],
|
||||
'getEventEmitterAccessors': [[spyEventAccessor1]],
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
var spyEventAccessor2 = SpyObject.stub({"subscribe" : null});
|
||||
SpyObject.stub(hostView.elementInjectors[1], {
|
||||
'getHostActionAccessors': [],
|
||||
'getEventEmitterAccessors': [[spyEventAccessor2]],
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
@ -172,6 +176,36 @@ export function main() {
|
||||
expect(spyEventAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
});
|
||||
|
||||
it("should set up host action listeners", () => {
|
||||
var dir = new Object();
|
||||
|
||||
var hostPv = createProtoView([
|
||||
createComponentElBinder(null),
|
||||
createEmptyElBinder()
|
||||
]);
|
||||
var hostView = createView(hostPv);
|
||||
var spyActionAccessor1 = SpyObject.stub({"subscribe" : null});
|
||||
SpyObject.stub(hostView.elementInjectors[0], {
|
||||
'getHostActionAccessors': [[spyActionAccessor1]],
|
||||
'getEventEmitterAccessors': [],
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
var spyActionAccessor2 = SpyObject.stub({"subscribe" : null});
|
||||
SpyObject.stub(hostView.elementInjectors[1], {
|
||||
'getHostActionAccessors': [[spyActionAccessor2]],
|
||||
'getEventEmitterAccessors': [],
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
|
||||
var shadowView = createView();
|
||||
utils.attachComponentView(hostView, 0, shadowView);
|
||||
|
||||
utils.attachAndHydrateInPlaceHostView(null, null, hostView, createInjector());
|
||||
|
||||
expect(spyActionAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
expect(spyActionAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('attachViewInContainer', () => {
|
||||
|
@ -25,7 +25,8 @@ export function main() {
|
||||
someDirectiveWithHostProperties,
|
||||
someDirectiveWithHostAttributes,
|
||||
someDirectiveWithEvents,
|
||||
someDirectiveWithGlobalEvents
|
||||
someDirectiveWithGlobalEvents,
|
||||
someDirectiveWithHostActions
|
||||
];
|
||||
parser = new Parser(new Lexer());
|
||||
});
|
||||
@ -171,6 +172,14 @@ export function main() {
|
||||
expect(eventBinding.source.source).toEqual('doItGlobal()');
|
||||
});
|
||||
|
||||
it('should bind directive host actions', () => {
|
||||
var results = process(
|
||||
el('<div some-decor-host-actions></div>')
|
||||
);
|
||||
var directiveBinding = results[0].directives[0];
|
||||
expect(directiveBinding.hostActions[0].actionName).toEqual('focus');
|
||||
});
|
||||
|
||||
//TODO: assertions should be enabled when running tests: https://github.com/angular/angular/issues/1340
|
||||
describe('component directives', () => {
|
||||
it('should save the component id', () => {
|
||||
@ -276,6 +285,13 @@ var someDirectiveWithEvents = new DirectiveMetadata({
|
||||
})
|
||||
});
|
||||
|
||||
var someDirectiveWithHostActions = new DirectiveMetadata({
|
||||
selector: '[some-decor-host-actions]',
|
||||
hostActions: MapWrapper.createFromStringMap({
|
||||
'focus': 'focus()'
|
||||
})
|
||||
});
|
||||
|
||||
var someDirectiveWithGlobalEvents = new DirectiveMetadata({
|
||||
selector: '[some-decor-globalevents]',
|
||||
hostListeners: MapWrapper.createFromStringMap({
|
||||
@ -287,4 +303,4 @@ var componentWithNonElementSelector = new DirectiveMetadata({
|
||||
id: 'componentWithNonElementSelector',
|
||||
selector: '[attr]',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
});
|
10
modules/angular2/test/render/dom/convert_spec.js
vendored
10
modules/angular2/test/render/dom/convert_spec.js
vendored
@ -2,7 +2,7 @@ import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DirectiveMetadata} from 'angular2/src/render/api';
|
||||
import {directiveMetadataFromMap, directiveMetadataToMap} from
|
||||
'angular2/src/render/dom/convert';
|
||||
import {describe, expect, it} from 'angular2/test_lib';
|
||||
import {ddescribe, describe, expect, it} from 'angular2/test_lib';
|
||||
|
||||
export function main() {
|
||||
describe('convert', () => {
|
||||
@ -12,6 +12,8 @@ export function main() {
|
||||
hostListeners: MapWrapper.createFromPairs([['listenKey', 'listenVal']]),
|
||||
hostProperties:
|
||||
MapWrapper.createFromPairs([['hostPropKey', 'hostPropVal']]),
|
||||
hostActions:
|
||||
MapWrapper.createFromPairs([['hostActionKey', 'hostActionVal']]),
|
||||
id: 'someComponent',
|
||||
properties: MapWrapper.createFromPairs([['propKey', 'propVal']]),
|
||||
readAttributes: ['read1', 'read2'],
|
||||
@ -24,6 +26,8 @@ export function main() {
|
||||
MapWrapper.createFromPairs([['listenKey', 'listenVal']]));
|
||||
expect(MapWrapper.get(map, 'hostProperties')).toEqual(
|
||||
MapWrapper.createFromPairs([['hostPropKey', 'hostPropVal']]));
|
||||
expect(MapWrapper.get(map, 'hostActions')).toEqual(
|
||||
MapWrapper.createFromPairs([['hostActionKey', 'hostActionVal']]));
|
||||
expect(MapWrapper.get(map, 'id')).toEqual('someComponent');
|
||||
expect(MapWrapper.get(map, 'properties')).toEqual(
|
||||
MapWrapper.createFromPairs([['propKey', 'propVal']]));
|
||||
@ -39,6 +43,8 @@ export function main() {
|
||||
['hostListeners', MapWrapper.createFromPairs([['testKey', 'testVal']])],
|
||||
['hostProperties',
|
||||
MapWrapper.createFromPairs([['hostPropKey', 'hostPropVal']])],
|
||||
['hostActions',
|
||||
MapWrapper.createFromPairs([['hostActionKey', 'hostActionVal']])],
|
||||
['id', 'testId'],
|
||||
['properties', MapWrapper.createFromPairs([['propKey', 'propVal']])],
|
||||
['readAttributes', ['readTest1', 'readTest2']],
|
||||
@ -51,6 +57,8 @@ export function main() {
|
||||
MapWrapper.createFromPairs([['testKey', 'testVal']]));
|
||||
expect(meta.hostProperties).toEqual(
|
||||
MapWrapper.createFromPairs([['hostPropKey', 'hostPropVal']]));
|
||||
expect(meta.hostActions).toEqual(
|
||||
MapWrapper.createFromPairs([['hostActionKey', 'hostActionVal']]));
|
||||
expect(meta.id).toEqual('testId');
|
||||
expect(meta.properties).toEqual(
|
||||
MapWrapper.createFromPairs([['propKey', 'propVal']]));
|
||||
|
@ -96,6 +96,25 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call actions on the element',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<div with-host-actions></div>',
|
||||
directives: [directiveWithHostActions]
|
||||
})
|
||||
]).then( (protoViewDtos) => {
|
||||
var views = tb.createRootViews(protoViewDtos);
|
||||
var componentView = views[1];
|
||||
|
||||
tb.renderer.callAction(componentView.viewRef, 0, 'setAttribute("key", "value")', null);
|
||||
expect(DOM.getOuterHTML(tb.rootEl)).toContain('key="value"');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should add and remove views to and from containers',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([someComponent,
|
||||
@ -152,3 +171,12 @@ var someComponent = new DirectiveMetadata({
|
||||
type: DirectiveMetadata.COMPONENT_TYPE,
|
||||
selector: 'some-comp'
|
||||
});
|
||||
|
||||
var directiveWithHostActions = new DirectiveMetadata({
|
||||
id: 'withHostActions',
|
||||
type: DirectiveMetadata.DIRECTIVE_TYPE,
|
||||
selector: '[with-host-actions]',
|
||||
hostActions: MapWrapper.createFromStringMap({
|
||||
'setAttr' : 'setAttribute("key", "value")'
|
||||
})
|
||||
});
|
||||
|
@ -6,6 +6,7 @@
|
||||
"hostListeners": {},
|
||||
"hostProperties": {},
|
||||
"hostAttributes": {},
|
||||
"hostActions": null,
|
||||
"properties": {},
|
||||
"readAttributes": [],
|
||||
"type": 1,
|
||||
|
Reference in New Issue
Block a user