11import { reflectComponentType } from '@angular/core'
22import type {
3+ Binding ,
34 ComponentMirror ,
45 Injector ,
56 InputSignal ,
67 OutputEmitterRef ,
78 Type ,
9+ createComponent ,
810} from '@angular/core'
911
10- interface FlexRenderRequiredInputOptions <
12+ type CreateComponentOptions = Parameters < typeof createComponent > [ 1 ]
13+ type CreateComponentBindings = CreateComponentOptions [ 'bindings' ]
14+ type CreateComponentDirectives = CreateComponentOptions [ 'directives' ]
15+
16+ interface FlexRenderOptions <
1117 TInputs extends Record < string , any > ,
1218 TOutputs extends Record < string , any > ,
1319> {
1420 /**
15- * Component instance inputs.
16- * They will be set via [componentRef.setInput API](https://angular.dev/api/core/ComponentRef#setInput)
21+ * Native Angular bindings applied at component creation time via `createComponent`.
22+ * Use this option to set inputs, outputs, or two-way bindings at creation time.
23+ * Shouldn't be used together with {@link FlexRenderOptions#inputs} or {@link FlexRenderOptions#outputs} option.
24+ *
25+ * Binding input/outputs at creation time: {@link https://angular.dev/guide/components/programmatic-rendering#binding-inputs-outputs-and-setting-host-directives-at-creation}
26+ *
27+ * Two-way binding: {@link https://angular.dev/api/core/twoWayBinding}
28+ *
29+ * Output binding: {@link https://angular.dev/api/core/outputBinding}
30+ *
31+ * Input binding: {@link https://angular.dev/api/core/inputBinding}
32+ *
33+ * @example
34+ * ```ts
35+ * import {flexRenderComponent} from '@tanstack/angular-table';
36+ * flexRenderComponent(MyComponent, {
37+ * bindings: [
38+ * // Will update `value` input every time `mySignalValue` changes
39+ * inputBinding('value', mySignalValue),
40+ * // Set myProperty to 1 when the component is created
41+ * inputBinding('myProperty', () => 1),
42+ * // Callback called every time `valueChange` output emit
43+ * outputBinding('valueChange', value => {
44+ * console.log("my value changed to", value)
45+ * }),
46+ * // Two-way binding between `value` input and `valueChange` output
47+ * // Useful while using `model` inputs.
48+ * twoWayBinding('value', mySignal)
49+ * ]
50+ * })
51+ * ```
1752 */
18- inputs : TInputs
53+ readonly bindings ?: Array < Binding >
1954 /**
20- * Component instance outputs.
55+ * Directives to apply to the component at creation time.
56+ *
57+ * Binding directives at creation time: {@link https://angular.dev/guide/components/programmatic-rendering#binding-inputs-outputs-and-setting-host-directives-at-creation}
58+ *
59+ * Two-way binding: {@link https://angular.dev/api/core/twoWayBinding}
60+ *
61+ * Output binding: {@link https://angular.dev/api/core/outputBinding}
62+ *
63+ * Input binding: {@link https://angular.dev/api/core/inputBinding}
64+ *
65+ * @example
66+ * ```ts
67+ * import {flexRenderComponent} from '@tanstack/angular-table';
68+ * flexRenderComponent(MyComponent, {
69+ * bindings: [
70+ * // ...
71+ * ],
72+ * directives: [
73+ * DirectiveA,
74+ * {
75+ * type: DirectiveB,
76+ * bindings: [
77+ * inputBinding('value', mySignalValue),
78+ * // ...
79+ * ]
80+ * }
81+ * ]
82+ * })
83+ * ```
2184 */
22- outputs ?: TOutputs
23- /**
24- * Optional {@link Injector} that will be used when rendering the component
25- */
26- injector ?: Injector
27- }
28-
29- interface FlexRenderOptions <
30- TInputs extends Record < string , any > ,
31- TOutputs extends Record < string , any > ,
32- > {
85+ readonly directives ?: CreateComponentDirectives
3386 /**
3487 * Component instance inputs.
35- * They will be set via [componentRef.setInput API](https://angular.dev/api/core/ComponentRef#setInput)
88+ *
89+ * These values are assigned after the component has been created using
90+ * [componentRef.setInput API](https://angular.dev/api/core/ComponentRef#setInput).
91+ *
92+ * Shouldn't be used together with {@link FlexRenderOptions#bindings} option
3693 */
37- inputs ?: TInputs
94+ readonly inputs ?: TInputs
3895 /**
3996 * Component instance outputs.
97+ *
98+ * Outputs are wired imperatively after component creation using {@link OutputEmitterRef#subscribe}.
99+ *
100+ * Shouldn't be used together with {@link FlexRenderOptions#bindings} option
40101 */
41- outputs ?: TOutputs
102+ readonly outputs ?: TOutputs
42103 /**
43104 * Optional {@link Injector} that will be used when rendering the component
44105 */
45- injector ?: Injector
106+ readonly injector ?: Injector
46107}
47108
48109type Inputs < T > = {
@@ -59,41 +120,145 @@ type Outputs<T> = {
59120 : never
60121}
61122
62- type OptionalKeys < T , K = keyof T > = K extends keyof T
63- ? T [ K ] extends Required < T > [ K ]
64- ? undefined extends T [ K ]
65- ? K
66- : never
67- : K
68- : never
69-
70123/**
71- * Helper function to create a [@link FlexRenderComponent] instance, with better type-safety.
124+ * Helper function to create a {@link FlexRenderComponent} instance, with better type-safety.
125+ *
126+ * @example
127+ * ```ts
128+ * import {flexRenderComponent} from '@tanstack/angular-table'
129+ * import {inputBinding, outputBinding} from '@angular/core';
72130 *
73- * - options object must be passed when the given component instance contains at least one required signal input.
74- * - options/inputs is typed with the given component inputs
75- * - options/outputs is typed with the given component outputs
131+ * const columns = [
132+ * {
133+ * cell: ({ row }) => {
134+ * return flexRenderComponent(MyComponent, {
135+ * inputs: { value: mySignalValue() },
136+ * outputs: { valueChange: (val) => {} }
137+ * // or using angular native createComponent#binding api
138+ * bindings: [
139+ * inputBinding('value', mySignalValue),
140+ * outputBinding('valueChange', value => {
141+ * console.log("my value changed to", value)
142+ * })
143+ * ]
144+ * })
145+ * },
146+ * },
147+ * ]
148+ * ```
76149 */
77- export function flexRenderComponent <
78- TComponent = any ,
79- TInputs extends Inputs < TComponent > = Inputs < TComponent > ,
80- TOutputs extends Outputs < TComponent > = Outputs < TComponent > ,
81- > (
150+ export function flexRenderComponent < TComponent = any > (
82151 component : Type < TComponent > ,
83- ...options : OptionalKeys < TInputs > extends never
84- ? [ FlexRenderOptions < TInputs , TOutputs > ?]
85- : [ FlexRenderRequiredInputOptions < TInputs , TOutputs > ]
152+ options ?: FlexRenderOptions < Inputs < TComponent > , Outputs < TComponent > > ,
86153) : FlexRenderComponent < TComponent > {
87- const { inputs, injector, outputs } = options [ 0 ] ?? { }
88- return new FlexRenderComponent ( component , inputs , injector , outputs )
154+ const { inputs, injector, outputs, directives, bindings } = options ?? { }
155+ return new FlexRenderComponentInstance (
156+ component ,
157+ inputs ,
158+ injector ,
159+ outputs ,
160+ directives ,
161+ bindings ,
162+ )
163+ }
164+
165+ /**
166+ * Wrapper interface for a component that will be used as content for {@link FlexRenderDirective}.
167+ * Can be created using {@link flexRenderComponent} helper.
168+ *
169+ * @example
170+ *
171+ * ```ts
172+ * import {flexRenderComponent} from '@tanstack/angular-table'
173+ *
174+ * // Usage in cell/header/footer definition
175+ * const columns = [
176+ * {
177+ * cell: ({ row }) => {
178+ * return flexRenderComponent(MyComponent, {
179+ * inputs: { value: mySignalValue() },
180+ * outputs: { valueChange: (val) => {} }
181+ * // or using angular createComponent#bindings api
182+ * bindings: [
183+ * inputBinding('value', mySignalValue),
184+ * outputBinding('valueChange', value => {
185+ * console.log("my value changed to", value)
186+ * })
187+ * ]
188+ * })
189+ * },
190+ * },
191+ * ]
192+ *
193+ * import {input, output} from '@angular/core';
194+ *
195+ * @Component ({
196+ * selector: 'my-component',
197+ * })
198+ * class MyComponent {
199+ * readonly value = input(0);
200+ * readonly valueChange = output<number>();
201+ * }
202+ *
203+ * ```
204+ */
205+ export interface FlexRenderComponent < TComponent = any > {
206+ /**
207+ * The component type
208+ */
209+ readonly component : Type < TComponent >
210+ /**
211+ * Reflected metadata about the component.
212+ */
213+ readonly mirror : ComponentMirror < TComponent >
214+ /**
215+ * List of allowed input names.
216+ */
217+ readonly allowedInputNames : Array < string >
218+ /**
219+ * List of allowed output names.
220+ */
221+ readonly allowedOutputNames : Array < string >
222+ /**
223+ * Component instance outputs. Subscribed via {@link OutputEmitterRef#subscribe}
224+ *
225+ * @see {@link FlexRenderOptions#outputs }
226+ */
227+ readonly outputs ?: Outputs < TComponent >
228+ /**
229+ * Component instance inputs. Set via [componentRef.setInput API](https://angular.dev/api/core/ComponentRef#setInput))
230+ *
231+ * @see {@link FlexRenderOptions#inputs }
232+ */
233+ readonly inputs ?: Inputs < TComponent >
234+ /**
235+ * Optional {@link Injector} that will be used when rendering the component.
236+ *
237+ * @see {@link FlexRenderOptions#injector }
238+ */
239+ readonly injector ?: Injector
240+ /**
241+ * Bindings to apply to the root component
242+ *
243+ * @see {@link FlexRenderOptions#bindings }
244+ */
245+ bindings ?: CreateComponentBindings
246+ /**
247+ * Directives that should be applied to the component.
248+ *
249+ * @see {FlexRenderOptions#directives}
250+ */
251+ directives ?: CreateComponentDirectives
89252}
90253
91254/**
92255 * Wrapper class for a component that will be used as content for {@link FlexRenderDirective}
93256 *
94257 * Prefer {@link flexRenderComponent} helper for better type-safety
95258 */
96- export class FlexRenderComponent < TComponent = any > {
259+ export class FlexRenderComponentInstance <
260+ TComponent = any ,
261+ > implements FlexRenderComponent < TComponent > {
97262 readonly mirror : ComponentMirror < TComponent >
98263 readonly allowedInputNames : Array < string > = [ ]
99264 readonly allowedOutputNames : Array < string > = [ ]
@@ -103,6 +268,8 @@ export class FlexRenderComponent<TComponent = any> {
103268 readonly inputs ?: Inputs < TComponent > ,
104269 readonly injector ?: Injector ,
105270 readonly outputs ?: Outputs < TComponent > ,
271+ readonly directives ?: CreateComponentDirectives ,
272+ readonly bindings ?: CreateComponentBindings ,
106273 ) {
107274 const mirror = reflectComponentType ( component )
108275 if ( ! mirror ) {
0 commit comments