OK, i have a web page with some steps for the user and a mixin that handle those steps properties and logic, like current/next step value, checks to enable user advancing from each step to the next, etc.
Now i have to add a new functionality to be executed only after a certain step, so what i would like to be able to do is to call the original mixin method that is called everytime the user advance to the next step to add this functionality.
To be more clear, the mixin is (obviously) used in many pages of the webapp, so i would like to override the culprit mixin method in the component extending it, call the original mixin method to reuse its logic and then call oher methods for the new functionality.
Is it possibile?
sure you can, try it so:
// MyMixin.js
export default {
methods: {
myMethod() {
//...
}
}
}
// Component.vue
<script>
import MyMixin from 'path-to-mixins/MyMixin'
export default {
//...
mixins: [MyMixin],
methods: {
// there is overrinding you mixin method called myMethod
myMethod() {
// then some logic before to use your mixin method
// and call your mixin method as below
MyMixin.methods.myMethod()
}
}
//...
}
</script>
The previous answer does not work, in many cases...
you can directly access the function from the import but you need to bind the scope to this so the function uses your component data and/or other overriden functions
// Mixin.js
export default {
methods: {
aMethod() {
//...
}
}
}
// Component.vue
import Mixin from './Mixin'
export default {
//...
mixins: [Mixin],
methods: {
aMethod() {
// you need to bind the method to `this` when running so that the mixin uses your component data.
Mixin.methods.aMethod.call(this);
}
}
//...
}
if the mixin you are importing is already a vue constructor, this can happend if you used Vue.extend to create the mixin, in this case you need to use the options to access your method
You can access the mixin function using the options of the mixin
// Mixin.js
export default Vue.extend({
extends: OtherComponent
methods: {
aMethod() {
//...
}
}
})
// Component.vue
import Mixin from './Mixin'
export default {
//...
mixins: [Mixin],
methods: {
aMethod() {
// you need to bind the method to `this` when running so that the mixin uses your component data.
Mixin.options.methods.aMethod.call(this);
}
}
//...
}
Related
I developed a plugin to centralize HTTP calls and need to access its function ($api) from an imported module in a component.
The following works fine:
The plugin (http-transport.js)
export default {
install: function (Vue) { ...
Vue.prototype.$api = (...)
main.js
import HTTPTransport from './http-transport/http-transport'
Vue.use (HTTPTransport);
Usage from any component.vue
methods: {
someMethod() {
this.$api(...)
}}
All the above works.
Now, I have a SFC component that imports a module
component.vue
import logic from "./LogicService.js";
The question: how can I call $api from a function within LogicService.js?
The real case is that LogicService.js imports DataService.js from which I need to call the $api function, but I guess the solution to the question solves this as well.
Thanks so much!
(vue 2.6.11)
there are couple ways to do that.
the most easy way is just import vue and call the function. but for that case you must add the plugin function as global to vue
for example. in http-transport.js
export default function (Vue) {
//add global method or property
Vue.api= function () {
// some logic ...
api();
}
//add an instance method
Vue.prototype.$api= function () {
// some logic ...
api();
}
}
function api(){
//code goes here
}
and then in your js files just import Vue and call Vue.api().
for example in LogicService.js
import Vue from "vue";
export default function(){
//call api
Vue.api();
}
the problem with this way is that you can access Vue.api only when vue finish installize (most cases that the case so no problem).
a second way you can do that is to write the plugin like that
export default function (Vue) {
//add global method or property
Vue.api= function () {
// some logic ...
api();
}
//add an instance method
Vue.prototype.$api= function () {
// some logic ...
api();
}
}
export function api(){
//code goes here
}
now the plugin is actually indepent of vue and can work by itself without vue. for example now you can do that in LogicService.js
import {api} from './http-transport.js'
api()
I want to skip all of the methods that are being called within the created() hook. Is there a way to do this?
So instead of this
created() {
this.getAllocations();
this.getModels();
this.getTeams();
this.getCustodians();
this.getDefaultFeeStructure();
}
I want this
created() { }
It's worth noting, I cannot actually change the component itself, but for testing purposes, this needs to be done.
You can accomplish this with a global mixin (see https://v2.vuejs.org/v2/guide/mixins.html#Global-Mixin)
However, for your case you need a custom merge strategy to prevent the created hook on the component from being run:
Hook functions with the same name are merged into an array so that all of them will be called. Mixin hooks will be called before the component’s own hooks. (https://v2.vuejs.org/v2/guide/mixins.html#Option-Merging)
See a working example at https://jsfiddle.net/rushimusmaximus/9akf641z/3/
Vue.mixin({
created() {
console.log("created() in global mixin")
}
});
const mergeCreatedStrategy = Vue.config.optionMergeStrategies.created;
Vue.config.optionMergeStrategies.created = (parent, child) => {
return mergeCreatedStrategy(parent);
};
new Vue ({
el: "#vue-app",
template: '<p>See console output for logging. Rendered at {{renderDate}}</p>',
data() {
return {
renderDate: new Date()
}
},
created() {
console.log("created() in component")
}
})
Can I call mixin function from asyncData() method of the page component with Nuxt.js?
My code:
<template>
...
</template>
<script>
import api from "#/plugins/api/api.js"
...
export default {
...
async asyncData(context) {
...
context.apiMethodName()
...
}
...
}
...
</script>
api.js
import Vue from 'vue'
import API from '#/assets/js/api'
Vue.mixin({
methods: {
apiMethodName() { ... }
}
})
You cant call vue methods from withing asyncData, because asyncData executed before vue have an instance.
You can extract method into simple js function and call it both in asyncData and your vue method, but keep in mind that in asyncData you wont be able to access vue instance properties and other methods
You can inject mixin into app, see https://nuxtjs.org/guide/plugins#inject-in-root-amp-context
I see it is quite late for the answer, but it is possible.
template.vue
<template>
...
</template>
<script>
import api from "~/mixins/api/api"
...
export default {
...
async asyncData({context}) {
...
// You don't need to use context
// You have to use "api" like this:
const collection = await api.methods.apiMethodName()
...
// bear in mind you should return data from this function
return {
collection,
...
}
}
...
}
...
</script>
~/mixins/api/api.js
const api = {
...
methods: {
async apiMethodName() {
...
// better use here try / catch or Promise with then / catch
const data = await do_something()
...
return data
}
...
}
...
}
export api
A similar approach was tested with stack VueJS + NuxtJS and it is working on the live website https://elfinforce.com.
you can access global mixin methods like this:
app.router.app.gloablMethod()
so simple!
You need to abandon using mixins and inject your methods instead.
First; Replace your mixin method to be
export default ({ app }, inject) => {
inject('apiMethodName', () => {
return 'some data!';
})
}
Then, inside asyncData() method, call apiMethodName() function like so
async asyncData(context) {
context.app.$apiMethodName();
})
I have a bit of code that makes an api call to a server and returns some JSON.
It did exist as a method in my component but as it is getting a bit long I want to extract it to it's own file
In vuejs what is the best practice here.
should it be a component without a template? How would this work?
will I just create an es6 module?
I would suggest using a mixin here.
In a file like myCoolMixin.js define your mixin...
export default {
methods: {
myAwesomeMethod() {
//do something cool...
}
}
}
You can define anything in a mixin just like a component. e.g. data object, computed or watched properties, etc. Then you simply include the mixin in your component.
import myCoolMixin from '../path/to/myCoolMixin.js'
export default {
mixins: [myCoolMixin],
data: function() {
return: {
//...
}
},
mounted: function() {
this.myAwesomeMethod(); // Use your method like this!
}
}
More on Mixins here: https://v2.vuejs.org/v2/guide/mixins.html
Mixins work, or you could create a plugin. Here's the docs example:
MyPlugin.install = function (Vue, options) {
// 1. add global method or property
Vue.myGlobalMethod = function () {
// something logic ...
}
// 2. add a global asset
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// something logic ...
}
...
})
// 3. inject some component options
Vue.mixin({
created: function () {
// something logic ...
}
...
})
// 4. add an instance method
Vue.prototype.$myMethod = function (methodOptions) {
// something logic ...
}
}
Vue Plugins
How to pass values between exported part of a script and non-exported one?
The construction looks like this:
<script>
// PART 1:
import { EventBus } from './event-bus.js';
EventBus.$on('value-received', value => {
this.receivedValue = value;
});
// PART 2:
export default {
data() {
return {
receivedValue: ''
}
},
watch: {...},
methods: {...}
}
</script>
How can I get the value assigned to receivedValue variable and made usable by Vue methods?
Because your EventBus.$on call uses an arrow function, the this it references is the this in scope at the time of the EventBus call.
If you are okay with all instances of your event having the same receivedValue, you could redirect the values received to an object in the scope of your file:
var shared = { receivedValue: '' };
EventBus.$on('value-received', value => {
shared.receivedValue = value;
});
export default {
data() { return shared; }
watch: ...,
methods: ....
}
Vue.js will register a handler to react on changes to the object you returned, ie. shared.
If for some reason you want a separate event stream per instance of your component, you need to create a new object inside the data() function and have your event bus update the new object directly.