Duplicated key error on Vue composition API's return statement - vuejs2

Since Vuetify's support for Vue 3 is still in beta, I'm trying to use Composition API in Vue 2. I'm using it like this:
<script>
import { defineComponent } from '#vue/composition-api'
import { computed, toRef } from 'vue'
import fetchOtherImages from 'some-library'
export default defineComponent({
name: 'PhotoGallery',
props: {
images: {
type: Array,
required: true
}
},
setup(props) {
const { images: imagesFromProps } = toRef(props)
const images = computed(() => [
...imagesFromProps.value.map(image => image.getUrl()),
...fetchOtherImages()
])
return { images }
},
})
</script>
The problem is, it throws vue/no-dupe-keys in that return statement. Am I doing this properly? I'm new to the whole Composition thing and Vue 2's documentation on the subject is not helping.

https://eslint.vuejs.org/rules/no-dupe-keys.html
There are a data named images (returned by setup) and a prop named images, which is against rule vue/no-dupe-keys.
You can rename the data returned by setup:
setup(props) {
const { images: imagesFromProps } = toRef(props)
const images = computed(() => [
...imagesFromProps.value.map(image => image.getUrl()),
...fetchOtherImages()
])
return { imagesComputed: images }
},
By the way, highly recommend Vue 2.7 instead of Vue 2.6 + #vue/composition-api!

Related

Can a Vue component/plugin have its own pinia state, so that multiple component instances don't share the same state

I have a "standalone" component which is set up as a Vue plugin (to be downloaded via npm and used in projects) and it uses pinia, but it looks like multiple instances of the component share the same pinia state. Is there a way to set up pinia such that each component instance has its own state?
The component is made up of multiple sub-(sub)-components and I'm using pinia to manage its overall state. Imagine something fairly complex like a <fancy-calendar /> component but you could have multiple calendars on a page.
I have the standard pinia set up in an index.js:
import myPlugin from "./myPlugin.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
export function myFancyPlugin(app, options) {
app.use(pinia);
app.component("myPlugin", myPlugin);
}
Then myPlugin.vue has:
<script setup>
import { useMyStore } from '#/myPlugin/stores/myStore'
import { SubComponent1 } from '#/myPlugin/components/SubComponent1'
import { SubComponent2 } from '#/myPlugin/components/SubComponent2'
...
const store = useMyStore()
The sub-components also import the store. Also some of the sub-components also have their own sub-components which also use the store.
myStore.js is set up like this:
import { defineStore } from "pinia";
export const useMyStore = defineStore("myStore", {
state: () => ({
...
}),
getters: {
...
},
actions: {
...
}
});
Edit: This is the solution I ended up using:
myStore.js:
import { defineStore } from "pinia"
export const useMyStore = (id) =>
defineStore(id, {
state: () => ({
...
}),
getters: {},
actions: {},
})();
myPlugin.vue
...
<script setup>
import { provide } from "vue"
import { useMyStore } from '#/MyNewPlugin/stores/MyStore'
import { v4 } from "uuid"
const storeId = v4()
provide('storeId', storeId)
const store = useMyStore(storeId)
...
SubComponent1.vue
<script setup>
import { inject } from "vue"
import { useMyStore } from '#/MyNewPlugin/stores/MyStore'
const storeId = inject('storeId')
const store = useMyStore(storeId)
</script>
A simple way of solving this is to create a stores map, using unique identifiers:
When you init a new instance of the root component of your plugin, you create a unique identifier for the current instance:
import { v4 } from 'uuid'
const storeId = v4();
You pass this id to its descendants via props or provide/inject.
Whenever a descendent component calls the store, it calls it with the storeId:
const store = useMyStore(storeId)
Finally, inside myStore:
const storesMap = {};
export const useMyStore = (id) => {
if (!storesMap[id]) {
storesMap[id] = defineStore(id, {
state: () => ({ ... }),
actions: {},
getters: {}
})
}
return storesMap[id]()
}
Haven't tested it, but I don't see why it wouldn't work.
If you need hands-on help, you'll have to provide a runnable minimal reproducible example on which I could test implementing the above.

Slots not working in my VUE CustomElement (defineCustomElement)

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.

How I can get Vue component data while using script setup

I need to rewrite
this code
export default Vue.extend<Props>({
functional: true,
render(h, { props, slots, data }) {
const { href, external, target, type, button } = props;
const slot = slots().default;
console.log(data);
....
to Vue 3 Composition script setup,
So I managed to get
<script setup lang="ts">
import {h, useSlots} from 'vue';
const props = defineProps<Props>();
const slots = useSlots();
...
But how I can get data ?
from this part -> render(h, { props, slots, data }) {
data should contain domProps if have such.. etc
console.log(data);
{
{
attrs: {
target: '_self',
href: 'https://example.com',
button: true
},
class: [
'X-button',
{
'X-button--': false,
'X-button--size-auto': true
}
]
}
Thanks in advance
If you still need this,
useSlots().default() returns the data for the slots, including the DOM props.

Nuxt acces vuex store inside mixin

I m making small mixin for driving animations based on how user is crawling through page and I need to have access to data stored in Vuex store from that mixin.
Any ides how to pass data from vuex store there, I am getting mad.
pages/index.vue
import Vue from 'vue'
import {ToplevelAnimations} from '~/mixins/toplevel_animations'
export default Vue.extend({
mixins: [
ToplevelAnimations
]
})
mixins/toplevel_animations.js
export const ToplevelAnimations = {
transition(to, from) {
// some logic, here I need to access routes stored in store
return (to_index < from_index ? 'switch-to-left' : 'switch-to-right');
}
}
store/index.js
import {StoreExtensions} from "~/plugins/store_extensions.js";
export const state = () => ({
MAIN_NAV_LINKS: [
{
path: '/',
title: 'Domov',
order: 1
},
// ...etc
],
CURRENT_PAGE_INDEX: 1
})
export const getters = {
getMainNavLinks: (state, getters) => {
return state.MAIN_NAV_LINKS
}
// ..etc
}
export const mutations = {
setCurrentPageIndex(index) {
state.CURRENT_PAGE_INDEX = index
}
// ...etc
}
export const actions = {
nuxtServerInit ({ commit }, req ) {
var page_index = StoreExtensions.calculatePageIndex(req.route.path, state.MAIN_NAV_LINKS)
mutations.setCurrentPageIndex(page_index)
}
}
I never used Vue.extend(), always using .vue files, so it might be different (although it should not). From my experience with mixins in both Vue and Nuxt seems that mixins receive the relevant this context when they are assigned to components. So for me using simple this.$store worked.

AngularJS services in Vue.js

I'm new to Vue.js and looking for the equivalent of a service in AngularJS, specifically for storing data once and getting it throughout the app.
I'll be mainly storing the results of network requests and other promised data so I don't need to fetch again on very state.
I'm using Vue.JS 2.0 with Webpack.
Thanks!
I think what u are seeking for is vuex, which can share data from each component.
Here is a basic demo which from my code.
store/lottery.module.js
import lotteryType from './lottery.type'
const lotteryModule = {
state: {participantList: []},
getters: {},
mutations: {
[lotteryType.PARTICIPANT_CREATE] (state, payload) {
state.participantList = payload;
}
},
actions: {
[lotteryType.PARTICIPANT_CREATE] ({commit}, payload) {
commit(lotteryType.PARTICIPANT_CREATE, payload);
}
}
};
export default lotteryModule;
store/lottery.type.js
const PARTICIPANT_CREATE = 'PARTICIPANT_CREATE';
export default {PARTICIPANT_CREATE};
store/index.js
Vue.use(Vuex);
const store = new Vuex.Store();
store.registerModule('lottery', lotteryModule);
export default store;
component/lottery.vue
<template>
<div id="preparation-container">
Total Participants: {{participantList.length}}
</div>
</template>
<script>
import router from '../router';
import lotteryType from '../store/lottery.type';
export default {
data () {
return {
}
},
methods: {
},
computed: {
participantList() {
return this.$store.state.lottery.participantList;
}
},
created() {
this.$store.dispatch(lotteryType.PARTICIPANT_CREATE, [{name:'Jack'}, {name:'Hugh'}]);
},
mounted() {
},
destroyed() {
}
}
</script>
You don't need Vue-specific services in Vue2 as it is based on a modern version of JavaScript that uses Modules instead.
So if you want to reuse some services in different locations in your code, you could define and export it as follows:
export default {
someFunction() {
// ...
},
someOtherFunction() {
// ...
}
};
And then import from your Vue code:
import service from 'filenameofyourresources';
export default {
name: 'something',
component: [],
data: () => ({}),
created() {
service.someFunction();
},
};
Note that this is ES6 code that needs to be transpiled to ES5 before you can actually use it todays browsers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export