Function: useFieldComposer()
ts
function useFieldComposer<T>(
commonProps: ToRefs<
LooseRequired<Readonly<CommonResourcefulFieldPublicProps>>
>,
stringifier: MaybeRef<RFieldStringifier<T>>,
singleFieldRenderer: MaybeRef<(...args: SingleVariantSlotArgs) => VNode>,
emit: EmitWithUpdateModelValue<T>,
onUpdateModelValueCamelProp: (
val: T | T[] | null | undefined,
) => void | undefined,
onUpdateModelValueKebabProp: (
val: T | T[] | null | undefined,
) => void | undefined,
modelValueRef: Ref<T | T[] | null | undefined>,
modelValueProp: T | T[] | null | undefined,
nullIsValue: MaybeRef<boolean>,
undefinedIsValue: MaybeRef<boolean>,
onChange: (
is: LocalStateModelValue<T>,
was?: LocalStateModelValue<T>,
) => void,
callRefreshChoices?: () => void | Promise<void>,
): {
composed: () => VNode<
RendererNode,
RendererElement,
{
[key: string]: any;
}
> | null;
getLocalState: () => any;
localState: WritableComputedRef<any, T | T[] | null | undefined>;
recomputeLocalState: () => void;
setLocalState: (val: T | T[] | null | undefined) => void;
};A sophisticated composable that unifies field rendering across three operational modes: single field editing, multiple selection management, and read-only display.
This composer eliminates the complexity of managing different field states by providing a unified interface. Individual field components only need to implement their single field renderer, and the composer automatically handles multiple selection and read mode.
Operational Modes
- Single Mode (
multiple: false,renderMode: 'edit'): Direct single field rendering - Multiple Mode (
multiple: true,renderMode: 'edit'): RMultipleField with single renderer as slot - Read Mode (
renderMode: 'read'): RReadModeRenderer with Oxford comma formatting
Key Benefits
- Unified Configuration: All options consolidated in commonProps - no separate option objects
- Unified State Management: Automatic local state sync with proper emit handling
- Consistent Interface: Same slot args pattern across all modes
- Type Safety: Full generic type propagation with proper TypeScript support
- Event Delegation: Complete event handling with prop binding automation
- Zero Boilerplate: Individual fields become trivial to implement
Simplified Architecture
typescript
// Individual field becomes this simple:
const { composed } = useFieldComposer(
toRefs(props), // All options unified in commonProps!
stringifier,
singleRenderer, // Just focus on single field logic!
emit,
// ... standard parameters
);
return () => composed();Type Parameters
| Type Parameter | Description |
|---|---|
T | The data type managed by the field (string, number, User, etc.) |
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
commonProps | ToRefs<LooseRequired<Readonly<CommonResourcefulFieldPublicProps>>> | undefined | Reactive common field properties including all mode options (renderMode, multiple, variant, theme, to, href, events, etc.) |
stringifier | MaybeRef<RFieldStringifier<T>> | undefined | Function to convert values to display format (supports VNode + string) |
singleFieldRenderer | MaybeRef<(...args: SingleVariantSlotArgs) => VNode> | undefined | The core field renderer using SingleVariantSlotArgs interface |
emit | EmitWithUpdateModelValue<T> | undefined | Vue emit function for modelValue updates |
onUpdateModelValueCamelProp | (val: T | T[] | null | undefined) => void | undefined | undefined | Optional camelCase prop event handler |
onUpdateModelValueKebabProp | (val: T | T[] | null | undefined) => void | undefined | undefined | Optional kebab-case prop event handler |
modelValueRef | Ref<T | T[] | null | undefined> | undefined | Reactive reference to the current model value |
modelValueProp | T | T[] | null | undefined | undefined | The initial model value prop |
nullIsValue | MaybeRef<boolean> | false | Whether null should be treated as a valid value (default: false) |
undefinedIsValue | MaybeRef<boolean> | false | Whether undefined should be treated as a valid value (default: false) |
onChange | (is: LocalStateModelValue<T>, was?: LocalStateModelValue<T>) => void | ... | Optional callback for state change tracking |
callRefreshChoices? | () => void | Promise<void> | undefined | - |
Returns
ts
{
composed: () =>
| VNode<RendererNode, RendererElement, {
[key: string]: any;
}>
| null;
getLocalState: () => any;
localState: WritableComputedRef<any, T | T[] | null | undefined>;
recomputeLocalState: () => void;
setLocalState: (val: T | T[] | null | undefined) => void;
}Object with state management functions and the composed renderer
| Name | Type |
|---|---|
composed() | () => | VNode<RendererNode, RendererElement, { [key: string]: any; }> | null |
getLocalState() | () => any |
localState | WritableComputedRef<any, T | T[] | null | undefined> |
recomputeLocalState() | () => void |
setLocalState() | (val: T | T[] | null | undefined) => void |
Example
typescript
// String field implementation with unified props
export const RStringField = defineComponent({
name: "RStringField",
props: {
...makeCommonResourcefulFieldProps(),
// Add any field-specific props here
},
emits: commonEmits,
setup(props, { emit }) {
const stringStringifier: RFieldStringifier<string> = (val) => val || "";
const stringRenderer = ({ props }: SingleVariantSlotArgs<string>[0]) =>
h(VTextField, props);
const { composed } = useFieldComposer(
toRefs(props), // All mode options included automatically!
stringStringifier,
stringRenderer,
emit,
props["onUpdate:modelValue"],
props["onUpdate:model-value"],
toRef(props, "modelValue"),
props.modelValue,
false, // nullIsValue - null is not a valid string value
false, // undefinedIsValue - undefined is not a valid string value
);
return () => composed();
},
});