Skip to content

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

  1. Single Mode (multiple: false, renderMode: 'edit'): Direct single field rendering
  2. Multiple Mode (multiple: true, renderMode: 'edit'): RMultipleField with single renderer as slot
  3. 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 ParameterDescription
TThe data type managed by the field (string, number, User, etc.)

Parameters

ParameterTypeDefault valueDescription
commonPropsToRefs<LooseRequired<Readonly<CommonResourcefulFieldPublicProps>>>undefinedReactive common field properties including all mode options (renderMode, multiple, variant, theme, to, href, events, etc.)
stringifierMaybeRef<RFieldStringifier<T>>undefinedFunction to convert values to display format (supports VNode + string)
singleFieldRendererMaybeRef<(...args: SingleVariantSlotArgs) => VNode>undefinedThe core field renderer using SingleVariantSlotArgs interface
emitEmitWithUpdateModelValue<T>undefinedVue emit function for modelValue updates
onUpdateModelValueCamelProp(val: T | T[] | null | undefined) => void | undefinedundefinedOptional camelCase prop event handler
onUpdateModelValueKebabProp(val: T | T[] | null | undefined) => void | undefinedundefinedOptional kebab-case prop event handler
modelValueRefRef<T | T[] | null | undefined>undefinedReactive reference to the current model value
modelValuePropT | T[] | null | undefinedundefinedThe initial model value prop
nullIsValueMaybeRef<boolean>falseWhether null should be treated as a valid value (default: false)
undefinedIsValueMaybeRef<boolean>falseWhether 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

NameType
composed()() => | VNode<RendererNode, RendererElement, { [key: string]: any; }> | null
getLocalState()() => any
localStateWritableComputedRef<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();
  },
});