Skip to content

Commit 88a15be

Browse files
SkyZeroZxAndrewKushnir
authored andcommitted
docs: Add programmatic component input/output/directive binding (angular#64240)
Expands the guide to explain how inputs, outputs, and directives can be provided when creating components programmatically PR Close angular#64240
1 parent d73d9ac commit 88a15be

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

adev/src/content/guide/components/programmatic-rendering.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,117 @@ export class AdminSettings {
139139
```
140140

141141
The example above loads and displays the `AdvancedSettings` upon receiving a button click.
142+
143+
## Binding inputs, outputs and setting host directives at creation
144+
145+
When dynamically creating components, manually setting inputs and subscribing to outputs can be error-prone. You often need to write extra code just to wire up bindings after the component is instantiated.
146+
147+
To simplify this, both `createComponent` and `ViewContainerRef.createComponent` support passing a `bindings` array with helpers like `inputBinding()`, `outputBinding()`, and `twoWayBinding()` to configure inputs and outputs up front. You can also specify a `directives` array to apply any host directives. This enables creating components programmatically with template-like bindings in a single, declarative call.
148+
149+
### Host view using `ViewContainerRef.createComponent`
150+
151+
`ViewContainerRef.createComponent` creates a component and automatically inserts its host view and host element into the container’s view hierarchy at the container’s location. Use this when the dynamic component should become part of the container’s logical and visual structure (for example, adding list items or inline UI).
152+
153+
By contrast, the standalone `createComponent` API does not attach the new component to any existing view or DOM location — it returns a `ComponentRef` and gives you explicit control over where to place the component’s host element.
154+
155+
```angular-ts
156+
import { Component, input, model, output } from "@angular/core";
157+
158+
@Component({
159+
selector: 'app-warning',
160+
template: `
161+
@if(isExpanded()) {
162+
<section>
163+
<p>Warning: Action needed!</p>
164+
<button (click)="close.emit(true)">Close</button>
165+
</section>
166+
}
167+
`
168+
})
169+
export class AppWarningComponent {
170+
readonly canClose = input.required<boolean>();
171+
readonly isExpanded = model<boolean>();
172+
readonly close = output<boolean>();
173+
}
174+
```
175+
176+
```ts
177+
import { Component, ViewContainerRef, signal, inputBinding, outputBinding, twoWayBinding, inject } from '@angular/core';
178+
import { FocusTrap } from "@angular/cdk/a11y";
179+
import { ThemeDirective } from '../theme.directive';
180+
181+
@Component({
182+
template: `<ng-container #container />`
183+
})
184+
export class HostComponent {
185+
private vcr = inject(ViewContainerRef);
186+
readonly canClose = signal(true);
187+
readonly isExpanded = signal(true);
188+
189+
showWarning() {
190+
const compRef = this.vcr.createComponent(AppWarningComponent, {
191+
bindings: [
192+
inputBinding('canClose', this.canClose),
193+
twoWayBinding('isExpanded', this.isExpanded),
194+
outputBinding<boolean>('close', (confirmed) => {
195+
console.log('Closed with result:', confirmed);
196+
})
197+
],
198+
directives: [
199+
FocusTrap,
200+
{ type: ThemeDirective, bindings: [inputBinding('theme', () => 'warning')] }
201+
]
202+
});
203+
}
204+
}
205+
```
206+
207+
In the example above, the dynamic **AppWarningComponent** is created with its `canClose` input bound to a reactive signal, a two-way binding on its `isExpanded` state, and an output listener for `close`. The `FocusTrap` and `ThemeDirective` are attached to the host element via `directives`.
208+
209+
### Popup attached to `document.body` with `createComponent` + `hostElement`
210+
211+
Use this when rendering outside the current view hierarchy (e.g., overlays). The provided `hostElement` becomes the component’s host in the DOM, so Angular doesn’t create a new element matching the selector. Lets you configure **bindings** directly.
212+
213+
```ts
214+
import {
215+
ApplicationRef,
216+
createComponent,
217+
EnvironmentInjector,
218+
inject,
219+
Injectable,
220+
inputBinding,
221+
outputBinding,
222+
} from '@angular/core';
223+
import { PopupComponent } from './popup.component';
224+
225+
@Injectable({ providedIn: 'root' })
226+
export class PopupService {
227+
private readonly injector = inject(EnvironmentInjector);
228+
private readonly appRef = inject(ApplicationRef);
229+
230+
show(message: string) {
231+
// Create a host element for the popup
232+
const host = document.createElement('popup-host');
233+
234+
// Create the component and bind in one call
235+
const ref = createComponent(PopupComponent, {
236+
environmentInjector: this.injector,
237+
hostElement: host,
238+
bindings: [
239+
inputBinding('message', () => message),
240+
outputBinding('closed', () => {
241+
document.body.removeChild(host);
242+
this.appRef.detachView(ref.hostView);
243+
ref.destroy();
244+
}),
245+
],
246+
});
247+
248+
// Registers the component’s view so it participates in change detection cycle.
249+
this.appRef.attachView(ref.hostView);
250+
// Inserts the provided host element into the DOM (outside the normal Angular view hierarchy).
251+
// This is what makes the popup visible on screen, typically used for overlays or modals.
252+
document.body.appendChild(host);
253+
}
254+
}
255+
```

0 commit comments

Comments
 (0)