mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
committed by
Jessica Janiuk
parent
4cfb06086b
commit
bc97322641
@@ -325,6 +325,7 @@ groups:
|
||||
'aio/content/guide/two-way-binding.md',
|
||||
'aio/content/examples/two-way-binding/**/{*,.*}',
|
||||
'aio/content/guide/built-in-directives.md',
|
||||
'aio/content/guide/directive-composition-api.md',
|
||||
'aio/content/examples/built-in-directives/**/{*,.*}',
|
||||
'aio/content/images/guide/built-in-directives/**/{*,.*}',
|
||||
'aio/content/guide/template-reference-variables.md',
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# Directive composition API
|
||||
|
||||
Angular directives offer a great way to encapsulate reusable behaviors— directives can apply
|
||||
attributes, CSS classes, and event listeners to an element.
|
||||
|
||||
The *directive composition API* lets you apply directives to a component's host element from
|
||||
_within_ the component.
|
||||
|
||||
## Adding directives to a component
|
||||
|
||||
You apply directives to a component by adding a `hostDirectives` property to a component's
|
||||
decorator. We call such directives *host directives*.
|
||||
|
||||
In this example, we apply the directive `MenuBehavior` to the host element of `AdminMenu`. This
|
||||
works similarly to applying the `MenuBehavior` to the `<admin-menu>` element in a template.
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'admin-menu',
|
||||
template: 'admin-menu.html',
|
||||
hostDirectives: [MenuBehavior],
|
||||
})
|
||||
export class AdminMenu { }
|
||||
```
|
||||
|
||||
When the framework renders a component, Angular also creates an instance of each host directive. The
|
||||
directives' host bindings apply to the component's host element. By default, host directive inputs
|
||||
and outputs are not exposed as part of the component's public API. See
|
||||
[Including inputs and outputs](#including-inputs-and-outputs) below for more information.
|
||||
|
||||
**Angular applies host directives statically at compile time.** You cannot dynamically add
|
||||
directives at runtime.
|
||||
|
||||
**Directives used in `hostDirectives` must be `standalone: true`.**
|
||||
|
||||
**Angular ignores the `selector` of directives applied in the `hostDirectives` property.**
|
||||
|
||||
## Including inputs and outputs
|
||||
|
||||
When you apply `hostDirectives` to your component, the inputs and outputs from the host directives
|
||||
are not included in your component's API by default. You can explicitly include inputs and outputs
|
||||
in your component's API by expanding the entry in `hostDirectives`:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'admin-menu',
|
||||
template: 'admin-menu.html',
|
||||
hostDirectives: [{
|
||||
directive: MenuBehavior,
|
||||
inputs: ['menuId'],
|
||||
outputs: ['menuClosed'],
|
||||
}],
|
||||
})
|
||||
export class AdminMenu { }
|
||||
```
|
||||
|
||||
By explicitly specifying the inputs and outputs, consumers of the component with `hostDirective` can
|
||||
bind them in a template:
|
||||
|
||||
```html
|
||||
|
||||
<admin-menu menuId="top-menu" (menuClosed)="logMenuClosed()">
|
||||
```
|
||||
|
||||
Furthermore, you can alias inputs and outputs from `hostDirective` to customize the API of your
|
||||
component:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'admin-menu',
|
||||
template: 'admin-menu.html',
|
||||
hostDirectives: [{
|
||||
directive: MenuBehavior,
|
||||
inputs: ['menuId: id'],
|
||||
outputs: ['menuClosed: closed'],
|
||||
}],
|
||||
})
|
||||
export class AdminMenu { }
|
||||
```
|
||||
|
||||
```html
|
||||
|
||||
<admin-menu id="top-menu" (closed)="logMenuClosed()">
|
||||
```
|
||||
|
||||
## Adding directives to another directive
|
||||
|
||||
You can also add `hostDirectives` to other directives, in addition to components. This enables the
|
||||
transitive aggregation of multiple behaviors.
|
||||
|
||||
In the following example, we define two directives, `Menu` and `Tooltip`. We then compose the behavior
|
||||
of these two directives in `MenuWithTooltip`. Finally, we apply `MenuWithTooltip`
|
||||
to `SpecializedMenuWithTooltip`.
|
||||
|
||||
When `SpecializedMenuWithTooltip` is used in a template, it creates instances of all of `Menu`
|
||||
, `Tooltip`, and `MenuWithTooltip`. Each of these directives' host bindings apply to the host
|
||||
element of `SpecializedMenuWithTooltip`.
|
||||
|
||||
```typescript
|
||||
@Directive({...})
|
||||
export class Menu { }
|
||||
|
||||
@Directive({...})
|
||||
export class Tooltip { }
|
||||
|
||||
// MenuWithTooltip can compose behaviors from multiple other directives
|
||||
@Directive({
|
||||
hostDirectives: [Tooltip, Menu],
|
||||
})
|
||||
export class MenuWithTooltip { }
|
||||
|
||||
// CustomWidget can apply the already-composed behaviors from MenuWithTooltip
|
||||
@Directive({
|
||||
hostDirectives: [MenuWithTooltip],
|
||||
})
|
||||
export class SpecializedMenuWithTooltip { }
|
||||
```
|
||||
|
||||
## Host directive semantics
|
||||
|
||||
### Directive execution order
|
||||
|
||||
Host directives go through the same lifecycle as components and directives used directly in a
|
||||
template. However, host directives always execute their constructor, lifecycle hooks, and bindings _
|
||||
before_ the component or directive on which they are applied.
|
||||
|
||||
The following example shows minimal use of a host directive:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'admin-menu',
|
||||
template: 'admin-menu.html',
|
||||
hostDirectives: [MenuBehavior],
|
||||
})
|
||||
export class AdminMenu { }
|
||||
```
|
||||
|
||||
The order of execution here is:
|
||||
|
||||
1. `MenuBehavior` instantiated
|
||||
2. `AdminMenu` instantiated
|
||||
3. `MenuBehavior` receives inputs (`ngOnInit`)
|
||||
4. `AdminMenu` receives inputs (`ngOnInit`)
|
||||
5. `MenuBehavior` applies host bindings
|
||||
6. `AdminMenu` applies host bindings
|
||||
|
||||
This order of operations means that components with `hostDirectives` can override any host bindings
|
||||
specified by a host directive.
|
||||
|
||||
This order of operations extends to nested chains of host directives, as shown in the following
|
||||
example.
|
||||
|
||||
```typescript
|
||||
@Directive({...})
|
||||
export class Tooltip { }
|
||||
|
||||
@Directive({
|
||||
hostDirectives: [Tooltip],
|
||||
})
|
||||
export class CustomTooltip { }
|
||||
|
||||
@Directive({
|
||||
hostDirectives: [CustomTooltip],
|
||||
})
|
||||
export class EvenMoreCustomTooltip { }
|
||||
```
|
||||
|
||||
In the example above, the order of execution is:
|
||||
|
||||
1. `Tooltip` instantiated
|
||||
2. `CustomTooltip` instantiated
|
||||
3. `EvenMoreCustomTooltip` instantiated
|
||||
4. `Tooltip` receives inputs (`ngOnInit`)
|
||||
5. `CustomTooltip` receives inputs (`ngOnInit`)
|
||||
6. `EvenMoreCustomTooltip` receives inputs (`ngOnInit`)
|
||||
7. `Tooltip` applies host bindings
|
||||
8. `CustomTooltip` applies host bindings
|
||||
9. `EvenMoreCustomTooltip` applies host bindings
|
||||
|
||||
### Dependency injection
|
||||
|
||||
A component or directive that specifies `hostDirectives` can inject the instances of those host
|
||||
directives and vice versa.
|
||||
|
||||
When applying host directives to a component, both the component and host directives can define
|
||||
providers.
|
||||
|
||||
If a component or directive with `hostDirectives` and those host directives both provide the same
|
||||
injection token, the providers defined by class with `hostDirectives` take precedence over providers
|
||||
defined by the host directives.
|
||||
|
||||
### Performance
|
||||
|
||||
While the directive composition API offers a powerful tool for reusing common behaviors, excessive
|
||||
use of host directives can impact your application's memory use. If you create components or
|
||||
directives that use _many_ host directives, you may inadvertently balloon the memory used by your
|
||||
application.
|
||||
|
||||
The following example shows a component that applies several host directives.
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
hostDirectives: [
|
||||
DisabledState,
|
||||
RequiredState,
|
||||
ValidationState,
|
||||
ColorState,
|
||||
RippleBehavior,
|
||||
],
|
||||
})
|
||||
export class CustomCheckbox { }
|
||||
```
|
||||
|
||||
This example declares a custom checkbox component that includes five host directives. This
|
||||
means that Angular will create six objects each time a `CustomCheckbox` renders— one for the
|
||||
component and one for each host directive. For a few checkboxes on a page, this won't pose any
|
||||
significant issues. However, if your page renders _hundreds_ of checkboxes, such as in a table, then
|
||||
you could start to see an impact of the additional object allocations. Always be sure to profile
|
||||
your application to determine the right composition pattern for your use case.
|
||||
@@ -269,6 +269,11 @@
|
||||
"url": "guide/structural-directives",
|
||||
"title": "Structural directives",
|
||||
"tooltip": "Structural directives manipulate the layout of the page."
|
||||
},
|
||||
{
|
||||
"url": "guide/directive-composition-api",
|
||||
"title": "Directive composition API",
|
||||
"tooltip": "Apply directive to host elements."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
"!/**/api/common/SelectControlValueAccessor-*",
|
||||
"!/**/api/common/index/MaxLengthValidator-*",
|
||||
"!/**/cookbook/ts-to-js*",
|
||||
"!/analytics/{0,1}",
|
||||
"!/apf/{0,1}",
|
||||
"!/api/*/*-(class|decorator|directive|function|interface|let|pipe|type|type-alias|var)",
|
||||
"!/api/*/testing/*-(class|decorator|directive|function|interface|let|pipe|type|type-alias|var)",
|
||||
@@ -102,6 +103,7 @@
|
||||
"!/api/testing/*-*",
|
||||
"!/api/upgrade/*/*-(class|decorator|directive|function|interface|let|pipe|type|type-alias|var)",
|
||||
"!/api/upgrade/*/index/*",
|
||||
"!/cli/usage-analytics-gathering/{0,1}",
|
||||
"!/config/solution-tsconfig/{0,1}",
|
||||
"!/config/tsconfig/{0,1}",
|
||||
"!/devtools/{0,1}",
|
||||
|
||||
@@ -333,8 +333,13 @@ export interface Directive {
|
||||
|
||||
/**
|
||||
* Standalone directives that should be applied to the host whenever the directive is matched.
|
||||
* By default none of the inputs or outputs of the host directives will be available on the host,
|
||||
* By default, none of the inputs or outputs of the host directives will be available on the host,
|
||||
* unless they are specified in the `inputs` or `outputs` properties.
|
||||
*
|
||||
* You can additionally alias inputs and outputs by putting a colon and the alias after the
|
||||
* original input or output name. For example, if a directive applied via `hostDirectives`
|
||||
* defines an input named `menuDisabled`, you can alias this to `disabled` by adding
|
||||
* `'menuDisabled: disabled'` as an entry to `inputs`.
|
||||
*/
|
||||
hostDirectives?: (Type<unknown>|{
|
||||
directive: Type<unknown>,
|
||||
|
||||
Reference in New Issue
Block a user