Inject Custom Store into Application - vue.js

Vuex allows you to inject the store into your root instance, making it accessible via this.$store in all child components.
Without Vuex, is it possible to inject a custom store implementation into child components?
e.g.
// main.js
let app = new Vue({router, store, ...App}).$mount("#flightdeck-app")
export { app, store, router }
// SomeComponent.vue
export default {
name: "Overview",
components: { "credentials": Credentials },
computed: {
count() {
// injected store; is currently undefined.
return this.$store.state.items.length
}
},
Attempting to access this.$store results in undefined in child components, as Vuex seemingly has additional hooks to make this happen.

You may create a custom plugin for vue that register an initialize your custom store (or anything, I'll create a logger object just for demonstration).
For example you could have
//myLogger.js
export default {
install(Vue, options) {
function log(type, title, text) {
console.log(`[${type}] ${title} - ${text}`);
}
Vue.prototype.$log = {
error(title, text) { log('danger', title, text) },
success(title, text) { log('success', title, text) },
log
}
}
}
Before your main Vue instance tell to register your plugin
//main.js
import Logger from './path/to/myLogger';
Vue.use(Logger);
var vm = new Vue({
el: '#app',
template: '<App/>',
components: { App }
})
Now you can call this.$log on any child component
//myComponent.vue
export default {
data() {
return {};
},
methods: {
Save() {
this.$log.success('Transaction saved!');
}
}
}
Hope it helps, for more detail please see Vue plugins documentation

Just use in your components files:
import store from './vuex/store.js'
Place your store in separate file to get it clear.
Import store to every component where you need store.

Related

How to get the this instance in vue 3?

In vue 2+ I can easily get the instance of this as a result I can write something like this,
// main.js
app.use(ElMessage)
// home.vue
this.$message({
showClose: true,
message: 'Success Message',
type: 'success',
})
What should I do for vue 3 as,
Inside setup(), this won't be a reference to the current active
instance Since setup() is called before other component options are
resolved, this inside setup() will behave quite differently from this
in other options. This might cause confusions when using setup() along
other Options API. - vue 3 doc.
Using ElMessage directly
ElementPlus supports using ElMessage the same way as $message(), as seen in this example:
import { ElMessage } from 'element-plus'
export default {
setup() {
const open1 = () => {
ElMessage('this is a message.')
}
const open2 = () => {
ElMessage({
message: 'Congrats, this is a success message.',
type: 'success',
})
}
return {
open1,
open2,
}
}
}
Using $message()
Vue 3 provides getCurrentInstance() (an internal API) inside the setup() hook. That instance allows access to global properties (installed from plugins) via appContext.config.globalProperties:
import { getCurrentInstance } from "vue";
export default {
setup() {
const globals = getCurrentInstance().appContext.config.globalProperties;
return {
sayHi() {
globals.$message({ message: "hello world" });
},
};
},
};
demo
Note: Being an internal API, getCurrentInstance() could potentially be removed/renamed in a future release. Use with caution.
Providing a different method where the idea is to set a globally scoped variable to the _component property of the viewmodel/app or component:
pageVM = Vue.createApp({
data: function () {
return {
renderComponent: true,
envInfo: [],
dependencies: [],
userGroups: []
}
},
mounted: function () {
//Vue version 3 made it harder to access the viewmodel's properties.
pageVM_props = pageVM._component;
this.init();
},

Ionic 5, Vue 3 - How to use Ionic lifecycle hooks inside setup()?

The Ionic documentation describes how to use the Ionic lifecycle methods like ionViewWillEnter, ionViewDidEnter etc. inside Vue method.
https://ionicframework.com/docs/vue/lifecycle
I'm looking for a way to access them inside the new Vue 3 setup() method so that I can able to access the properties defined there. Is it something possible?
export default defineComponent({
...
setup(){
const list = ref([]);
// I need something like this
const ionViewDidEnter = () => {
list.value.push(...['some', 'array', 'here']);
},
return {
list,
ionViewDidEnter
};
}
});
This is now possible in a composition API style since this PR was merged
https://github.com/ionic-team/ionic-framework/pull/22970
export default defineComponent({
...,
components: { IonPage },
setup() {
onIonViewDidEnter(() => {
console.log('ionViewDidEnter!');
});
}
});
Unfortunately no, at least not yet, since the code firing the events is specifically looking for the lifecycle events to be registered as part of the component methods. Luckily I've submitted a PR which is going to fix this and allow you to define them the same way you would define mounted or any other Vue hooks.
I will make sure to add tests that cover the composition API scenario as well.
https://github.com/ionic-team/ionic-framework/pull/22241
Meaning that you'd be able to do it this way:
export default defineComponent({
...
ionViewDidEnter() {
...
},
setup(){
...
}
});
Just to add to what Mark Beech said, you will have to import onIonViewDidEnter
import { onIonViewDidEnter } from '#ionic/vue';
export default defineComponent({
...
ionViewDidEnter() {
console.log('Hompage');
},
setup(){
...
}
});

How to add emit information to a component dynamically generated using Vue.extent()?

I'm dynamically generating instances of my child component "Action.vue" using Vue.extent() this way:
let ActionClass = Vue.extend(Action)
let ActionInstance = new ActionClass({
store
})
ActionInstance.$mount()
this.$refs.actions.appendChild(ActionInstance.$el)
This works fine. However, besides access to the store, child component also needs to emit an event (in response to user interaction with one of its elements) for the parent component to execute a method.
How to achieve this?
You can use instance.$on method to add eventListenersdynamically :
Consumer
import Dummy from "./Dummy.vue";
import Vue from "vue";
export default {
name: "HelloWorld",
props: {
msg: String
},
methods: {
mount: function() {
const DummyClass = Vue.extend(Dummy);
const store = { data: "some data" };
const instance = new DummyClass({ store });
instance.$mount();
instance.$on("dummyEvent", e => console.log("dummy get fired", e));
this.$refs.actions.appendChild(instance.$el);
}
}
};
Child component
export default {
methods: {
fire: function() {
console.log("fired");
this.$emit("dummyEvent", { data: "dummyData" });
}
}
};
Here is the Sandbox
https://v2.vuejs.org/v2/guide/components-custom-events.html
https://v2.vuejs.org/v2/api/#Options-Lifecycle-Hooks
You can use a lifecylce hook (for example: mounted) to emit the event when the child has been created.
you can listen to the events as documented in the documentation.
the store can be reached through this.$store.

Transfer Data From One Component to Another

I have a component which makes a call to my backend API. This then provides me with data that I use for the component. I now want to create another component which also uses that data. While I could just do another api call that seems wasteful.
So, in Profile.vue i have this in the created() function.
<script>
import axios from 'axios';
import { bus } from '../main';
export default {
name: 'Profile',
data() {
return {
loading: false,
error: null,
profileData: null,
getImageUrl: function(id) {
return `http://ddragon.leagueoflegends.com/cdn/9.16.1/img/profileicon/` + id + `.png`;
}
}
},
beforeCreate() {
//Add OR Remove classes and images etc..
},
async created() {
//Once page is loaded do this
this.loading = true;
try {
const response = await axios.get(`/api/profile/${this.$route.params.platform}/${this.$route.params.name}`);
this.profileData = response.data;
this.loading = false;
bus.$emit('profileData', this.profileData)
} catch (error) {
this.loading = false;
this.error = error.response.data.message;
}
}
};
</script>
I then have another child component that I've hooked up using the Vue router, this is to display further information.
MatchHistory compontent
<template>
<section>
<h1>{{profileDatas.profileDatas}}</h1>
</section>
</template>
<script>
import { bus } from '../main';
export default {
name: 'MatchHistory',
data() {
return {
profileDatas: null
}
},
beforeCreate() {
//Add OR Remove classes and images etc..
},
async created() {
bus.$on('profileData', obj => {
this.profileDatas = obj;
});
}
};
</script>
So, I want to take the info and display the data that I have transferred across.
My assumption is based on the fact that these components are defined for two separate routes and an event bus may not work for your situation based on the design of your application. There are several ways to solve this. Two of them listed below.
Vuex (for Vue state management)
Any local storage option - LocalStorage/SessionStorage/IndexDB e.t.c
for more information on VueX, visit https://vuex.vuejs.org/.
for more information on Localstorage, visit https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage.
for more information on session storage, visit https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
The flow is pretty much the same for any of the options.
Get your data from an API using axios as you did above in Profile.vue
Store the retrieved data with VueX or Local/Session storage
Retrieve the data from Vuex or local/session storage in the created method of MatchHistory.vue component
For the local / session storage options, you will have to convert your object to a json string as only strings can be stored in storage. see below.
in Profile.vue (created)
const response = await axios.get(........)
if(response){
localStorage.setItem('yourstoragekey', JSON.stringify(response));
}
In MatchHistory.Vue (created)
async created() {
var profileData = localStorage.getItem('yourstoragekey')
if(profileData){
profileData = JSON.parse(profileData );
this.profileData = profileData
}
}
You can use vm.$emit to create an Eventbus
// split instance
const EventBus = new Vue({})
class IApp extends Vue {}
IApp.mixin({
beforeCreate: function(){
this.EventBus = EventBus
}
})
const App = new IApp({
created(){
this.EventBus.$on('from-mounted', console.log)
},
mounted(){
this.EventBus.$emit('from-mounted', 'Its a me! Mounted')
}
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
further readings
You can make use of the VUEX which is a state management system for Vue.
When you make api call and get the data you need, you can COMMIT a MUTATION and pass your data to it. What it will do, it will update your STATE and all of your components will have access to its state (data)
In your async created(), when you get response, just commit mutation to your store in order to update the state. (omitted example here as the vuex store will need configuration before it can perform mutations)
Then in your child component,
data(){
return {
profileDatas: null
}
},
async created() {
this.profileDatas = $store.state.myData;
}
It might seem like an overkill in your case, but this approach is highly beneficial when working with external data that needs to be shared across multiple components

Vue: how to apply multiple global mixins

I am currently working on a Vue project including multiple applications.
It has project-wide used methods and application-wide used methods.
Therefore, I created 2 mixins which need to be available in every Vue component. However, I don't know how to implement it using Vue.mixin().
Please help.
I tried this. Not work..
The error says "Cannot read property 'VALIDATION' of undefined".
Somehow URL is not imported. URL() returns a object where urls are defined according to either DEV or PRODUCTION mode.
import global_mixin from './global_mixin.js'
import application_mixin from './application_mixin.js'
Vue.mixin(global_mixin)
Vue.mixin(application_mixin)
new Vue({
el: '#app',
render: h => h(App)
})
global_mixin.js
export default {
data() {
return {
// data items
}
}
}
application_mixin.js
import { URL } from './_util'
export default {
data() {
return {
URL_VALIDATION: URL().VALIDATION
}
}
}
_util.js
import store from './_store'
export const URL = () => {
const urls = {
PROD: {
VALIDATION: '/api/web/company/profile/validation',
PROFILE: '/api/web/company/profile',
COUNTRY: '/api/app/countries',
ADDRESS: '/api/web/address'
},
DEV: {
PROFILE: '/data/profile_company.json',
VALIDATION: 'https://httpbin.org/post',
COUNTRY: '/data/countries.json',
ADDRESS: '/data/zip.json'
}
}
return urls[store.getters.mode]
}