So you can create a plugin in nuxt like this:
const utils = {
name: utils,
emmitModel(name, val) {
const value = Object.assign({}, this.value)
value[name] = val
this.$emit('input', value)
},
}
export default ({ app }, inject) => {
inject('utils', utils)
}
However, in the above, 'this' is not defined. How do I access the 'this' component context inside emmitModel?
Assuming this references your Vue instance, wrap your utils object in a Vue instance, like so:
const utils = new Vue({
// ..stuff here
})
export default ({ app }, inject) => {
inject('utils', utils)
}
And then within the Vue object you can fully use component methods, computed, data, and access the VM and utils will be injected in nuxt as this.$utils
Related
I have created this initialization of CustomElement in VUE 3 from various sources on the web (doc's, stackoverflow, etc).
Unfortunately, nowhere was discussed how to deal with slots in this type of initialization.
If I understand it correctly, it should work according to the documentation.
https://vuejs.org/guide/extras/web-components.html#slots
import { defineCustomElement, h, createApp, getCurrentInstance } from "vue";
import audioplayer from "./my-audioplayer.ce.vue";
import audioplayerlight from "./my-audioplayerlight.ce.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
export const defineCustomElementWrapped = (component, { plugins = [] } = {}) =>
defineCustomElement({
styles: component.styles,
props: component.props,
setup(props, { emit }) {
const app = createApp();
plugins.forEach((plugin) => {
app.use(plugin);
});
const inst = getCurrentInstance();
Object.assign(inst.appContext, app._context);
Object.assign(inst.provides, app._context.provides);
return () =>
h(component, {
...props,
});
},
});
customElements.define(
"my-audioplayer",
defineCustomElementWrapped(audioplayer, { plugins: [pinia] })
);
customElements.define(
"my-audioplayerlight",
defineCustomElementWrapped(audioplayerlight, { plugins: [pinia] })
);
I suspect that I forgot something during initialization and the contents of the slot are not passed on.
A little late, but we are working with this approach doing Web Components with Vue 3 and this workaround, adding Vue Component context to Custom Elements.
setup(props, { slots })
And then:
return () =>
h(component, {
...props,
...slots
});
Thanks #tony19, author of this workaround.
I would like to create a Nuxt plugin that automatically adds a computed to components that have a certain property (without using a mixin).
For example, any component that have a addComputedHere property:
export default {
data() {
return {}
},
computed: {
myComputed: () => 'foo'
},
addComputedHere: true
}
would turn into:
export default {
data() {
return {}
},
computed: {
myComputed: () => 'foo',
injectedComputed: () => 'bar' // Injected
},
addComputedHere: true
}
So far, I'm not sure what's the best solution among using a Nuxt plugin/module/middleware or simply a Vue Plugin (if it's feasible).
How would you do it?
If anybody is in the same case, I found a solution by creating a Vue plugin that applies a mixin to customize the component in beforeCreate:
import Vue from 'vue';
const plugin = {
install(Vue, options) {
Vue.mixin({
beforeCreate() {
if (this.$options.addComputedHere) {
this.$options.computed['injectedComputed'] = () => 'bar';
}
}
})
}
};
Vue.use(plugin);
So I'm trying to convert this Vue2 project to Vue3(typescript).
It's registering components globally and accessing them to match against a value in my store, however when trying to implement this in Vue3 the components stay undefined.
import getComponentTypeForContent from "../api/getComponentTypeForContent";
import { mapState } from "vuex";
import { defineComponent } from "vue";
export default defineComponent({
name: "PageComponentSelector",
beforeCreate: function () {
console.log("CREATED PAGECOMPONENTSELECTOR");
},
computed: mapState({
model: (state) => state.epiDataModel.model,
modelLoaded: (state) => state.epiDataModel.modelLoaded,
}),
methods: {
getComponentTypeForPage(model) {
// this.$options.components will contain all globally registered components from main.js
return getComponentTypeForContent(model, this.$options.components);
// this.$options.components fetches all components for vue2 app
},
},
});
and registering components like this:
//Pages
import LoginPage from "./components/pages/Login.vue";
const appAdv = createApp(App);
//Register components
appAdv.component("LoginPage", LoginPage);
appAdv.use(store).use(router).mount("#appAdv");
Can't find (or searching badly) how to do this or similar in vue3 so I've come here hoping someone could help hehe
I also frequently use parent components in child components, but don't (or haven't) used the App.component method
In vue3 I usually use the provide/inject . method
Provide
const app = createApp(App);
app.provide('someVarName', someVar); // `Provide` a variable to all components here
Inject:
// In *any* component
const { inject } = Vue;
...
setup() {
const someVar = inject('someVarName'); // injecting variable in setup
return {someVar}
}
How am i able to mock $parent for my specs? When using shallowMount with my component i always get clientWidth/clientHeight of undefined. I already tried mocking $parent as an object with an $el as a key and two more nested keys for clientWidth and clientHeight, but that's not working as expected. I cannot figure out the right usage of parentComponent.
I've got a single file component as seen below:
<template>
<img :src="doSomething">
</template>
<script>
export default {
name: "Foobar",
data() {
return {
size: null
};
},
computed: {
doSomething() {
# here is some string concatenation etc.
# but not necessary for example
return this.size;
}
},
created() {
let parent = this.$parent.$el;
this.size = `size=${parent.clientWidth}x${parent.clientHeight}`;
}
};
</script>
creating the vue app looks like this:
import Vue from "vue";
import Foobar from "./Foobar";
const vueEl = "[data-vue-app='foobar']";
if (document.querySelector(vueEl)) {
new Vue({
el: vueEl,
components: {
"foo-bar": Foobar
}
});
}
and the combination of using slim with my component looks like this:
div data-vue-app="foobar"
foo-bar
This is my test setup:
import { shallowMount } from "#vue/test-utils";
import Foobar from "#/store/Foobar";
describe("Foobar.vue", () => {
let component;
beforeEach(() => {
component = shallowMount(Foobar, {});
});
The parentComponent mounting option expects a component object:
import Parent from "../Parent.vue";
...
beforeEach(() => {
component = shallowMount(Foobar, {
parentComponent: Parent
});
});
Also, try pinning the version of vue-test-utils to "^1.0.0-beta.28". This should allow your tests to complete, but the clientWidth/Height will still be 0x0
ref: https://vue-test-utils.vuejs.org/api/options.html#parentcomponent
I have an app which is contained in this div:
<div id="app" v-bind:style='{backgroundColor: backgroundColor}'>
... the app ...
</div>
The routing is done following the example in the documentation (this is a webpack project):
import Vue from 'vue/dist/vue.js'
import VueRouter from 'vue-router'
import ComponentOne from './component1.vue'
import ComponentTwo from './component2.vue'
Vue.use(VueRouter)
const routes = [{
path: '/foo',
component: ComponentOne
},
{
path: '/bar',
component: ComponentTwo
}
]
const router = new VueRouter({
routes // short for `routes: routes`
})
const app = new Vue({
router,
data: {
day: "Monday"
},
computed: {
backgroundColor: function () {
console.log(JSON.stringify(router.currentRoute))
if (router.currentRoute.path == "/foo") {
return "green"
} else {
return "blue"
}
}
}
}).$mount('#app')
I wanted the background to be dependent on the current route (router.currentRoute.path).
But, the solution above does not work, because router.currentRoute.path is not detected by the Vue instance as having changed (is not reactive).
What is the correct way to access the dynamic router data from within the Vue instance?
The router object created via new VueRouter is not reactive because Vue has no way to know to watch and update any object outside of its scope.
Passing router in the Vue config object is what allows the current route to be watched, but you need to reference it via this.$route:
if (this.$route.path == "/foo") {
...
}
You can also access the entire router object via this.$router, but its data is not reactive.
And if you are using Vue 2 with composition api setup() approach you can do this:
import { computed } from '#vue/composition-api'
export default {
setup (props, context) {
const params = computed ( () => context.root.$route.params)
const path = computed( () => context.root.$route.path)
I found on Vue's documentation page that tracks the router using watch for transition animations. Not sure if this is a best practice but you can use to.path or from.path to grab the path instead.
// then, in the parent component,
// watch the `$route` to determine the transition to use
watch: {
'$route': (to, from) => {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}