loveyrejoice
loveyrejoice

Reputation: 53

this.$router is undefined inside Vue.extend() method

Using vue v2.5.9 and vue-router v.3.0.1.

Minimal reproduction link: https://codepen.io/clarewhatever/pen/eyzMNo

I am trying to dynamically insert a <router-link> element into component using the Vue.extend() method.

Vue.component('markdown', {
  name: 'markdown',
  props: {
    routeName: {
      type: String,
      required: true
    },
    routeLabel: {
      type: String,
      required: true
    }
  },
  template: `<span ref="content"></span>`,
  mounted () {
    let self = this;
    let routerLink = `<router-link :to="{ name: ${routeName} }">${routeLabel}</router-link>`;
    let MarkdownContent = Vue.extend({
      template: `<div>${routerLink}</div>`,
      // router: self.$router,
      mounted () {
        // this.$router is undefined unless router is explicitly defined
        console.log(this.$router);
      }
    });
    new MarkdownContent().$mount(this.$refs.content);
  }
});

The problem I'm running into is that this.$router is undefined within the extend() method so the route being passed to the router-link does not exist. If I pass in the router from the parent component (router: self.$router) the router-link element mounts as expected.

I tried swapping out Vue.extend() for Vue.component() but ran into the same issue. I think it must have something to do with the vm.$mount method being called on a constructor, although I'm not sure why it works that way. All of the docs show it being used that way, but I haven't been able to find an example that includes vue-router.

Upvotes: 0

Views: 3322

Answers (1)

thanksd
thanksd

Reputation: 55664

By creating a new Vue instance and mounting it, via new MarkdownContent().$mount(...), you are breaking the normal paradigm of parent/child relationship. The VueRouter plugin automatically sets the reference for this.$router for child components included in the template. But, by manually mounting the component, you lose that reference.

Instantiating and mounting a new component just to render a <router-link> tag seems very roundabout. But, if you really need do it this way, you just need to specify the router reference in the component definition object:

let MarkdownContent = Vue.extend({
  router,
  template: `<div>${routerLink}</div>`,
  mounted () {
    console.log(this.$router);
  }
});

Here's a working codepen.

Upvotes: 3

Related Questions