/
host_directives_feature.ts
92 lines (83 loc) 路 3.18 KB
/
host_directives_feature.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**
* @license
* Copyright Google LLC 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 {resolveForwardRef} from '../../di';
import {Type} from '../../interface/type';
import {EMPTY_OBJ} from '../../util/empty';
import {getDirectiveDef} from '../definition';
import {DirectiveDef} from '../interfaces/definition';
import {TContainerNode, TElementContainerNode, TElementNode} from '../interfaces/node';
import {LView, TView} from '../interfaces/view';
/** Values that can be used to define a host directive through the `HostDirectivesFeature`. */
type HostDirectiveConfig = Type<unknown>|{
directive: Type<unknown>;
inputs?: string[];
outputs?: string[];
};
/**
* This feature add the host directives behavior to a directive definition by patching a
* function onto it. The expectation is that the runtime will invoke the function during
* directive matching.
*
* For example:
* ```ts
* class ComponentWithHostDirective {
* static 傻cmp = defineComponent({
* type: ComponentWithHostDirective,
* features: [傻傻HostDirectivesFeature([
* SimpleHostDirective,
* {directive: AdvancedHostDirective, inputs: ['foo: alias'], outputs: ['bar']},
* ])]
* });
* }
* ```
*
* @codeGenApi
*/
export function 傻傻HostDirectivesFeature(rawHostDirectives: HostDirectiveConfig[]|
(() => HostDirectiveConfig[])) {
return (definition: DirectiveDef<unknown>) => {
definition.findHostDirectiveDefs = findHostDirectiveDefs;
definition.hostDirectives =
(Array.isArray(rawHostDirectives) ? rawHostDirectives : rawHostDirectives()).map(dir => {
return typeof dir === 'function' ?
{directive: resolveForwardRef(dir), inputs: EMPTY_OBJ, outputs: EMPTY_OBJ} :
{
directive: resolveForwardRef(dir.directive),
inputs: bindingArrayToMap(dir.inputs),
outputs: bindingArrayToMap(dir.outputs)
};
});
};
}
function findHostDirectiveDefs(
matches: DirectiveDef<unknown>[], def: DirectiveDef<unknown>, tView: TView, lView: LView,
tNode: TElementNode|TContainerNode|TElementContainerNode): void {
if (def.hostDirectives !== null) {
for (const hostDirectiveConfig of def.hostDirectives) {
const hostDirectiveDef = getDirectiveDef(hostDirectiveConfig.directive)!;
// TODO(crisbeto): assert that the def exists.
// Host directives execute before the host so that its host bindings can be overwritten.
findHostDirectiveDefs(matches, hostDirectiveDef, tView, lView, tNode);
matches.push(hostDirectiveDef);
}
}
}
/**
* Converts an array in the form of `['publicName', 'alias', 'otherPublicName', 'otherAlias']` into
* a map in the form of `{publicName: 'alias', otherPublicName: 'otherAlias'}`.
*/
function bindingArrayToMap(bindings: string[]|undefined) {
if (bindings === undefined || bindings.length === 0) {
return EMPTY_OBJ;
}
const result: {[publicName: string]: string} = {};
for (let i = 0; i < bindings.length; i += 2) {
result[bindings[i]] = bindings[i + 1];
}
return result;
}