Search code examples
vue.jsvuejs2vue-render-function

How to pass dynamic props to vue's render function?


I try to render components dynamically based on descriptions.
From

{component: 'customComponent', props: {value: "val1"}, ...}

I'd render

<custom-component :value="val1" @input="v=>val1=v" />` 

I aim to do this for arbitrary events and dynamic props.
I have no idea though how to pass dynamic props to render.

Partial solution:

A solution that works but re-renders everytime val1 changes, based on (https://symfonycasts.com/screencast/vue/vue-instance) is

render: function(h){
    const template = "...building up the html here..."
    return Vue.compile(template).render.call(this, h);
}

My attempt using the VueJS docs

I could not find in the docs on render how I could pass dynamic variables.

In the minimal implementation you can see how far I've got, if you can help me finish it, it would be awesome!

Minimal implementation so far

I expect to see 'hello' instead of 'values.value1' and values.value1 should update once I change the text in the text box.

demo.html:

<!DOCTYPE html>
<html>
<body>
  <div id="q-app">
    The text input should say 'hello' instead of 'values.value1'
    <custom-component :descriptor="mainComponent"></custom-component>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/quasar.umd.min.js"></script>
  <script>
Vue.component('custom-component', {
    props: ['descriptor'],
    render: function (createElement) {
        const attributes = {
            on: this.descriptor.events,
            props: this.descriptor.props
        }
        return createElement(
            this.descriptor.component,
            attributes)
    }
})
const app = new Vue({
    el: '#q-app',
    data: function(){
    return {
        mainComponent: {
           component: 'q-input',
           props: {
               value: 'values.value1'
           },
           events: {
               input: value => this.values.value1 = value
           }
        },
        values: {
            value1: 'hello'
        }
    }
  }
})
  </script>
</body>

Solution

  • I guess, I have fixed your example.

    Of course, you can watch the value in the main app. But it is better to sent an input event with the value.

    Added the reference to the component

    ref="mc"
    

    and the input event binding

    @input="logEventValue" 
    

    Vue.component('custom-component', {
        props: ['descriptor'],
        render: function (createElement) {
            const attributes = {
                on: this.descriptor.events,
                props: this.descriptor.props
            }
            return createElement(
                this.descriptor.component,
                attributes)
        }
    })
    const app = new Vue({
        el: '#q-app',
        data: function(){      
          return {
              mainComponent: {
                 component: 'q-input',
                 props: {
                     value: 'hello'
                 },
                 events: {              
                   input: value => {
                      this.values.value1 = value;
                      // with ref="mc" 
                      this.$refs.mc.$emit('input', value); 
                      // or over the list of children
                      this.$children[0].$emit('input', value); 
                    }
                 }  
              },
              values: {
                value1: 'hello'
            }
          }
       },   
       watch: {
          'values.value1': (newVal) => console.log(`1. watcher: ${newVal}`),
          // or deeply
          values: {
            handler(newVal) {
                console.log(`2. watcher: ${newVal.value1}`)
            },
            deep: true,
          }
       },
       methods: {
          logEventValue(value) {
            console.log(`logEventValue: ${value}`);
          }
       }
    })
    <div id="q-app">
      <custom-component ref="mc" @input="logEventValue" :descriptor="mainComponent"></custom-component>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/quasar.umd.min.js"></script>