jsonforms-vue-seed icon indicating copy to clipboard operation
jsonforms-vue-seed copied to clipboard

Example for Custom Renderer

Open bishopandco opened this issue 3 years ago • 8 comments

It would be very helpful if this seed project showed how to add a custom renderer.

bishopandco avatar Jul 25 '22 11:07 bishopandco

First: Create a folder for you renderers. Add an index.ts

import { entry as CustomRenderer } from "@/renderers";

export const CustomRenderers: unknown[] = [
  {
    renderer: CustomRenderer.renderer,
    tester: CustomRenderer.tester
  }
];

export default CustomRenderers;

Create CustomRenderer.vue


<script lang="ts">
import { isLayout, and, uiTypeIs, rankWith } from "@jsonforms/core";
import type { JsonFormsRendererRegistryEntry, Layout } from "@jsonforms/core";
import { defineComponent } from "vue";
import {
  DispatchRenderer,
  rendererProps,
  useJsonFormsLayout,
} from "@jsonforms/vue";

const CustomRenderer = defineComponent({
  name: "block-layout-renderer",
  components: {
    DispatchRenderer,
  },
  props: {
    ...rendererProps<Layout>(),
  },
  setup(props) {
    return useJsonFormsLayout(props);
  },
});

export default CustomRenderer;

export const entry: JsonFormsRendererRegistryEntry = {
  renderer: CustomRenderer,
  tester: rankWith(3, and(isLayout, uiTypeIs("BlockLayout"))),
};
</script>

<template>
  <div>
    <div
      v-for="(element, index) in layout.uischema.elements"
      v-bind:key="`${layout.path}-${index}`"
    >
      <dispatch-renderer
        v-bind:schema="layout.schema"
        v-bind:uischema="element"
        v-bind:path="layout.path"
        v-bind:enabled="layout.enabled"
        v-bind:renderers="layout.renderers"
        v-bind:cells="layout.cells"
      />
    </div>
  </div>
</template>


Import the Custom Renderers and the vanilla renderers.

<template>
  <json-forms
  :data="fooBar"
  :renderers="renderers"
  :schema="schema"
  :uischema="uiSchema"
  @change="onChange"
  />
</template>
<script lang="ts">
  import { JsonForms } from "@jsonforms/vue";
  import { vanillaRenderers } from "@jsonforms/vue-vanilla";
  import type { JsonFormsRendererRegistryEntry } from "@jsonforms/core";
  import type { JsonSchema7 } from "@jsonforms/core";

  import { CustomRenderers } from "@/renderers";

  const renderers: JsonFormsRendererRegistryEntry[] = [
  ...customRenderers,
  ...vanillaRenderers,
  ];

  export default {
  components: { JsonForms },
  props: {
  fooBar: Object,
  schema: Object as JsonSchema7,
  uiSchema: Object,
  onChange: Function,
},
  setup() {
  return { renderers };
},
};
</script>

bishopandco avatar Aug 30 '22 13:08 bishopandco

@bishopandco following your response on issue eclipsesource/jsonforms#1744 I would like to add example of SFC component with <script setup> for registering renderers.

<script lang="ts" setup>
import type {ControlElement,} from "@jsonforms/core";
import ControlWrapper from './ControlWrapper.vue';
import {rendererProps, useJsonFormsControl,} from "@jsonforms/vue";
import {useVanillaControl} from "../util";

const props = defineProps(rendererProps<ControlElement>())

const { control, controlWrapper, onChange, styles, isFocused, appliedOptions } = useVanillaControl(useJsonFormsControl(props))
</script>
<template>
  <control-wrapper
    v-bind="controlWrapper"
    :styles="styles"
    :isFocused="isFocused"
    :appliedOptions="appliedOptions"
  >
    <input
      :id="control.id + '-input'"
      :class="styles.control.input"
      :value="control.data"
      :disabled="!control.enabled"
      :autofocus="appliedOptions.focus"
      :placeholder="appliedOptions.placeholder"
      @change="onChange"
      @focus="isFocused = true"
      @blur="isFocused = false"
    />
  </control-wrapper>
</template>

Then, you register if from scratch in another file. index.ts (ommited import section)

export const renderers = [
  {
    renderer: StringControlRenderer,
    tester: rankWith(1, isStringControl)
  }
]

Additionally, I would suggest to markRaw components you register as renderer because Vue complains about possible performance issues when passing reactive components.

bart-jaskulski avatar Sep 01 '22 07:09 bart-jaskulski

Hi @bishopandco,

It definitely makes sense to add an example custom renderer to the Vue seed, similar to the React seed. If you like you can contribute your custom renderer(s) to the main and vue2 branches :+1:

Note that redefining the renderer entry within the index.ts is not necessary. You can just add it to the list directly.

sdirix avatar Sep 01 '22 08:09 sdirix

@bart-jaskulski Awesome! I've been chasing the markRaw warning for a while. Can't find the right place to do that. Any tips there?

@sdirix will try to soon!

bishopandco avatar Sep 01 '22 19:09 bishopandco

@bishopandco I do during registration, just like:

  {
    renderer: markRaw(StringControlRenderer),
    tester: rankWith(1, isStringControl)
  }

For what I know, it doesn't cause any issues about rendering fields.

bart-jaskulski avatar Sep 02 '22 09:09 bart-jaskulski

In the Readmes we suggest using Object.freeze on the whole renderer set. Is that not sufficient?

sdirix avatar Sep 02 '22 09:09 sdirix

Thank you @sdirix, it actually does help! I didn't notice that vue-vanilla repository uses renderers in this way in readme section. I think such information could go to some FAQ section in documentation, wherever it will be more exposed.

bart-jaskulski avatar Sep 02 '22 12:09 bart-jaskulski

That's a good idea. If you like you can contribute the FAQ question here: https://github.com/eclipsesource/jsonforms2-website

sdirix avatar Sep 02 '22 13:09 sdirix