Thanks I will look at that… Let me be a little more specific. I’m using a custom component as a renderer for jsonb.
I’ve managed to pass a default object so can parse some data in but not sure how to ensure the output is then saved to the correct field for json forms. The custom component uses a json prop that is reactive. So what I want to do is bind a json prop to the required field so when the form is submitted the json is sent in the payload as that field.
here is my renderer:
<!-- CustomJsonbRenderer.vue -->
<template>
<div>
<Vue3JsonEditor
v-model="json"
:show-btns="false"
:expandedOnStart="true"
@json-change="onJsonChange"
@mode-change="onModeChange"
class="py-3"
/>
</div>
</template>
<script>
import { defineComponent } from "vue";
import { Vue3JsonEditor } from "vue3-json-editor";
export default defineComponent({
props: {
json: {
type: Object,
default: () => ({
name: "Example",
description: "Example description",
}),
},
},
name: "JsonbRenderer",
components: {
Vue3JsonEditor,
},
setup() {
function onJsonChange(value) {
console.log("value:", value);
// set the value to the parent component JsonForms
// Emit the value to the parent component
this.$emit("update:json", value);
}
function onModeChange(value) {
console.log("value:", value);
// value: code, tree, view
// switch to json_viewer mode
this.$refs.jsonEditor.mode = "view";
}
return {
onJsonChange,
onModeChange,
};
},
});
</script>
And Im using it like this:
<script>
import { JsonForms } from "@jsonforms/vue";
import { vanillaRenderers } from "@jsonforms/vue-vanilla";
import JsonbRenderer from "@/components/JsonbRenderer.vue";
import { getUISchema, postTableData } from "@/api/requests.js";
// add custom renderer to vanillaRenderers
// renderers is an array of objects with two properties: tester and renderer
// tester is a function that returns true or false
// renderer is a component that will be used to render the data
// see https://jsonforms.io/docs/uischema/renderers
//
const CustomTester = (uischema, schema) => {
return (
uischema.scope === "table" &&
schema.type === "object" &&
schema.properties &&
schema.properties.type &&
schema.properties.type.enum &&
schema.properties.type.enum.includes("jsonb")
);
};
const renderers = [
...vanillaRenderers,
{
tester: CustomTester,
// bind the data to the component so it can be used in the template as `this.data` or `this.data.value` etc
renderer: JsonbRenderer,
},
];
export default {
components: {
JsonForms,
JsonbRenderer
},
computed: {
id() {
return this.$route.params.id;
},
payload() {
return this.transformData();
},
crumbs() {
return [
{
name: "Home",
route: "/",
label: "Home",
order: 1,
},
{
name: "Tables",
route: "/tables",
label: "Tables",
order: 2,
},
{
name: "Table",
route: `/tables/${this.table}`,
label: "Table",
order: 3,
},
{
name: "Create",
route: `/tables/${this.table}/create`,
label: "Create",
order: 4,
},
];
},
table() {
return this.$route.params.id;
},
},
data() {
return {
items: [],
data: {},
renderers: renderers,
jsonb: {},
schema: {},
uischema: {},
};
},
methods: {
// transform data into this object format {name: "name", value: "string"}
transformData() {
let data = this.data;
let payload = [];
for (let key in data) {
let obj = {};
obj.key = key;
obj.value = data[key];
payload.push(obj);
}
return payload;
},
send() {
let data = {
id: this.$route.params.id,
page: 1,
page_size: 50,
data: this.payload,
};
postTableData(data).then((res) => {
console.log(res);
this.$router.push({ name: "Table", params: { id: this.id } });
});
},
onChange(event) {
this.data = event.data;
},
getSchema() {
const id = this.$route.params.id;
getUISchema(id).then((response) => {
this.schema = response.schema;
this.uischema = response.ui_schema;
this.data = response.data;
});
},
},
created() {
this.getSchema();
},
};
</script>
<template>
<div>
<nav class="pl-5 w-full max-w-xl flex px-5 py-5" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li
class="inline-flex items-center"
v-for="item in crumbs"
v-bind:key="item.order"
>
<router-link
v-if="item.name === 'Home'"
:to="item.route"
class="inline-flex items-center text-sm font-medium text-pickled-700 hover:text-rock-blue-600 dark:text-pickled-400 dark:hover:text-rock-blue-50"
>
<!-- if home is route -->
<svg
aria-hidden="true"
class="w-4 h-4 mr-2"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"
></path>
</svg>
{{ item.label }}
</router-link>
<router-link
v-else
:to="item.route"
class="inline-flex items-center text-sm font-medium text-pickled-700 hover:text-rock-blue-600 dark:text-pickled-400 dark:hover:text-rock-blue-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-chevron-right w-4 h-4 mr-2"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
{{ item.label }}
</router-link>
</li>
</ol>
</nav>
<!-- container -->
<div class="container mx-auto px-4 sm:px-8">
<h1 class="text-3xl mt-5 font-bold text-pickled-500">
Table: <span class="text-1xl">{{ id }} </span>
</h1>
<p class="text-pickled-500 mt-1">
Use this form to add a record to the table
</p>
<!-- Tailwind submit btn -->
<form action="submit">
<json-forms
:data="data"
:schema="schema"
:uischema="uischema"
:renderers="renderers"
@change="onChange"
@update:json="jsonb = $event"
>
<JsonbRenderer
v-for="renderer in renderers"
:key="renderer.name"
:renderer="renderer"
:json="{ data: data, schema: schema, uischema: uischema }"
/>
</json-forms>
</form>
<div class=" ">
<button
@click.prevent="send"
class="bg-pickled-600 hover:bg-pickled-400 text-white font-bold py-2 px-4 rounded mt-0"
>
Submit
</button>
</div>
</div>
</div>
<!-- Tailwind submit btn -->
</template>
If you look at the code I’m trying to bind json prop of the custom component from the parent. And emit a event that Im listening for on the parent.
I mean this is just my POC work I imagine there is a better way to do this, simpler even.