title: How to create reusable component in Vue.js
body: <p>I've started learning Vue.js and it's also my first experience in WebUI development, that why, I'm asking to sorry me if I tell or do something wrong.</p>
  
  <p>Ok. I'm looking for a solution on how to create reusable components using this framework. I decide to create something like treeview with possibility of custom rendering of tree item.</p>
  
  <p>Well, first of all, I decided to implement <strong>treeview</strong> component like this:</p>
  
  <p><a href="https://i.stack.imgur.com/ePTzX.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ePTzX.png" alt="enter image description here"></a></p>
  
  <p>tree.vue.html</p>
  
  <pre><code>&lt;template&gt;
      &lt;div class="clt"&gt;
          &lt;ul v-if="hasRoots"&gt;
              &lt;tree-item-component v-for="rootNode in rootNodes"
                                   :content="rootNode"
                                   :itemViewName="itemViewName"
                                   :key="rootNode.id"/&gt;
          &lt;/ul&gt;
      &lt;/div&gt;
  &lt;/template&gt;
  &lt;script src="./tree.ts"&gt;&lt;/script&gt;
  </code></pre>
  
  <p>tree.ts</p>
  
  <pre><code>import Vue from 'vue'
  import { Component } from 'vue-property-decorator'
  
  import { ITreeItemContent } from './treeitem/treeitem'
  
  @Component({
      components: {
          TreeItemComponent: require('./treeitem/treeitem.vue.html')
      },
      props: ['rootNodes', 'itemViewName', 'componentPath']
  })
  export default class TreeComponent extends Vue {
      rootNodes: ITreeItemContent[];
      itemViewName: string; // use this property to determine what component will be used to render tree item content
  
      get hasRoots(): boolean {
          return this.rootNodes &amp;&amp; this.rootNodes.length &gt; 0;
      }
  };
  </code></pre>
  
  <p>treeitem.vue.html</p>
  
  <pre><code>&lt;template&gt;
      &lt;li&gt;
          &lt;span v-if="hasSubNodes"
                @click="toggle"&gt;[{{isOpen ? '-' : '+'}}]&lt;/span&gt;
          &lt;span&gt;
              &lt;component v-if="itemViewName"
                         :is="itemViewName"
                         :itemData="itemData" /&gt;
          &lt;/span&gt;
          &lt;ul v-if="hasSubNodes" v-show="isOpen"&gt;
              &lt;tree-item-component v-for="subNode in subNodes"
                                   :content="subNode"
                                   :itemViewName="itemViewName"
                                   :key="subNode.id" /&gt;
          &lt;/ul&gt;
      &lt;/li&gt;
  &lt;/template&gt;
  &lt;script src="./treeitem.ts"&gt;&lt;/script&gt;
  </code></pre>
  
  <p>treeitem.ts</p>
  
  <pre><code>import Vue from 'vue'
  import { Component, Prop } from 'vue-property-decorator'
  
  interface ITreeItemContent {
      id: number;
      itemData: any;
      subNodes: ITreeItemContent[];
  }
  
  @Component
  export default class TreeItemComponent extends Vue {
      @Prop()
      content: ITreeItemContent;
  
      @Prop()
      itemViewName: string; // use this property to determine what component will be used to render content
  
      @Prop()
      componentnPath: string;
  
      isOpen: boolean = true;
  
      get hasSubNodes(): boolean {
          return this.subNodes &amp;&amp; this.subNodes.length &gt; 0;
      }
  
      get subNodes(): ITreeItemContent[] {
          return this.content.subNodes;
      }
  
      get itemData(): any {
          return this.content.itemData;
      }
  
      toggle(): void {
          this.isOpen = !this.isOpen;
      }
  }
  
  export { ITreeItemContent };
  </code></pre>
  
  <p>I register my component like this:</p>
  
  <pre><code>Vue.component('tree-component', require('./components/tree/tree.vue.html'));
  </code></pre>
  
  <p>Now I want to use it as a part of other components. I'm doing it this way:
  <a href="https://i.stack.imgur.com/ufEFH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ufEFH.png" alt=""></a></p>
  
  <p>categories.ts</p>
  
  <pre><code>import Vue from 'vue'
  import { Component } from 'vue-property-decorator'
  import { ITreeItemContent } from '../tree/treeitem/treeitem'
  import { ITreeItemViewModel } from './treeitemview/treeitemview'
  
  @Component({
      components: { // local registration of component what will be used to render tree item content
          TreeItemViewComponent: require('./treeitemview/treeitemview.vue.html')
      }
  })
  export default class CategoriesComponent extends Vue {
      itemViewName: string = 'tree-item-view-component';
  
      categories: ITreeItemContent[];
  
      data(): any {
          return { categories: testItemsData }
      }
  }
  
  let testItemsData: ITreeItemContent[] = [ /* test data */ ];
  </code></pre>
  
  <p>categories.vue.html</p>
  
  <pre><code>&lt;template&gt;
      &lt;div class="clearfix"&gt;
          &lt;tree-component :rootNodes="categories"
                          :itemViewName="itemViewName"&gt;&lt;/tree-component&gt;
      &lt;/div&gt;
  &lt;/template&gt;
  &lt;script src="./categories.ts"&gt;&lt;/script&gt;
  </code></pre>
  
  <p>treeitemview.ts</p>
  
  <pre><code>import Vue from 'vue'
  import { Component, Prop } from 'vue-property-decorator'
  
  interface ITreeItemViewModel {
      msg: string;
      icon: string;
  }
  
  @Component
  export default class TreeItemViewComponent extends Vue {
      @Prop({ required: true })
      itemData: ITreeItemViewModel;
  
      get msg(): string {
          return this.itemData.msg;
      }
  
      get _class(): string {
          return 'glyphicon glyphicon-' + this.itemData.icon;
      }
  }
  
  export { ITreeItemViewModel };
  </code></pre>
  
  <p>treeitemview.vue.html</p>
  
  <pre><code>&lt;template&gt;
      &lt;span :class="_class"&gt;{{msg}}&lt;/span&gt;
  &lt;/template&gt;
  &lt;script src="./treeitemview.ts"&gt;&lt;/script&gt;
  </code></pre>
  
  <p>I want to use <code>TreeItemViewComponent</code> as component what will display the content of node in <strong>treeview</strong> whats why, I registred it as local component for <code>CategoriesComponent</code> but then I run my code I catch an error:</p>
  
  <pre><code>[Vue warn]: Unknown custom element: &lt;tree-item-view-component&gt; - did you register the component correctly? For recursive components, make sure to provide the "name" option.
  </code></pre>
  
  <p>If I register <code>TreeItemViewComponent</code> as global component like this:</p>
  
  <pre><code>Vue.component('tree-item-view-component', require('./components/categories/treeitemview/treeitemview.vue.html'));
  </code></pre>
  
  <p>everything work well. I read about component registration in Vue.js manual, but as I understand now, nested components don't know about localy registred components. In other words, if I register <code>TreeItemViewComponent</code> as local component for <code>CategoriesComponent</code> I can use <code>TreeItemViewComponent</code> only inside <code>CategoriesComponent</code> template. I've tried to pass <code>TreeItemViewComponent</code> component as property value for <code>TreeComponent</code> but I've got compilation errors.</p>
  
  <p>I've also read about <code>$.refs</code> witch gives a possibility to access parent component inside child, but I don't understand how to use it.</p>
  
  <p>The easiest solution is to register <code>TreeItemViewComponent</code> as global Vue.js component, but I think this is not right way, because of I want create multiple components what will use <code>TreeComponent</code> and provides their own <code>TreeItemViewComponent</code> and global registration of each <code>TreeItemViewComponent</code> will provide confusion in feature.</p>
  
  <p>Do other solutions exist or it's impossible to create reusable components in Vue.js?</p>