0
votes

I am building a dashboard app where users will be able to choose widgets that appear in certain positions. I am doing this using

<component :is="name-of-component">

Its all working well but I want the user to be able to edit the component and emit the changes to the parent. By editing lets say for example 1 component displays a welcome message which the user can change.

Right now I have the following

Dashboard.vue

<template>
  <component :is="name-of-component"></component>
</template>
<script>
  data () {
    return {
      name-of-component: 'selected-component-name'
    }
  }
</script>

In the script is the computed, mounted etc. which I don't think have relevance to the question.

Since Im using component :is Im finding it tricky to pass props and emit changes. In my store I have 2 props for the component (title & subtitle) in an array. I can hardcode :props="welcomeMessage" but I don't want to hard code since Im using :is and position of widgets can change.

Emit is also causing an issue for me. I can, of course, emit by hard coding the call to the component but since Im using :is its not going to work for me.

Heres what is working but I need to make it dynamic as any component :is wan contain any widget. Any ideas?

<component 
  :is="welcomeMessage" 
  :props="dashboard.welcomeMessage" 
  @update-welcomeMessage="welcomeMessage(e)">
</component>

OR

<component 
  :is="busStops" 
  :props="dashboard.myBusStop" 
  @update-busStop="busStop(e)">
</component>

Id like to have the components so that I can pull in the different concerns and have each one be more like this where "name-of-component" could be used to populate the :is, :props and @update:

<component 
  :is="name-of-component" 
  :props="dashboard.name-of-component" 
  @update-name-of-component="name-of-component(e)">
</component>
1

1 Answers

1
votes

You can use the v-bind and v-on capabilities, and use computed properties just like you are already doing it. I'll explain myself:

<some-component @some-event="doThis" foo="bar"></some-component>

is the same as writing:

<some-component v-bind="{foo: 'bar'}" v-on="{'some-event': doThis}"></some-component>

That means that you can write computed properties to compute which listeners and attributes you want to use for your dynamic component.

I wrote a complete example on jsFiddle if you want: https://jsfiddle.net/tsc2pmrx/

Template:

<div id="app">
  <component :is="componentToUse" v-on="listeners" v-bind="attributes"></component>
</div>

JS:

Vue.component('greeting', {
    props: ['name'],
  template: '<h1>Welcome {{ name }} !</h1>',
  mounted () {
    setTimeout(() => {
        this.$emit('some-event')
    }, 2000)
  }
});

Vue.component('other-component', {
    template: '<h1>Welcome to Other Component</h1>'
})

// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
  el: '#app',
  data: {
    componentToUse: 'greeting'
  },
  methods: {
    handleOtherComponentEvent () {
        console.log('Hello World from other component listener')
    },
    handleGreetingComponentEvent () {
        console.log('Hello World from greeting component event listener')
    }
  },
  computed: {
    listeners () {
        if (this.componentToUse === 'greeting') {
        return {
            'some-event': this.handleOtherComponentEvent
        }
      } else if (this.componentToUse === 'other-component') {
        return {
            'some-greeting-event': this.handleGreetingComponentEvent
        }
      }

      return {}
    },
    attributes () {
        if (this.componentToUse === 'greeting') {
        return {
            'name': 'Hammerbot'
        }
      }

      return {}
    }
  }
});