Search code examples
typescriptvue.jsvuejs2vue-componentvue-typescript

How can I pass property to dynamic Vue component in code


I'm using Vue2 and TypeScript and I want to pass multiple properties to a Vue component in code. The component is not mounted via a template but in code. I dynamicly create an element in the DOM and later mount the component on the element.

Vue2 component 'ActivityStream':

<template>
<div id="activity-stream">
  <md-list>
    <activity-list-item v-for="item in items" :key="item.getId()" :activity="item" :language="language"/>
  </md-list>
</div>
</template>

<script lang="ts">
import Vue from "vue";
import { Activity } from "models/Activity";
import { Component, Prop } from "vue-property-decorator";
import MdList from "vue-material/dist/components/MdList";
import MdIcon from "vue-material/dist/components/MdIcon";
import ActivityListItem from "components/ActivityListItem.vue";

Vue.use(MdList);
Vue.use(MdIcon);

@Component({
    components: {
        ActivityListItem
    }
})
export default class ActivityStream extends Vue {
    name: "activity-stream";
    @Prop()
    count: number;
    @Prop({ type: LanguageDefinitionNL })
    language: ILanguageDefinition;
    items: Activity[] = new Array<Activity>();
</script>

Create and mount the element dynamicly in code (Typescript):

stream = document.createElement('activity-stream');
stream.setAttribute("id", "stream");
stream.setAttribute("count", "20");
var ActivityStreamClass = Vue.extend(ActivityStream);
this._activityStream = new ActivityStreamClass();
this._activityStream.$mount('#stream');

First of all the 'count' attribute is not picked up by the Vue component and turns out to be undefined. Secondly I don't know how to pass a non-static property like a data model in code (e.g. the 'language' property in this example). Can it be passed in the constructor of the Vue (e.g. new ActivityStreamClass({..})) or should I make my own setter methods on the component class? ActivityStream.setCount(count: number))


Solution

  • As I didn't find any standard Vue(2) way for doing this I just added methods to my component which kind of looks logical to do:

    <template>
        <div id="activity-stream">
          <md-list>
            <activity-list-item v-for="item in items" :key="item.getId()" :activity="item" :language="language"/>
          </md-list>
        </div>
      </template>
      
      <script lang="ts">
      import Vue from "vue";
      import { Activity } from "models/Activity";
      import { Component, Prop } from "vue-property-decorator";
      
      import MdList from "vue-material/dist/components/MdList";
      import MdIcon from "vue-material/dist/components/MdIcon";
      import ActivityListItem from "components/ActivityListItem.vue";
      import { LanguageDefinitionNL } from "tools/i18n/LanguageDefinitionNL";
      import { ILanguageDefinition } from "tools/i18n/interfaces/ILanguageDefinition";
      
      Vue.use(MdList);
      Vue.use(MdIcon);
      
      @Component({
        components: {
          ActivityListItem
        }
      })
      export default class ActivityStream extends Vue {
        name: "activity-stream";
        @Prop()
        count: number;
        @Prop({ type: LanguageDefinitionNL })
        language: ILanguageDefinition;
        items: Activity[] = new Array<Activity>();
        
        public setCount(count: number): void {
          this.count = count;
        }
    
        public setLanguage(language: ILanguageDefinition): void {
          this.language = language;
        }
    
        public addItem(activity: Activity): void {
          // Add new item at the top of the list
          this.items.unshift(activity);
        }
      }
      </script>
    

    And when I'm using the component from plain Typescript/Javascript I do the following:

    var ActivityStreamClass = Vue.extend(ActivityStream);
    this._activityStream = new ActivityStreamClass();
    this._activityStream.setCount(10);
    this._activityStream.setLanguage(language);
    this._activityStream.$mount('#stream');
    

    Enjoy!