JSFiddle - React, Tailwind, and code Playground
HTML
<div id="vue">
<autoform :schema="schema" @success="alert"></autoform>
<autoform :schema="schema" @success="alert"></autoform>
<pre>{{schema}}</pre>
</div>
SCSS
.item {
font-family:sans-serif;
background:white;
margin:10px;
padding:10px;
border:1px solid lightgrey;
> label:first-child{
display:block;
font-weight:bold;
margin-bottom:5px;
}
}
JavaScript
Vue.component('item', {
name:'item',
props:['linkId','text','type','item'],
inject:['formData'],
template:`
<div class="item">
<label :for="linkId">{{text}}</label>
<textarea v-if="type == 'string'" :id="linkId" v-model="formData[linkId]"></textarea>
<input v-else-if="type == 'date'" type="date" :id="linkId" v-model="formData[linkId]">
<input v-else-if="type == 'integer'" type="number" :id="linkId" v-model="formData[linkId]">
<template v-else-if="type == 'boolean'" v-for="option in ['yes','no']">
<input type="radio" :id="linkId + option" v-model="formData[linkId]" :value="option">
<label :for="linkId + option">{{option}}</label>
</template>
<template v-else-if="type == 'choice'" v-for="option in ['yes','no','dont know']">
<input type="radio" :id="linkId + option" v-model="formData[linkId]" :value="option">
<label :for="linkId + option">{{option}}</label>
</template>
<template v-else-if="type == 'group'">
<item v-for="i in item" v-bind="i" :key="i.linkId"></item>
</template>
</div>`
})
Vue.component('autoform', {
props:['schema'],
data(){
return {
formData:{}
}
},
provide(){
return {formData:this.formData}
},
template:`
<form v-if="schema" @submit.prevent="$emit('success', formData)">
<h1>{{schema.code[0].display}}</h1>
<item v-for="item in schema.item" v-bind="item" :key="item.linkId"></item>
<button type="submit">Submit</button>
</form>
<h2 v-else>Loading</h2>`
})
new Vue({
el:'#vue',
data:{
schema:undefined
},
created(){
fetch('https://hl7.org/fhir/questionnaire-example-f201-lifelines.json')
.then(res => res.json())
.then(schema => this.schema = schema)
},
methods:{
alert(stuff){
alert(JSON.stringify(stuff,null,' '))
}
}
})