Error trying to implement custom vuetify renderer in Vue composition <script setup> syntax

Hello all!

I am working on a project that uses Vue 3 and vuetify that requires me to implement custom renderers.
As a first step a tried coping the vuetify string component renderer and importing it as a custom renderer which worked fine.
The second step involves rewriting the component in vue3 composition syntax and then finally on step 3 replacing the template with vuetify components for the projects needs.

I ran into difficulty after refactoring the existing component to the new syntax and i am getting the following error

displayOnlyRenderer.vue:1 [Vue warn]: Component is missing template or render function.

As stated before the form displays without a problem in the old syntax so i figure the error is not originating from the schema / ui schema / data.

The component in the composition syntax:

<script setup lang="ts">
import { ControlElement } from '@jsonforms/core';
import { RendererProps, useJsonFormsControl } from '@jsonforms/vue';
import { ControlWrapper, useVuetifyControl } from '@jsonforms/vue-vuetify';
import { computed } from 'vue';
import { VCombobox, VHover, VTextField } from 'vuetify/lib/components/index.mjs';
  
  const props = defineProps<RendererProps<ControlElement>>();
  
  const { control, 
          //handleChange,
          styles,
          isFocused,
          appliedOptions,
          controlWrapper,
          onChange,
          vuetifyProps,
          persistentHint,
          computedLabel,
         } =
          useVuetifyControl
          (useJsonFormsControl(props),
          (value) => value || undefined,
          300);
  
  const suggestions = computed(() : string[] | undefined => {
    return undefined;
  });
  </script>
  
  <template>
    <control-wrapper
      v-bind="controlWrapper"
      :styles="styles"
      :isFocused="isFocused"
      :appliedOptions="appliedOptions"
    >
      <v-hover v-slot="{ isHovering }">
        <v-combobox
          v-if="suggestions !== undefined"
          :id="control.id + '-input'"
          :class="styles.control.input"
          :disabled="!control.enabled"
          :autofocus="appliedOptions.focus"
          :placeholder="appliedOptions.placeholder"
          :label='computedLabel'
          :hint="control.description"
          :persistent-hint="persistentHint()"
          :required="control.required"
          :error-messages="control.errors"
          :maxlength="
            appliedOptions.restrict ? control.schema.maxLength : undefined
          "
          :counter="
            control.schema.maxLength !== undefined
              ? control.schema.maxLength
              : undefined
          "
          :clearable="isHovering"
          :model-value="control.data"
          :items="suggestions"
          hide-no-data
          v-bind="vuetifyProps('v-combobox')"
          @update:model-value="onChange"
          @focus="isFocused = true"
          @blur="isFocused = false"
        />
        <v-text-field
          v-else
          :id="control.id + '-input'"
          :class="styles.control.input"
          :disabled="!control.enabled"
          :autofocus="appliedOptions.focus"
          :placeholder="appliedOptions.placeholder"
          :label='computedLabel'
          :hint="control.description"
          :persistent-hint="persistentHint()"
          :required="control.required"
          :error-messages="control.errors"
          :model-value="control.data"
          :maxlength="
            appliedOptions.restrict ? control.schema.maxLength : undefined
          "
          :counter="
            control.schema.maxLength !== undefined
              ? control.schema.maxLength
              : undefined
          "
          :clearable="isHovering"
          v-bind="vuetifyProps('v-text-field')"
          @update:model-value="onChange"
          @focus="isFocused = true"
          @blur="isFocused = false"
        />
      </v-hover>
    </control-wrapper>
  </template>

The renderer is added in the following way -

function buildRendererRegistryEntry(testRenderer: any, controlType: Tester) {
  const entry: JsonFormsRendererRegistryEntry = {
    renderer: testRenderer,
    tester: rankWith(3, controlType),
  };
  return entry;
}

export const displayOnlyCustomRenderer = buildRendererRegistryEntry(DisplayOnlyRenderer, isStringControl);

const renderers = [
  //...vuetifyRenderers,
  displayOnlyCustomRenderer
];
Object.freeze(renderers

Thank you for the help!

Hi @Roi-p,

This seems to be more of a Vue error than one of JSON Forms, especially as it works for you before the rewrite.

Can you show the full code of how you import and register the custom renderer?

Hi,
Thank you for the quick response.

The custom renderer I am trying to implement (The code that is attached in the previous message) is a file named displayOnlyRenderer.vue.

I have another file which is used to create the Registry Entry-
Plugins/Jsonforms/index.ts -

function buildRendererRegistryEntry(testRenderer: any, controlType: Tester) {
  const entry: JsonFormsRendererRegistryEntry = {
    renderer: testRenderer,
    tester: rankWith(3, controlType),
  };
  return entry;
}

export const displayOnlyCustomRenderer = buildRendererRegistryEntry(DisplayOnlyRenderer, isStringControl);

The above code is imported in the form.vue component -

<script setup lang="ts">

import { displayOnlyRenderer } from "../../infrastructure/plugins/jsonforms"

const renderers = [
  //...vuetifyRenderers,
  displayOnlyCustomRenderer
];

Object.freeze(renderers)

...
<template>
  <div>
    <JsonForms
    :data="data"
    v-if="isVisible"
    :schema="jsonSchema"
    :uischema="uiSchema"
    :renderers="renderers" 
    >
    <JsonForms>
  <\div>
</template>

The schema currently only contains two properties of type string.

Thanks again

Hi @Roi-p,

How do you import the DisplayOnlyRenderer?

With a regular import -

is this the issue?

Vue tooling typically requires to include the .vue file ending in the imports so it can recognize that the respective import represents a Vue SFC

Unfortunately, this does not solve the problem