3

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/quasar@1.15.15/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>
tony19
  • 125,647
  • 18
  • 229
  • 307
Barney Szabolcs
  • 11,846
  • 12
  • 66
  • 91
  • If you're rendering components dynamically, shouldn't you be using `v-bind:is`? – Terry May 19 '21 at 14:44
  • @Terry, thanks for looking into this! I'm open to using `v-bind:is` as well, when I tried that I had no luck binding the variable. Do you know how I can bind by ref there? – Barney Szabolcs May 19 '21 at 14:55
  • Ah, I remember now, why I could not use `v-bind:is`! Although there is `v-bind="props"`, but there is no `v-on="events"`. – Barney Szabolcs May 19 '21 at 15:01
  • Is it possible for you to share a minimal, concrete and verifiable example? – Terry May 19 '21 at 15:23
  • Sure, I have updated my question. If you just copy-paste it to say demo.html, you can see that it displays 'values.value1' instead of 'hello' and that is what I'd expect. – Barney Szabolcs May 19 '21 at 15:42

1 Answers1

2

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/quasar@1.15.15/dist/quasar.umd.min.js"></script>
Tolbxela
  • 4,767
  • 3
  • 21
  • 42