vue-json-ui-editor

repository·main·Indexed 19 days ago

https://github.com/yourtion/vue-json-ui-editor

A Vue 3 JSON Schema based form editor component with TypeScript support. It allows developers to build complex UI forms from JSON schemas and includes built-in support for UI libraries like element-plus. The library provides a JsonEditor component for rendering forms, a static setComponent API for custom component registration, and a standalone createArrayRenderer utility for managing object-array fields.

Tokens
6.5K
Snippets
16
Records
23
Agent score
68%

What's inside vue-json-ui-editor

  1. How JSON Schema features are rendered

    main

    The editor interprets several JSON Schema features to control UI behavior:

    • Read-only/Disabled: disabled: true or readOnly: true on a property will disable the input (readOnly also implies disabled).
    • Nested Objects: type: 'object' with properties renders in a sub-container using the object's title (as .sub-title) and description (as .sub-description).
    • Object Arrays: type: 'array' with items: { type: 'object', properties } renders as an editable list. Each row includes a remove button. Add/remove buttons are controlled by the arrayadd and arrayremove component types.
    • Choice Arrays: array types combined with enum, oneOf, or anyOf render as a single selection control (e.g., select, radio, or checkbox group).
  2. Wire specific widgets via schema `attrs`

    main

    You can drive specific widget selection from the JSON Schema using attrs.type. This allows you to register a component once globally and then apply it to specific fields in your schema.

    1. Register the custom type: JsonEditor.setComponent('my-type', 'MyComponent')
    2. Use it in schema: { fieldName: { type: 'boolean', attrs: { type: 'my-type' } } }
    JsonEditor.setComponent('switch', 'el-switch');
    JsonEditor.setComponent('date', 'el-date-picker');
    
    // schema:
    {
      active: { type: 'boolean', attrs: { type: 'switch' } },
      createdAt: { type: 'string', format: 'date-time', attrs: { type: 'date' } }
    }
    JsonEditor.setComponent('switch', 'el-switch');
    JsonEditor.setComponent('date', 'el-date-picker');
    // schema:
    { active: { type: 'boolean', attrs: { type: 'switch' } } }
    { createdAt: { type: 'string', format: 'date-time', attrs: { type: 'date' } } }
  3. Basic usage of JsonEditor

    main

    To use the editor, import JsonEditor and provide a schema and a v-model for two-way data binding. You can use a template ref to access the editor's methods for validation and resetting.

    <template>
      <json-editor ref="jsonEditorRef" :schema="schema" v-model="model">
        <el-button type="primary" @click="submit">Submit</el-button>
        <el-button @click="reset">Reset</el-button>
      </json-editor>
    </template>
    
    <script setup lang="ts">
    import { ref } from 'vue';
    import JsonEditor from 'vue-json-ui-editor';
    
    const schema = {
      type: 'object',
      title: 'vue-json-editor demo',
      properties: {
        name: { type: 'string' },
        email: { type: 'string' },
      },
    };
    
    const model = ref({ name: 'Yourtion' });
    const jsonEditorRef = ref<InstanceType<typeof JsonEditor>>();
    
    function submit() {
      // jsonEditorRef.value?.form() returns the underlying element-plus form instance
      jsonEditorRef.value?.form().validate((valid: boolean) => {
        if (!valid) {
          jsonEditorRef.value?.setErrorMessage('Please fill out the required fields');
        }
      });
    }
    
    function reset() {
      jsonEditorRef.value?.reset();
    }
    </script>
  4. JsonEditor Instance Methods

    main

    The following methods are exposed via a template ref (e.g., jsonEditorRef.value):

    • input(name): Get a form input reference.
    • form(): Get the rendered form component instance (e.g., the element-plus el-form), allowing calls to .validate() or .resetFields().
    • checkValidity(): Returns boolean indicating if the form satisfies constraints.
    • validate(): Async validation shortcut; delegates to the underlying form component's validate().
    • reset(): Resets all elements to the initial modelValue.
    • setErrorMessage(message): Sets an error message (rendered via the error component type).
    • clearErrorMessage(): Clears the error message.
    • getFields(): Returns the current parsed field tree (including $sub containers for nested objects).
    • vm: The reactive view-model ({ model, fields, error }) for advanced usage.
  5. Register components globally with JsonEditor.setComponent

    main

    By default, json-editor renders with native HTML elements. To use a UI library like element-plus, use the static JsonEditor.setComponent API to register components for specific field types (e.g., text, select, form, label, error).

    option can be a plain object or a factory callback ({ vm, field, item }) => propsObject.

    Important for element-plus: Since el-form-item reads labels from the label prop rather than the default slot, the label registration callback must return label: field.label and prop: field.name.

    JsonEditor.setComponent('text', 'el-input');
    JsonEditor.setComponent('form', 'el-form', ({ vm }) => ({ model: vm.model, rules: {} }));
    JsonEditor.setComponent('error', 'el-alert', ({ vm }) => ({ type: 'error', title: vm.error }));
    
    // Special handling for element-plus labels
    JsonEditor.setComponent('label', 'el-form-item', ({ field }) => ({ 
      label: field.label, 
      prop: field.name 
    }));
    JsonEditor.setComponent('text', 'el-input');
    JsonEditor.setComponent('form', 'el-form', ({ vm }) => ({ model: vm.model, rules: {} }));
    JsonEditor.setComponent('error', 'el-alert', ({ vm }) => ({ type: 'error', title: vm.error }));
  6. Reuse the array renderer with createArrayRenderer

    main

    The logic for object-array add/remove operations is available as a standalone, framework-agnostic module. This is useful for unit testing or custom implementations.

    Use createArrayRenderer by providing ArrayRendererDeps (which includes model, onChange, and component resolution helpers).

    import { createArrayRenderer, type ArrayRendererDeps } from 'vue-json-ui-editor';
    
    const renderer = createArrayRenderer({
      model, 
      onChange, 
      getComp, 
      resolveComp, 
      elementOptions, 
      wrapChild, 
      renderInput,
    } satisfies ArrayRendererDeps);
    
    renderer.render(field, fieldName);   // → returns vnode[] for the whole array (header + rows)
    renderer.addRow(fieldName);
    renderer.removeRow(fieldName, index);
    import { createArrayRenderer, type ArrayRendererDeps } from 'vue-json-ui-editor';
    
    const renderer = createArrayRenderer({
      model, onChange, getComp, resolveComp, elementOptions, wrapChild, renderInput,
    } satisfies ArrayRendererDeps);
    renderer.render(field, fieldName);   // → vnode[] for the whole array (header + rows)
    renderer.addRow(fieldName);
    renderer.removeRow(fieldName, index);
  7. JsonEditor Events Reference

    main

    The following events are emitted by the <json-editor> component:

    • update:modelValue: Emitted whenever a field value changes (used for v-model).
    • change: Fired when a change to an element's value is committed by the user.
    • submit: Fired when the form is submitted and passes validation.
    • invalid: Fired when a submittable element is checked and fails constraints.
  8. JsonEditor Props Reference

    main

    The following props are available on the <json-editor> component:

    PropTypeRequiredDefaultDescription
    schemaObjectYes-The JSON Schema object. Use v-if to load asynchronously.
    v-model / modelValueObjectNo{}Two-way binding for the form data.
    auto-completeStringNo-Browser auto-completion: off or on.
    no-validateBooleanNofalseIf true, the form is not validated when submitted.
    input-wrapping-classStringNo-Wraps controls in a <div class="...">. Set to undefined to disable.
    componentsObjectNoundefinedPer-instance component overrides. Merged over global defaults.
  9. TypeScript types for vue-json-ui-editor

    main

    The package includes full TypeScript support. You can import the following types directly:

    import JsonEditor, { type JsonSchema } from 'vue-json-ui-editor';
    
    // v3.1+ types:
    import type {
      JsonEditorStatic,   // Signature for setComponent
      JsonEditorInstance, // For ref<InstanceType<typeof JsonEditor>>
      ComponentConfig, 
      OptionContext, 
      VmContext, 
      ComponentsMap,
      ArrayRendererDeps,  // For createArrayRenderer
    } from 'vue-json-ui-editor';
    import JsonEditor, { type JsonSchema } from 'vue-json-ui-editor';
    // newly exported types (v3.1+):
    import type {
      JsonEditorStatic,   // the setComponent static method signature
      JsonEditorInstance, // exposed instance methods (form/validate/reset/getFields/vm)
      ComponentConfig, OptionContext, VmContext, ComponentsMap,
      ArrayRendererDeps,  // for createArrayRenderer
    } from 'vue-json-ui-editor';
  10. Create a custom array renderer with createArrayRenderer

    main

    The createArrayRenderer factory function allows you to create a standalone module for rendering array fields (specifically object arrays with items: {type: 'object', properties: ...}). This is useful for decoupling array rendering logic from the main component lifecycle and enabling independent testing or reuse.

    To use it, you must provide a deps object containing the necessary component-side dependencies (model, change handlers, component resolvers, etc.). Because the renderer and the main input rendering logic (renderInput) are mutually recursive, you typically pass a mutable dependency object where renderInput is filled in after the initial renderer creation.

    // 1. Define the dependencies with a placeholder for renderInput
    const deps: ArrayRendererDeps = {
      model: myModel,
      onChange: () => { /* notify changes */ },
      getComp: (key) => myComponentConfigs[key],
      resolveComp: (config) => myComponentResolver(config),
      elementOptions: (config, ext, field, item) => myOptionCalculator(config, ext, field, item),
      wrapChild: (config, child) => myChildWrapper(config, child),
      renderInput: (field, name) => [] // Placeholder to be filled later
    };
    
    // 2. Create the renderer
    const renderer = createArrayRenderer(deps);
    
    // 3. Fill the placeholder with the actual recursive call
    // (In the context of the main JsonEditor component)
    // deps.renderInput = (field, name) => {
    //   if (field.type === 'arrayitems') return renderer.render(field, name);
    //   ... other field types
    // };
  11. Register custom components via JsonEditor.setComponent

    main

    You can extend the editor's capabilities by registering new component types globally on the JsonEditor object using the setComponent static method. This allows you to map specific JSON schema types to custom Vue components.

    Usage: JsonEditor.setComponent(type, component, option)

    import { JsonEditor } from 'vue-json-ui-editor';
    import MyCustomInput from './MyCustomInput.vue';
    
    // Register 'MyCustomInput' for the 'special-text' type
    JsonEditor.setComponent('special-text', MyCustomInput, { 
      // component options here 
    });