formio / vue

Javascript Powered forms and JSON form builder for Vue.js
MIT License
119 stars 65 forks source link

How to register custom components in vue-formio #10

Closed github-weiwei closed 8 months ago

github-weiwei commented 6 years ago

Can anyone tell me how to solve it ? Thank you.

kamsi commented 6 years ago

@github-weiwei well, I would love to get an answer to this one as well. In the meantime I figured out sth that kind of works:


import { Form } from 'vue-formio'; // this one is needed so that we have Formio in global scope
import BaseComponent from 'vue-formio/node_modules/formiojs/components/base/Base';
import Vue from 'vue/dist/vue.esm';

var MyVueComponent = Vue.extend({
    name: 'MyVueComponent',
    props: {
        component: null
    },
    template: '<div v-bind:id="component.key">{{ component.someProp }}</div>'
});

class MyComponent extends BaseComponent {
    build() {
        super.build();
        let element = super.getElement();
        var instance = new MyVueComponent({
            propsData: {
                component: this.component,
            },
            el: element
        });
        instance.$mount();
        element.appendChild(instance.$el);
    }
}

Formio.registerComponent('mycomponent', MyComponent);
`

Now, if you add mycomponent to your form definition you should see a result.
github-weiwei commented 6 years ago

Thank you I try.

rohanrp commented 3 years ago

Answer is a bit outdated as the code has changed since 2018

Here's a sample ratings component based on a radio button

Custom component class

import { Formio } from 'vue-formio'
import Vue from 'vue'
import { generateUniqueId } from '../../models/form-utils'

const RatingsComponent = Vue.extend({
  props: {
    component: {
      type: Object,
      default: () => {}
    },
    // eslint-disable-next-line vue/require-default-prop
    instance: null,
    styles: {
      type: String,
      default: ''
    }
  },
  mounted: function() {
    var css = document.createElement('style')
    css.type = 'text/css'
    css.setAttributeNode(document.createAttribute('scopped'))
    css.appendChild(document.createTextNode(this.styles))
    this.$el.appendChild(css)
  },
  methods: {
    handleClick(value, values) {
      if (!this.component.disabled) {
        values.forEach((option) => {
          option.checked = false
          option.id = generateUniqueId()
          option.checked =
            value !== this.instance.getValue() ? option.value <= value : false
        })
        this.instance.updateValue(value, {
          modified: true
        })
      }
    }
  },
  template: `
    <div>
        <fieldset>
            <div v-for="(item) in component.values" :key="item.id" class="rating" :class="{'rating--disabled': component.disabled}">
                <input type="radio" :name="item.value" :value="item.value" :checked="item.checked"/>
                <label :for="item.value" :title="item.value" @click="handleClick(item.value, component.values)"></label>
            </div>
        </fieldset>
    </div>
  `
})

const RadioComponent = Formio.Components.components.radio

export class RatingFormIOComponent extends RadioComponent {
  attach(element) {
    const styles = this.styling('.rating')
    var instance = new RatingsComponent({
      propsData: {
        component: this.component,
        styles,
        instance: this
      },
      el: element.querySelector('.form-radio')
    })
    instance.$mount()
    return super.attach(element)
  }

  styling(id) {
    return `
    ${id} {
        float:left;
    }

    ${id}:not(:checked) > input {
        position: absolute;
        clip: rect(0, 0, 0, 0);
        height: 0;
        width: 0;
        overflow: hidden;
        opacity: 0;
    } 

    ${id}:not(:checked) > label {
        float:right;
        padding:0 .1em;
        overflow:hidden;
        white-space:nowrap;
        cursor:pointer;
        font-size:200%;
        line-height:1.2;
        color:#ddd;
        text-shadow:1px 1px #bbb, 2px 2px #666, .1em .1em .2em rgba(0,0,0,.5);
    }

    ${id}:not(:checked) > label:before {
        content: '★ ';
    }

    ${id} > input:checked ~ label {
        color: #f70;
        text-shadow:1px 1px #c60, 2px 2px #940, .1em .1em .2em rgba(0,0,0,.5);
    }

    ${id}:not(:checked) > label:hover,
    ${id}:not(:checked) > label:hover ~ label {
        color: gold;
        text-shadow:1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0,0,0,.5);
    }

    ${id} > input:checked + label:hover,
    ${id} > input:checked + label:hover ~ label,
    ${id} > input:checked ~ label:hover,
    ${id} > input:checked ~ label:hover ~ label,
    ${id} > label:hover ~ input:checked ~ label {
        color: #ea0;
        text-shadow:1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0,0,0,.5);
    }

    ${id} > label:active {
        position:relative;
        top:2px;
        left:2px;
        -webkit-touch-callout: none; /* iOS Safari */
        -webkit-user-select: none; /* Safari */
         -khtml-user-select: none; /* Konqueror HTML */
           -moz-user-select: none; /* Old versions of Firefox */
            -ms-user-select: none; /* Internet Explorer/Edge */
                user-select: none; /* Non-prefixed version, currently
                                      supported by Chrome, Edge, Opera and Firefox */
    }

    ${id}--disabled > label {
        cursor: not-allowed !important
    }

      `.replace(/\s+/g, '')
  }
}

call the addcompoment method before form render

import { Formio } from 'vue-formio'
import { RatingFormIOComponent } from './rating-formio'

export const registerCustomComponents = () => {
  Formio.Components.addComponent('rating', RatingFormIOComponent)
}
jeriah-formio commented 8 months ago

We're currently addressing a backlog of GitHub issues. Closing this thread as it is outdated. Please re-open if it is still relevant. Thank you for your contribution!