Nuxt - composition api and watchers - vue.js

I am trying to watch for some warnings in my component
import VueCompositionApi, { watch } from '#vue/composition-api';
import useWarning from '#composables/warnings';
Vue.use(VueCompositionApi);
setup () {
const { activeWarnings } = useWarning();
watch(activeWarnings, () => {
console.log('called inside on update')
});
}
In my composition function I just push into the reactive array to simulate warning.
import { reactive } from '#vue/composition-api';
export default function useWarnings () {
const activeWarnings = reactive([]);
setInterval(() => {
fakeWarning();
}, 3000);
function fakeWarning () {
activeWarnings.push({
type: 'severe',
errorCode: 0,
errorLevel: 'red',
}
);
}
return { activeWarnings };
Does this not work in Vue 2 at all? Is there a workaround? activeWarnings does update in my component - I see the array filling up but this watcher is never called.
I am using https://composition-api.nuxtjs.org/

Since activeWarnings is an array you should add immediate:true option :
const { activeWarnings } = useWarning();
watch(activeWarnings, () => {
console.log('called inside on update')
},{
immediate:true
});
or
const { activeWarnings } = useWarning();
watch(()=>activeWarnings, () => {
console.log('called inside on update')
},{
immediate:true
});
But i recommend the following syntax :
import { reactive,toRef } from '#vue/composition-api';
export default function useWarnings () {
const state= reactive({activeWarnings :[]});
setInterval(() => {
fakeWarning();
}, 3000);
function fakeWarning () {
state.activeWarnings.push({
type: 'severe',
errorCode: 0,
errorLevel: 'red',
}
);
}
return { activeWarnings : toRef(state,'activeWarnings')};
then :
const { activeWarnings } = useWarning();
watch(activeWarnings, () => {
console.log('called inside on update')
},{
immediate:true
});

Related

Test if imported method is called

I'm trying to test if a composable's method is called from my store. Here's what I have:
/stores/ui:
import { defineStore } from 'pinia';
import { useUtils } from '#/composables/utils';
const { sendEvent } = useUtils();
interface State {
tab: string,
}
export const useUIStore = defineStore('ui', {
state: (): State => ({
tab: 'Home',
}),
actions: {
setTab(tabName: string) {
this.tab = tabName;
sendEvent('navigation', `click_${tabName}`);
},
}
})
#/composables/utils:
export function useUtils() {
const sendEvent = (name: string, value: string) => {
// do stuff
};
return {
sendEvent
};
}
And here's my test file:
import { setActivePinia, createPinia } from 'pinia'
import { useUIStore } from '#/stores/ui';
import { useUtils } from '#/composables/utils';
describe('', () => {
let uiStore;
beforeEach(() => {
setActivePinia(createPinia());
uiStore = useUIStore();
})
it('Sets the active tab', () => {
let sendEvent = jest.spyOn(useUtils(), 'sendEvent');
expect(uiStore.tab).toBe('Home');
uiStore.setTab('help');
expect(uiStore.tab).toBe('help');
expect(sendEvent).toHaveBeenCalledTimes(1);
});
})
I've also tried mocking the import:
jest.mock(
'#/composables/utils',
() => {
function useUtils() {
return {
sendEvent: jest.fn(),
}
}
return {
useUtils
};
},
{ virtual: true },
);
import { useUtils } from '#/composables/utils';
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
What's the correct way of adding a spy to sendEvent ?
In your test file - mock the utils:
const mockSendEvent = jest.fn();
jest.mock("#/composables/utils", () => ({
useUtils: () => ({
sendEvent: mockSendEvent,
}),
}));
and then update your test to use the mock:
it('Sets the active tab', () => {
expect(uiStore.tab).toBe('Home');
uiStore.setTab('help');
expect(uiStore.tab).toBe('help');
expect(mockSendEvent).toHaveBeenCalledTimes(1);
});

vue-emoji-picker : import const from vuex module not yielding desired result

I am trying to add custom emoji set to vue-emoji-picker
based https://codepen.io/DCzajkowski/pen/gObWjEQ
I have implemented this with partial success. I get everything loaded, except "RECENT" not added to the emoji list. Any help is greatly appreciated.
//my store/index.js
import recentEmojis from "./modules/recentEmojis";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
recentEmojis,
},
// store/modules/recentEmojis/index.js
export const defaultEmojis = {
"Frequently used": {
thumbs_up: "+1",
red_hreat: "heart",
},
People: {
},
Nature: {
},
Objects: {
},
Places: {
},
Symbols: {
},};
const getName = (emoji) => {
console.log("getName");
const emojiMap = Object.values(defaultEmojis).reduce(
(a, b) => ({ ...a, ...b }),
{});
const object = Object.entries(emojiMap).find(
// eslint-disable-next-line no-unused-vars
([_name, value]) => value === emoji
);
return object ? object[0] : null;
};
export default {
namespaced: true,
defaultEmojis,
state: {
recentlyUsedEmojis: [],
},
mutations: {
recentlyUsedEmojis: (state, recentlyUsedEmojis) =>
(state.recentlyUsedEmojis = recentlyUsedEmojis),
},
actions: {
addEmojiToRecent: ({ state, commit }, emoji) => {
const name = getName(emoji);
const rest = state.recentlyUsedEmojis
.map(
// eslint-disable-next-line no-unused-vars
([_name, value]) => value
)
.includes(emoji)
? state.recentlyUsedEmojis.filter(
// eslint-disable-next-line no-unused-vars
([_name, value]) => value !== emoji
)
: state.recentlyUsedEmojis.length > 5
? state.recentlyUsedEmojis.slice(0, -1)
: state.recentlyUsedEmojis;
commit("recentlyUsedEmojis", [[name, emoji], ...rest])},},
getters: {
recentlyUsedEmojis: (state) =>
state.recentlyUsedEmojis.length
? { Recent: Object.fromEntries(state.recentlyUsedEmojis) }
: {},
},
}
//in my vue instance # src/pages/edtor.default.vue
...
<emoji-picker :emoji-table="emojis" #emoji="append" :search="search">
....
<script> import axios from "axios";
import EmojiPicker from "vue-emoji-picker"
import { defaultEmojis } from "../../store/modules/recentEmojis/index.js" // <<<<
export default { name: "ABCD", components: { EmojiPicker, },
...
computed: { emojis()
{ return { ...this.$store.getters.recentlyUsedEmojis, ...defaultEmojis } }, },
......
methods: {
append(emoji)
{ this.input += emoji this.$store.dispatch("recentEmojis/addEmojiToRecent", emoji) },
}
replaced
...this.$store.getters.recentlyUsedEmojis,
with
...this.$store.getters['recentEmojis/recentlyUsedEmojis']

How to insert a function into a directive Vue 3?

I need to write a specific global directive that should perform the same function in different hooks.
How can I implement something like this
export default {
directives: {
widthAsChild: {
widthAsChild(el) {
el.style.width = getComputedStyle(el.firstChild).getPropertyValue(
"width"
);
},
mounted(el) {
widthAsChild(el);
window.addEventListener("resize", () => {
widthAsChild(el);
});
},
},
},
}
The function has to be declared outside the directive's declaration object. One way to do that is to use an IIFE that contains a local method that you can can reuse in the returned directive declaration:
export default {
directives: {
widthAsChild: (() => {
const widthAsChild = el => {
el.style.width = getComputedStyle(el.firstChild).getPropertyValue("width");
}
return {
mounted(el) {
widthAsChild(el);
window.addEventListener("resize", () => {
widthAsChild(el);
});
},
}
})()
}
}
demo 1
Alternatively, you could move that to a separate file:
// #/directives/widthAsChild.js
const widthAsChild = el => {
el.style.width = getComputedStyle(el.firstChild).getPropertyValue('width')
}
export default {
mounted(el) {
widthAsChild(el)
window.addEventListener('resize', () => {
widthAsChild(el)
})
}
}
// MyComponent.vue
import widthAsChild from '#/directives/widthAsChild'
export default {
directives: {
widthAsChild,
}
}
demo 2

How to add a module getters in composition api?

I am using vue 2, installed composition api. How can I add Getters?
Usually:
computed: {
...mapGetters("Auth", ["isLogged"])}
..........................................................................
setup() {
const title_app = ref("Name App");
const logout = () => {
store
.dispatch("Auth/logout")
.then(() => {
router.push({ name: "About" });
})
.catch((err) => {
console.log(err);
});
};
return {
title_app,
logout,
};
},
You can do something like this:
import { computed } from 'vue'
export default {
setup (props, { root }) {
const isLogged = computed(() => root.$store.Auth.getters.isLogged)
return {
isLogged
}
}
}

Test Error Comparing Props and Data

I have a component that when created() sets date: with null or with props.datainicial.
export default {
props: ['numeroDaParcela', 'datainicial'],
data () {
return {
date: null,
dateBR: '',
modal: false
}
},
created () {
if (this.datainicial === '' ||
this.datainicial === undefined) {
this.date = null
} else {
this.date = this.datainicial
}
}
In DevTools with no props:
In DevTools with some props:
When I do my test:
import { mount } from 'vue-test-utils'
import SelecionadorData from '#/components/Shared/SelecionadorData.vue'
describe('SelecionadorData.vue', () => {
it('should receive the props datainicial', () => {
const wrapper = mount(SelecionadorData)
wrapper.setProps({
datainicial: '2018-01-01'
})
expect(wrapper.vm.date).toBe('2018-01-01')
})
})
I get this error:
created only runs 1 time when component is created.
When you use setProps, component props will be updated but created method will not be called again.
import { mount } from 'vue-test-utils'
import SelecionadorData from '#/components/Shared/SelecionadorData.vue'
describe('SelecionadorData.vue', () => {
it('should receive the props datainicial', () => {
const wrapper = mount(SelecionadorData,
{
propsData: {
datainicial: '2018-01-01'
}
})
expect(wrapper.vm.date).toBe('2018-01-01')
})
})