How to access Vue Instance from mixins?
Asked Answered
S

2

6

I would like to implement this kind of logic that assign this.$store.value to local data. This is how I do in pages/index.vue for instance.

method: {
  this.value = this.$store.value
}

I want to write it down into mixins because I actually have another logics around it and I use some pages.

However, I don't know how should I access this(VueInstnce) from mixins?

Scylla answered 31/10, 2018 at 7:21 Comment(0)
L
1

It is not supported by Vue because mixin runs first before component's code, then mixin is bound (merged) by Vue to the component instance so it's easy to access mixin from component/instance scope, but not vice versa.

To achieve your need I think the mixin method (like created) should be run (for example) with a given reference to the instance of your component as a parameter, but it's not like that.

However, if you reorganize your code to run what you need from instance.created accessing there methods and data of mixin is possible and passing arguments on your own:

var mixin = {
    data: {mixin: 'mixin'},
    created: function () {
    console.log('mixin hook called')
    },
    methods: { test: function(arg){console.log(arg); } }
};

vm=new Vue({
    data: {component: 'component'},
    mixins: [mixin],
    created: function () {
    console.log('called hook of ' + this.component + ' and accessing ' + this.mixin)
    },
});

vm.test(vm.mixin);
vm.test(vm.component);  // no problem to run mixin's method with component's data
> mixin hook called
> called hook of component and accessing mixin
> mixin
> component
Lulualaba answered 19/11, 2019 at 13:59 Comment(0)
S
0

Okay so I don't know if it's considered a bad practice but I have managed to accomplish one-way data transmission without an event bus.

I am using vuejs 3 with composition api. Requirement: all components shall be able to access a global singleton component shown on top of whole app.

plugin.js - here we use the created event in mixin to get a reference of an component instance. In example below I always have just 1 tracker component instance (global popup). If you have a different more complex scenario I would recommend sticking with event bus solution instead..

import Tracker from "@/plugins/ProgressTracker/components/Tracker.vue";

export default {
  install(app, params = {}) {
    app.component("tracker", Tracker);

    let instance = undefined;

    app.mixin({
      created() {
        if (this.$options.name === "ProgressTrackerPopup") {
          instance = this;
        }
      },
    });

    const progressTracker = () => {
      //
    };

    progressTracker.show = function () {
      instance.show();
    };

    app.config.globalProperties.$progressTracker = progressTracker;
  },
};

useProgressTracker.js - globally reusable composable function that exposes show method

import { ref, computed, getCurrentInstance } from "vue";

export default function useProgressTracker() {
  const internalInstance = getCurrentInstance();
  const progressTracker = internalInstance.appContext.config.globalProperties.$progressTracker;

  const show = () => {
    progressTracker.show();
  };

  return {
    show,
  };
}

Tracker.vue - component that we need to globally access from any other component (methods of it).. name is important. It shall be set in order for the mixin to be able to detect your component creation

<template>
  <div class="onTop" v-if="isShow">test</div>
</template>
<script>
import { ref } from "vue";

export default {
  name: "ProgressTrackerPopup",
  setup() {
    var isShow = ref(false);

    const show = () => {
      isShow.value = true;
    };

    return {
      isShow,
      show,
    };
  },
};
</script>
<style scoped>
.onTop{
  position: absolute;
  z-index: 1999;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #0000004f;
}
</style>

this is it. Don't forget to register the plugin:

import ProgressTracker from "@/plugins/plugin.js";
// ..
app.use(ProgressTracker, {});

Now when you want the pop-up to be shown you invoke:

// <tracker />

import useProgressTracker from "@/plugins/ProgressTracker/use/useProgressTracker.js";

const tracker = useProgressTracker();
tracker.show();

The last line of code will basically invoke the show method on global component instance itself! Whereas if you used an event bus instead - you would have subscribed to the pop event on target component itself.

I find this solution to be useful when you don't want to deal with an event bus and the case is relatively trivial (you only have 1 global instance at all times). Though, you could of course use an array of instances and loop-invoke the methods on them in sequence.. :)

Sarcocarp answered 16/12, 2021 at 14:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.