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.. :)