1
votes

I'm building a TabbedDetailView reusable component in vue. The idea is that the tab-detail component receives a list of objects which have a title and a component. It then does the logic so that when you click on a tab, then the component is displayed. The problem is that this components have a prop that is a user_id. How do I insert this prop into the components from outside of the template (directly in the script)? For example (using single file vue components with webpack):


TabDetail.vue

<template>
<div>
<nav class="tabs-nav">
  <ul class="tabs-list">
    <li class="tabs-item" v-for='tab in tabs'>
      <a v-bind:class="{active: tab.isActive, disabled: !tab.enabled}" @click="switchTab(tab)">{{tab.title}}</a>
    </li>
  </ul>
</nav>

<div v-for='tab in tabs'>
    <component :is="tab.detail" v-if='tab.isActive'></component>
</div>

</div>
</template>

<script>
  export default {
    name: 'NavigationTabs',
    props: ['tabs'],
    created: function() {
      this.clearActive();
      this.$set(this.tabs[0], 'isActive', true);
    },
    methods: {
      clearActive: function() {
        for (let tab of this.tabs) {
          this.$set(tab, 'isActive', false);
        }
      }, switchTab: function(tab) {
        if (tab.enabled) {
          this.clearActive();
          tab.isActive = true;
        }
      },
    },
  };
</script>

The idea is that this can be reused by only passing a props object with titles and components. eg. tabs = [{title: 'Example1', component: Component1}{title: 'Example2', component: Component2}] I want to be able to instantiate this components with props before passing them. eg. tabs = [{title: 'Example1', component: Component1({user_id: 5})}{title: 'Example2({user_id: 10})', component: Component2}]).


SomeComponent.vue

import Vue from 'vue';
import TabDetail from '@/components/TabDetail'
import Component1 from '@/components/Component1';
const Componenet1Constructor = Vue.extend(Component1);

export default {
  data() {
    return {
      tabs: [
          {title: 'Componenent 1', detail: new Component1Constructor({propsData: {user_id: this.user_id}})}
          {title: 'Component 2', detail: Component2},
          {title: 'Component 3', detail: Component3},
      ],
    };
  }, props: ['user_id'],
     components: {'tab-detail': TabbedDetail},
}
<template>
  <div>
    <tab-detail :tabs='tabs'></tab-detail>
  </div>
</template>

Component1.vue

export default {
  props: ['user_id'],
};
<template>
<div>
{{ user_id }}
</div>
</template> 

The approach above raises de error: [Vue warn]: Failed to mount component: template or render function not defined.

I think this is a good idea because I'm trying to follow the dependency injection design pattern with components. Is there a better approach to this problem without using global state?

1
{{ user_id }} is not a valid template. It has to be contained in an element.Bert
@BertEvans I fixed that. But that is not the main problem I'm trying to solve.eldermao
Maybe you could throw together a small example demonstrating the issue that would make it easier to understand what you want to accomplish.Bert
@BertEvans I ellaborated the example. It's hard to put it in a concrete example in JSFiddle since it's all part of a webpack build.eldermao

1 Answers

0
votes

This is could be done via Inject Loader when using vue loader with single file vue components but it adds a lot of unnecessary complexity and it's mostly meant for testing. It seems like the preferred way of managing state is by using a global state management store like Vuex.