diff --git a/adev/src/content/guide/components/programmatic-rendering.md b/adev/src/content/guide/components/programmatic-rendering.md index 2aee3823c84..0b431d4c9cb 100644 --- a/adev/src/content/guide/components/programmatic-rendering.md +++ b/adev/src/content/guide/components/programmatic-rendering.md @@ -41,6 +41,159 @@ export class CustomDialog { } ``` +### Passing inputs to dynamically rendered components + +You can pass inputs to the dynamically rendered component using the `ngComponentOutletInputs` property. This property accepts an object where keys are input names and values are the input values. + +```angular-ts +@Component({ + selector: 'user-greeting', + template: ` +
+

User: {{ username() }}

+

Role: {{ role() }}

+
+ `, +}) +export class UserGreeting { + username = input.required(); + role = input('guest'); +} + +@Component({ + selector: 'profile-view', + imports: [NgComponentOutlet], + template: ` + + ` +}) +export class ProfileView { + greetingComponent = UserGreeting; + greetingInputs = signal({ username: 'ngAwesome' , role: 'admin' }); +} +``` + +The inputs are updated whenever the `greetingInputs` signal changes, keeping the dynamic component in sync with the parent's state. + +### Providing content projection + +Use `ngComponentOutletContent` to pass projected content to the dynamically rendered component. This is useful when the dynamic component uses `` to display content. + +```angular-ts +@Component({ + selector: 'card-wrapper', + template: ` +
+ +
+ ` +}) +export class CardWrapper { } + +@Component({ + imports: [NgComponentOutlet], + template: ` + + + +

Dynamic Content

+

This content is projected into the card.

+
+ ` +}) +export class DynamicCard { + private vcr = inject(ViewContainerRef); + cardComponent = CardWrapper; + + private contentTemplate = viewChild>('contentTemplate'); + + cardContent = computed(() => { + const template = this.contentTemplate(); + if (!template) return []; + // Returns an array of projection slots. Each element represents one slot. + // CardWrapper has one , so we return an array with one element. + return [this.vcr.createEmbeddedView(template).rootNodes]; + }); +} +``` + +### Providing injectors + +You can provide a custom injector to the dynamically created component using `ngComponentOutletInjector`. This is useful for providing component-specific services or configuration. + +```angular-ts +export const THEME_DATA = new InjectionToken('THEME_DATA', { + factory: () => 'light', +}); + +@Component({ + selector: 'themed-panel', + template: `
...
` +}) +export class ThemedPanel { + theme = inject(THEME_DATA); +} + +@Component({ + selector: 'dynamic-panel', + imports: [NgComponentOutlet], + template: ` + + ` +}) +export class DynamicPanel { + panelComponent = ThemedPanel; + + customInjector = Injector.create({ + providers: [ + { provide: THEME_DATA, useValue: 'dark' } + ], + }); +} +``` + +### Accessing the component instance + +You can access the dynamically created component's instance using the directive's `exportAs` feature: + +```angular-ts +@Component({ + selector: 'counter', + template: `

Count: {{count()}}

` +}) +export class Counter { + count = signal(0); + increment() { + this.count.update(c => c + 1); + } +} + +@Component({ + imports: [NgComponentOutlet], + template: ` + + + + ` +}) +export class CounterHost { + counterComponent = Counter; +} +``` + +NOTE: The `componentInstance` property is `null` before the component is rendered. + See the [NgComponentOutlet API reference](api/common/NgComponentOutlet) for more information on the directive's capabilities.