@oarepo/data-renderer
v2.4.9
Published
A library for providing simple (but configurable) UI for rendering of JSON data
Downloads
15
Readme
@oarepo/data-renderer
A library for providing simple (but configurable) UI for rendering JSON data.
Example
<template lang="pug">
data-renderer(:data="data")
</template>
<script>
export default {
data: function() {
return {
data: { 'title': 'Hello world' }
}
}
}
</script>
Installation
yarn add @oarepo/data-renderer vue-uid
To register/configure the library, add a new boot file to quasar (or main.js for vue-cli projects):
import DataRenderer from '@oarepo/data-renderer'
import VueUid from 'vue-uid';
export default async ({ Vue, store, router }) => {
Vue.use(VueUid);
Vue.use(DataRenderer, {})
}
What the component does
DataRendererComponent
component iterates the layout tree and converts layout
into a VueJS component. Without any settings, the created component tree will look like:
wrapper.wrapperClass(:style="wrapperStyle" ...wrapperAttrs)
label.labelClass(:style="labelStyle" ...labelAttrs)
| labelValue
value.valueClass(:style="valueStyle" ...valueAttrs) {{ valueOnPath }}
childrenWrapper.childrenWrapperClass(:style="childrenWrapperStyle" ...childrenWrapperAttrs)
// children rendered in here
This tree is defined via the following layout:
elementProperties = {
element: null,
class: {}, // element classes
style: '', // element style
attrs: {}, // element attrs
visible: true // set to false to not render the element, just its content
}
layout = {
wrapper: {
...elementProperties,
element: 'div',
},
label: {
...elementProperties,
element: 'label',
label: 'Label to be shown',
},
value: {
...elementProperties,
element: 'div',
},
'children-wrapper': {
...elementProperties,
element: 'div',
},
'array-wrapper': {
...elementProperties,
element: 'div',
},
prop: '', // json path pointing to the displayed value inside record metadata
showEmpty: false, // if true, the element will be rendered even if there is no value
children: [] // layout of children of this node
}
Every property can be a function func({context, layout, data, vue, paths, ...})
where context
points to the actual parts of the data that is being rendered. To use property as a function, wrap it inside f
:
export default {
layout: {
prop: 'thumbnail',
value: {
component: 'img',
attrs: {
src: f(({ value }) => { return value }),
width: '16'
}
}
}
}
Usage
To apply this layout, add to template:
<template lang="pug">
data-renderer(:layout="layout" :data="data"
schema="block|inline|table|<object with default definition>")
</template>
Data
data
passed to data renderer must be an object. To render data consisting of an array of objects, data renderer can be wrapped with an element using v-for directive. Example:
<template lang="pug">
div(v-for="item in array")
data-renderer(:data="item")
</template>
Layout
The layout
element might contain the layout as shown above, or shortcut can be used:
export default {
layout: {
title: {
label: {
value: 'Title label'
}
},
location: {
children: [ ... ]
}
}
}
Rendering children
Array and object children can be defined in layout
. The array or object itself is placed inside children
in layout
and may contain another array of children
. See the examples below.
Object layout definition may look like this:
export default {
data: {
object: {}
},
layout: {
showEmpty: true,
'children-wrapper': {
element: 'div'
},
children: [
{
prop: 'location',
label: {
label: 'Location label'
},
children: [
{
prop: 'street',
label: {
label: 'Street'
}
},
{
prop: 'number',
label: {
label: 'Number'
}
},
{
prop: 'zipcode',
label: {
label: 'Zipcode'
}
}]
}]
}
}
Layout of an array contains the item property with definition of layout for array items:
export default {
data: {
object: {}
},
layout: {
showEmpty: true,
'array-wrapper': {
element: 'div'
},
children: [
{
prop: 'Contact',
label: {
label: 'List of contacts'
},
item: {
label: {
label: 'Phone number'
}
}
}]
}
}
If the item of an array is a complex value, then the item
property in layout
must contain children
. Example:
export default {
data: {
object: {}
},
layout: {
showEmpty: true,
'array-wrapper': {
element: 'div'
},
children: [
{
prop: 'Contact',
label: {
label: 'List of contacts'
},
item: {
children: [
{
prop: 'phone',
label: {
label: 'Phone number'
}
}
]
}
}]
}
}
Overriding parts of layout
It might be useful to be able to override the layout for selected paths.
To do this, pass :path-layouts
property.
The value of the property is:
- object with keys (same as slot names but without the 'element' prefix) and value for the layout of the corresponding object at the given path
export default {
data: {
object: {
a: 'red text'
}
},
pathLayouts: {
a: {
value: {
class: ['text-red']
}
}
}
}
Path details
As stated above, the path is composed based on the jsonpath of rendered data. The set of paths for each rendered node is constructed as follows:
export default {
layout: {
prop: "location",
children: [
{ prop: 'street' },
{ prop: 'number' },
{ prop: 'zipcode' }
]
}
}
The path for the root is ['location']
. The path for street
is ['location-street', 'street']
,
i.e. for each of the parent paths, '-street'
is appended to the path and an extra street
.
Translating labels
A function can be registered to create/translate labels. Set either a global labelTranslator when the module is
initialized or a :labelTranslator
prop containing function with the following layout:
func({label, context, layout, data, vue, paths, schema})
and returning the translated label or null if the label should not appear. The default implementation adds ':'
after the label for inline
schema.
Using the same logic, boolean values can be translated with :booleanTranslator
.
Dynamic layout
If there is no layout specified for the object, it is created dynamically from the data passed to data-renderer.
Links
To render the value as a link, define <a></a>
html tag and href attribute in pathLayouts.
export default {
a: {
value: {
element: 'a',
attrs: {
href: f(({url}) => { return url })
}
}
}
}
Using slots before and after rendered value
Components for primitive values contain a before
and after
slot which can be used to render custom code before and after rendered value. Example:
<template lang="pug">
data-renderer(:layout="layout" :data="data")
template(v-slot:before)
div a
template(v-slot:after)
div b
</template>
Rendering components before and after rendered data
To render custom component before or after rendered data, register component in layout, e.g.: before: CustomComponent
, after: CustomComponent
Rendering custom components based on type of value
Custom components can be used based instead of default ones, when passed to :renderer-components
property. Example:
<template lang="pug">
data-renderer(:layout="layout" :data="data" renderer-components="rendererComponents")
</template>
export default {
data: {
a: 'string value',
b: 1,
c: 'string value',
d: true
},
rendererComponents:{
string: CustomComponent
}
}