Skip to content

Commit 5049a50

Browse files
tboschIgorMinar
authored andcommitted
fix(core): host bindings and host listeners for animations
Host bindings / listeners for animation properties should use the renderer of the component view.
1 parent 6b7937f commit 5049a50

File tree

20 files changed

+647
-615
lines changed

20 files changed

+647
-615
lines changed

modules/@angular/compiler/src/view_compiler_next/view_compiler.ts

Lines changed: 78 additions & 56 deletions
Large diffs are not rendered by default.

modules/@angular/core/src/view/element.ts

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
*/
88

99
import {isDevMode} from '../application_ref';
10+
import {RendererTypeV2, RendererV2} from '../render/api';
1011
import {SecurityContext} from '../security';
1112

12-
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementHandleEventFn, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData} from './types';
13+
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementHandleEventFn, NodeData, NodeDef, NodeFlags, NodeType, OutputDef, OutputType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData, asProviderData} from './types';
1314
import {checkAndUpdateBinding, dispatchEvent, elementEventFullName, filterQueryId, getParentRenderElement, resolveViewDefinition, sliceErrorStack, splitMatchedQueriesDsl, splitNamespace} from './util';
1415

1516
const NOOP: any = () => {};
@@ -34,20 +35,20 @@ export function anchorDef(
3435
parent: undefined,
3536
renderParent: undefined,
3637
bindingIndex: undefined,
37-
disposableIndex: undefined,
38+
outputIndex: undefined,
3839
// regular values
3940
flags,
4041
childFlags: 0,
4142
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount,
4243
bindings: [],
43-
disposableCount: 0,
44+
outputs: [],
4445
element: {
4546
ns: undefined,
4647
name: undefined,
47-
attrs: undefined,
48-
outputs: [], template, source,
49-
// will bet set by the view definition
50-
component: undefined,
48+
attrs: undefined, template, source,
49+
componentProvider: undefined,
50+
componentView: undefined,
51+
componentRendererType: undefined,
5152
publicProviders: undefined,
5253
allProviders: undefined, handleEvent
5354
},
@@ -65,8 +66,13 @@ export function elementDef(
6566
fixedAttrs: [string, string][] = [],
6667
bindings?:
6768
([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
68-
[BindingType.ElementAttribute | BindingType.ElementProperty, string, SecurityContext])[],
69-
outputs?: (string | [string, string])[], handleEvent?: ElementHandleEventFn): NodeDef {
69+
[
70+
BindingType.ElementAttribute | BindingType.ElementProperty |
71+
BindingType.ComponentHostProperty,
72+
string, SecurityContext
73+
])[],
74+
outputs?: ([string, string])[], handleEvent?: ElementHandleEventFn,
75+
componentView?: () => ViewDefinition, componentRendererType?: RendererTypeV2): NodeDef {
7076
if (!handleEvent) {
7177
handleEvent = NOOP;
7278
}
@@ -93,29 +99,35 @@ export function elementDef(
9399
break;
94100
case BindingType.ElementAttribute:
95101
case BindingType.ElementProperty:
102+
case BindingType.ComponentHostProperty:
96103
securityContext = <SecurityContext>entry[2];
97104
break;
98105
}
99106
bindingDefs[i] = {type: bindingType, ns, name, nonMinifiedName: name, securityContext, suffix};
100107
}
101108
outputs = outputs || [];
102-
const outputDefs: ElementOutputDef[] = new Array(outputs.length);
109+
const outputDefs: OutputDef[] = new Array(outputs.length);
103110
for (let i = 0; i < outputs.length; i++) {
104-
const output = outputs[i];
105-
let target: string;
106-
let eventName: string;
107-
if (Array.isArray(output)) {
108-
[target, eventName] = output;
109-
} else {
110-
eventName = output;
111-
}
112-
outputDefs[i] = {eventName: eventName, target: target};
111+
const [target, eventName] = outputs[i];
112+
outputDefs[i] = {
113+
type: OutputType.ElementOutput,
114+
target: <any>target, eventName,
115+
propName: undefined
116+
};
113117
}
114118
fixedAttrs = fixedAttrs || [];
115119
const attrs = <[string, string, string][]>fixedAttrs.map(([namespaceAndName, value]) => {
116120
const [ns, name] = splitNamespace(namespaceAndName);
117121
return [ns, name, value];
118122
});
123+
// This is needed as the jit compiler always uses an empty hash as default RendererTypeV2,
124+
// which is not filled for host views.
125+
if (componentRendererType && componentRendererType.encapsulation == null) {
126+
componentRendererType = null;
127+
}
128+
if (componentView) {
129+
flags |= NodeFlags.HasComponent;
130+
}
119131
return {
120132
type: NodeType.Element,
121133
// will bet set by the view definition
@@ -124,21 +136,21 @@ export function elementDef(
124136
parent: undefined,
125137
renderParent: undefined,
126138
bindingIndex: undefined,
127-
disposableIndex: undefined,
139+
outputIndex: undefined,
128140
// regular values
129141
flags,
130142
childFlags: 0,
131143
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount,
132144
bindings: bindingDefs,
133-
disposableCount: outputDefs.length,
145+
outputs: outputDefs,
134146
element: {
135147
ns,
136148
name,
137149
attrs,
138-
outputs: outputDefs, source,
150+
source,
139151
template: undefined,
140152
// will bet set by the view definition
141-
component: undefined,
153+
componentProvider: undefined, componentView, componentRendererType,
142154
publicProviders: undefined,
143155
allProviders: undefined, handleEvent,
144156
},
@@ -174,21 +186,24 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El
174186
renderer.setAttribute(el, name, value, ns);
175187
}
176188
}
177-
if (elDef.outputs.length) {
178-
for (let i = 0; i < elDef.outputs.length; i++) {
179-
const output = elDef.outputs[i];
180-
const handleEventClosure = renderEventHandlerClosure(
181-
view, def.index, elementEventFullName(output.target, output.eventName));
182-
const disposable =
183-
<any>renderer.listen(output.target || el, output.eventName, handleEventClosure);
184-
view.disposables[def.disposableIndex + i] = disposable;
189+
return el;
190+
}
191+
192+
export function listenToElementOutputs(view: ViewData, compView: ViewData, def: NodeDef, el: any) {
193+
for (let i = 0; i < def.outputs.length; i++) {
194+
const output = def.outputs[i];
195+
const handleEventClosure = renderEventHandlerClosure(
196+
view, def.index, elementEventFullName(output.target, output.eventName));
197+
let listenTarget = output.target;
198+
let listenerView = view;
199+
if (output.target === 'component') {
200+
listenTarget = null;
201+
listenerView = compView;
185202
}
203+
const disposable =
204+
<any>listenerView.renderer.listen(listenTarget || el, output.eventName, handleEventClosure);
205+
view.disposables[def.outputIndex + i] = disposable;
186206
}
187-
return {
188-
renderElement: el,
189-
embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined,
190-
projectedViews: undefined
191-
};
192207
}
193208

194209
function renderEventHandlerClosure(view: ViewData, index: number, eventName: string) {
@@ -223,7 +238,8 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
223238
return;
224239
}
225240
const binding = def.bindings[bindingIdx];
226-
const renderNode = asElementData(view, def.index).renderElement;
241+
const elData = asElementData(view, def.index);
242+
const renderNode = elData.renderElement;
227243
const name = binding.name;
228244
switch (binding.type) {
229245
case BindingType.ElementAttribute:
@@ -238,6 +254,9 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
238254
case BindingType.ElementProperty:
239255
setElementProperty(view, binding, renderNode, name, value);
240256
break;
257+
case BindingType.ComponentHostProperty:
258+
setElementProperty(elData.componentView, binding, renderNode, name, value);
259+
break;
241260
}
242261
}
243262

modules/@angular/core/src/view/ng_content.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
1818
parent: undefined,
1919
renderParent: undefined,
2020
bindingIndex: undefined,
21-
disposableIndex: undefined,
21+
outputIndex: undefined,
2222
// regular values
2323
flags: 0,
2424
childFlags: 0,
@@ -28,7 +28,7 @@ export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
2828
references: {}, ngContentIndex,
2929
childCount: 0,
3030
bindings: [],
31-
disposableCount: 0,
31+
outputs: [],
3232
element: undefined,
3333
provider: undefined,
3434
text: undefined,

modules/@angular/core/src/view/provider.ts

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {ViewEncapsulation} from '../metadata/view';
1515
import {Renderer as RendererV1, RendererFactoryV2, RendererTypeV2, RendererV2} from '../render/api';
1616

1717
import {createChangeDetectorRef, createInjector, createRendererV1, createTemplateRef, createViewContainerRef} from './refs';
18-
import {BindingDef, BindingType, DepDef, DepFlags, DirectiveOutputDef, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
18+
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, OutputDef, OutputType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
1919
import {checkAndUpdateBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
2020

2121
const RendererV1TokenKey = tokenKey(RendererV1);
@@ -31,8 +31,7 @@ const NOT_CREATED = new Object();
3131
export function directiveDef(
3232
flags: NodeFlags, matchedQueries: [string | number, QueryValueType][], childCount: number,
3333
ctor: any, deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
34-
outputs?: {[name: string]: string}, component?: () => ViewDefinition,
35-
rendererType?: RendererTypeV2): NodeDef {
34+
outputs?: {[name: string]: string}): NodeDef {
3635
const bindings: BindingDef[] = [];
3736
if (props) {
3837
for (let prop in props) {
@@ -46,15 +45,16 @@ export function directiveDef(
4645
};
4746
}
4847
}
49-
const outputDefs: DirectiveOutputDef[] = [];
48+
const outputDefs: OutputDef[] = [];
5049
if (outputs) {
5150
for (let propName in outputs) {
52-
outputDefs.push({propName, eventName: outputs[propName]});
51+
outputDefs.push(
52+
{type: OutputType.DirectiveOutput, propName, target: null, eventName: outputs[propName]});
5353
}
5454
}
5555
return _def(
5656
NodeType.Directive, flags, matchedQueries, childCount, ProviderType.Class, ctor, ctor, deps,
57-
bindings, outputDefs, component, rendererType);
57+
bindings, outputDefs);
5858
}
5959

6060
export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef {
@@ -70,14 +70,8 @@ export function providerDef(
7070
export function _def(
7171
type: NodeType, flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
7272
childCount: number, providerType: ProviderType, token: any, value: any,
73-
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: DirectiveOutputDef[],
74-
component?: () => ViewDefinition, rendererType?: RendererTypeV2): NodeDef {
73+
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: OutputDef[]): NodeDef {
7574
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
76-
// This is needed as the jit compiler always uses an empty hash as default RendererTypeV2,
77-
// which is not filled for host views.
78-
if (rendererType && rendererType.encapsulation == null) {
79-
rendererType = null;
80-
}
8175
if (!outputs) {
8276
outputs = [];
8377
}
@@ -96,9 +90,6 @@ export function _def(
9690
}
9791
return {flags, token, tokenKey: tokenKey(token)};
9892
});
99-
if (component) {
100-
flags = flags | NodeFlags.HasComponent;
101-
}
10293

10394
return {
10495
type,
@@ -108,20 +99,14 @@ export function _def(
10899
parent: undefined,
109100
renderParent: undefined,
110101
bindingIndex: undefined,
111-
disposableIndex: undefined,
102+
outputIndex: undefined,
112103
// regular values
113104
flags,
114105
childFlags: 0,
115106
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references,
116-
ngContentIndex: undefined, childCount, bindings,
117-
disposableCount: outputs.length,
107+
ngContentIndex: undefined, childCount, bindings, outputs,
118108
element: undefined,
119-
provider: {
120-
type: providerType,
121-
token,
122-
tokenKey: tokenKey(token), value,
123-
deps: depDefs, outputs, component, rendererType
124-
},
109+
provider: {type: providerType, token, tokenKey: tokenKey(token), value, deps: depDefs},
125110
text: undefined,
126111
pureExpression: undefined,
127112
query: undefined,
@@ -149,17 +134,17 @@ export function createPipeInstance(view: ViewData, def: NodeDef): any {
149134

150135
export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
151136
// components can see other private services, other directives can't.
152-
const allowPrivateServices = (def.flags & NodeFlags.HasComponent) > 0;
137+
const allowPrivateServices = (def.flags & NodeFlags.IsComponent) > 0;
153138
const providerDef = def.provider;
154139
// directives are always eager and classes!
155140
const instance =
156141
createClass(view, def.parent, allowPrivateServices, def.provider.value, def.provider.deps);
157-
if (providerDef.outputs.length) {
158-
for (let i = 0; i < providerDef.outputs.length; i++) {
159-
const output = providerDef.outputs[i];
142+
if (def.outputs.length) {
143+
for (let i = 0; i < def.outputs.length; i++) {
144+
const output = def.outputs[i];
160145
const subscription = instance[output.propName].subscribe(
161146
eventHandlerClosure(view, def.parent.index, output.eventName));
162-
view.disposables[def.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
147+
view.disposables[def.outputIndex + i] = subscription.unsubscribe.bind(subscription);
163148
}
164149
}
165150
return instance;
@@ -371,7 +356,7 @@ export function resolveDep(
371356
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
372357
let compView: ViewData;
373358
if (allowPrivateServices) {
374-
compView = asProviderData(view, elDef.element.component.index).componentView;
359+
compView = asElementData(view, elDef.index).componentView;
375360
} else {
376361
compView = view;
377362
while (compView.parent && !isComponentView(compView)) {
@@ -396,8 +381,8 @@ function checkAndUpdateProp(
396381
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
397382
}
398383
if (changed) {
399-
if (def.flags & NodeFlags.HasComponent) {
400-
const compView = providerData.componentView;
384+
if (def.flags & NodeFlags.IsComponent) {
385+
const compView = asElementData(view, def.parent.index).componentView;
401386
if (compView.def.flags & ViewFlags.OnPush) {
402387
compView.state |= ViewState.ChecksEnabled;
403388
}

modules/@angular/core/src/view/pure_expression.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function _pureExpressionDef(type: PureExpressionType, propertyNames: string[]):
4343
parent: undefined,
4444
renderParent: undefined,
4545
bindingIndex: undefined,
46-
disposableIndex: undefined,
46+
outputIndex: undefined,
4747
// regular values
4848
flags: 0,
4949
childFlags: 0,
@@ -53,7 +53,7 @@ function _pureExpressionDef(type: PureExpressionType, propertyNames: string[]):
5353
references: {},
5454
ngContentIndex: undefined,
5555
childCount: 0, bindings,
56-
disposableCount: 0,
56+
outputs: [],
5757
element: undefined,
5858
provider: undefined,
5959
text: undefined,

modules/@angular/core/src/view/query.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function queryDef(
3131
parent: undefined,
3232
renderParent: undefined,
3333
bindingIndex: undefined,
34-
disposableIndex: undefined,
34+
outputIndex: undefined,
3535
// regular values
3636
flags,
3737
childFlags: 0,
@@ -42,7 +42,7 @@ export function queryDef(
4242
references: {},
4343
childCount: 0,
4444
bindings: [],
45-
disposableCount: 0,
45+
outputs: [],
4646
element: undefined,
4747
provider: undefined,
4848
text: undefined,

modules/@angular/core/src/view/refs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class ComponentFactory_ extends ComponentFactory<any> {
4242
injector: Injector, projectableNodes: any[][] = null,
4343
rootSelectorOrNode: string|any = null): ComponentRef<any> {
4444
const viewDef = resolveViewDefinition(this._viewClass);
45-
const componentNodeIndex = viewDef.nodes[0].element.component.index;
45+
const componentNodeIndex = viewDef.nodes[0].element.componentProvider.index;
4646
const view = Services.createRootView(
4747
injector, projectableNodes || [], rootSelectorOrNode, viewDef, EMPTY_CONTEXT);
4848
const component = asProviderData(view, componentNodeIndex).instance;
@@ -255,7 +255,7 @@ export function createInjector(view: ViewData, elDef: NodeDef): Injector {
255255
class Injector_ implements Injector {
256256
constructor(private view: ViewData, private elDef: NodeDef) {}
257257
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
258-
const allowPrivateServices = !!this.elDef.element.component;
258+
const allowPrivateServices = (this.elDef.flags & NodeFlags.HasComponent) !== 0;
259259
return Services.resolveDep(
260260
this.view, this.elDef, allowPrivateServices,
261261
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);

0 commit comments

Comments
 (0)