I am trying to include a dynamic route component in a Vue3 application using axios to connect to a Django API but instead I am getting the below mistakes. I am using Vite.dev. I follow the docs to use create method but i am a little lost in documents and examples.
first errors:
src/views/Page.vue:27:11 - error TS2339: Property 'page' does not exist on type '{ getPage(pageSlug: any): Promise<void>; }'.
27 this.page = response.data
~~~
src/views/Page.vue:35:14 - error TS2339: Property 'getPage' does not exist on type '{ name: string; components: { PageComponent: { props: string[]; }; }; data(): { page: {}; }; methods: { getPage(pageSlug: any): Promise<void>; }; created(): Promise<void>; }'.
35 await this.getPage(pageSlug)
~~~~~~~
src/views/Page.vue:36:8 - error TS2339: Property '$watch' does not exist on type '{ name: string; components: { PageComponent: { props: string[]; }; }; data(): { page: {}; }; methods: { getPage(pageSlug: any): Promise<void>; }; created(): Promise<void>; }'.
36 this.$watch (() => this.route.params, this.getPage())
~~~~~
src/views/Page.vue:36:27 - error TS2339: Property 'route' does not exist on type '{ name: string; components: { PageComponent: { props: string[]; }; }; data(): { page: {}; }; methods: { getPage(pageSlug: any): Promise<void>; }; created(): Promise<void>; }'.
36 this.$watch (() => this.route.params, this.getPage())
~~~~~
src/views/Page.vue:36:46 - error TS2339: Property 'getPage' does not exist on type '{ name: string; components: { PageComponent: { props: string[]; }; }; data(): { page: {}; }; methods: { getPage(pageSlug: any): Promise<void>; }; created(): Promise<void>; }'.
36 this.$watch (() => this.route.params, this.getPage()) ~~~~~~~
Found 5 errors.
I have edited my Page.vue script like below but does not help me to build in production
<template>
...
<PageComponent :page="page" />
....
</template>
<script lang=ts>
export default {
name: 'Page',
components: {
PageComponent
},
data() {
return {
page: {}
}
},
methods: {
async getPage(pageSlug:any) {
let page = {}
await axios
.get(`/api/v1/pages/${pageSlug}/`)
.then(response => {
console.log(response.data)
this.page = response.data
})
},
},
async created() {
const route = useRoute()
const pageSlug = route.params.slug
await this.getPage(pageSlug)
this.$watch (() => this.route.params, this.getPage())
}
};
</script>
and my pageComponent.vue:
<template>
<h1 class="title is-1">{{ page.title }}</h1>
<div class="container has-size-6">
<div class="columns is-centered is-vcentered is-mobile">
<div class="column is-narrow has-text-centered is-6 has-text-left has-text-weight-medium" v-html="page.content">
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
props: ['page']
}
</script>
and my tsconfig
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
and my viteconfig:
const { resolve } = require('path');
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: '/static/',
resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
build: {
outDir: resolve('./dist'),
assetsDir: '',
manifest: true,
emptyOutDir: true,
target: 'es2015',
rollupOptions: {
input: {
main: resolve('./src/main.ts'),
},
output: {
chunkFileNames: undefined,
},
},
}
})
How can I fix the errors ?
Thanks
ps here is what I have to solve it according to #tony19 answer:
<script lang="ts">
import { defineComponent } from 'vue'
import PageComponent from './../components/PageComponent.vue'
import axios from 'redaxios'
import { useRoute } from 'vue-router'
export default defineComponent({
name: 'Page',
components: {
PageComponent
},
data() {
return {
page: Object
}
},
methods: {
async getPage() {
const route = useRoute()
const pageSlug = route.params.slug
await axios
.get(`/api/v1/pages/${pageSlug}/`)
.then(response => {
this.page= response.data
})
},
},
async created() {
const route = useRoute()
await this.getPage()
this.$watch (() => route.params, () => this.getPage())
}
});
</script>
To enable type inference in components, use the defineComponent wrapper around the component declaration:
<script lang="ts">
import { defineComponent } from 'vue'
👇
export default defineComponent({
created() {
// 💡 type inference enabled
}
})
</script>
Related
I want to implement unit testing using vue-test utils for a vue component which uses a global event handler as a vue instance called responseBus. Need to implement unit test using jest.
Here is the code for vue component.
<script>
import NewQuestionBuilder from '#/components/NewQuestionBuilder.vue'
export default {
inject: ['$responseBus'],
props: {
question: {
type: Object,
required: true,
},
},
mounted() {
this.$responseBus.$on('pseudo', (payload) => {
if (payload.questionCode === this.question.questionCode) {
this.$emit('update:initialValue', payload)
}
})
},
render(h) {
return h('div', { class: 'pl-0 md:pl-4 w-full ' }, [
h(NewQuestionBuilder, { props: { questionData: this.question } }),
])
},
}
</script>
Here is the unit test file.
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vue from 'vue'
import Component from '../BaseComputed'
const GlobalPlugins = {
install(v) {
v.prototype.$eventBus = new Vue()
},
}
const localVue = createLocalVue()
localVue.use(GlobalPlugins)
describe('BaseComputed', () => {
const mocks = {
$responseBus: {
$on: jest.fn(),
$off: jest.fn(),
$emit: jest.fn(),
},
}
it('listens to event pseudo', () => {
const wrapper = shallowMount(Component, {
mocks,
propsData: {
imageUrl: '',
label: ``,
initialValue: [],
isEnabled: true,
code: '',
triggers: [],
choices: [],
question: {
questionCode: 'HFFIN0416USOTEL',
},
},
})
wrapper.vm.$responseBus.$emit('pseudo', {
questionCode: `HFFIN0416USOTEL`,
})
expect(wrapper.vm.question.questionCode).toEqual('HFFIN0416USOTEL')
expect(wrapper.vm.$responseBus.$on).toHaveBeenCalledTimes(1)
expect(wrapper.vm.$responseBus.$on).toHaveBeenCalledWith(
'pseudo',
jest.fn((payload) => {
if (payload.questionCode === this.question.questionCode) {
this.$emit('update:initialValue', payload)
}
})
)
})
})
Still couldn't test the 'psuedo' method. Guys need help to achieve this.
I am recieving "Critical dependency: require function is used in a way in which dependencies cannot be statically extracted friendly-errors 16:21:14" error when using the package scrollMonitor in my nuxt project
plugins/scroll-monitor.js
import Vue from 'vue';
// your imported custom plugin or in this scenario the 'vue-session' plugin
import ScrollMonitor from 'scrollmonitor';
Vue.use(ScrollMonitor);
nuxt.config.js
plugins: [
'~/plugins/wordpress-api',
{ src: '~/plugins/scroll-monitor.js', ssr: false }
],
build: {
/*
** You can extend webpack config here
*/
vendor: ['scrollmonitor'],
extend(config, ctx) {
}
}
At my index.vue file
let scrollmonitor
if (process.client) {
scrollmonitor = require('scrollmonitor')
}
More context
Still not working.
I am using new computer, at my old one everything is working fine.
index.vue
<template>
<div class="index page-padding-top">
<TheHero
:scaledUpDot="scaledUpDot"
:isFirstImageVisible="isFirstImageVisible"
/>
<ProjectsList :projects="projects" />
</div>
</template>
<script>
import { mapGetters } from "vuex";
import TheHero from "~/components/TheHero";
import ProjectsList from "~/components/ProjectsList";
export default {
async mounted () {
if (process.browser) {
const scrollMonitor = await import('scrollmonitor')
Vue.use(scrollMonitor)
console.log('HELLO FROM MOUNTED')
}
},
name: "Index",
components: { TheHero, ProjectsList},
data() {
return {
scaledUpDot: false,
isFirstImageVisible: false,
};
},
computed: {
...mapGetters({
projects: "getProjects",
}),
},
mounted() {
this.handleScaling();
this.hideScrollSpan();
},
destroyed() {
this.handleScaling();
this.hideScrollSpan();
},
methods: {
handleScaling() {
if (process.client) {
const heroSection = document.querySelectorAll(".hero");
const heroSectionWtcher = scrollMonitor.create(heroSection, 0);
heroSectionWtcher.enterViewport(() => {
this.scaledUpDot = true;
});
}
},
hideScrollSpan() {
if (process.client) {
const images = document.querySelectorAll(".projects-home img");
const firstImage = images[0];
const imageWatcher = scrollMonitor.create(firstImage, -30);
imageWatcher.enterViewport(() => {
this.isFirstImageVisible = true;
});
}
},
},
};
</script>
In my old computer I have it imported like this :
import { mapGetters } from 'vuex'
import scrollMonitor from 'scrollmonitor'
But when I want to run this in a new one I get an error that window is not defined
So I have started to add this plugin in other way and still not working
Still not working.
I am using new computer, at my old one everything is working fine.
index.vue
<template>
<div class="index page-padding-top">
<TheHero
:scaledUpDot="scaledUpDot"
:isFirstImageVisible="isFirstImageVisible"
/>
<ProjectsList :projects="projects" />
</div>
</template>
<script>
import { mapGetters } from "vuex";
import TheHero from "~/components/TheHero";
import ProjectsList from "~/components/ProjectsList";
export default {
async mounted () {
if (process.browser) {
const scrollMonitor = await import('scrollmonitor')
Vue.use(scrollMonitor)
console.log('HELLO FROM MOUNTED')
}
},
name: "Index",
components: { TheHero, ProjectsList},
data() {
return {
scaledUpDot: false,
isFirstImageVisible: false,
};
},
computed: {
...mapGetters({
projects: "getProjects",
}),
},
mounted() {
this.handleScaling();
this.hideScrollSpan();
},
destroyed() {
this.handleScaling();
this.hideScrollSpan();
},
methods: {
handleScaling() {
if (process.client) {
const heroSection = document.querySelectorAll(".hero");
const heroSectionWtcher = scrollMonitor.create(heroSection, 0);
heroSectionWtcher.enterViewport(() => {
this.scaledUpDot = true;
});
}
},
hideScrollSpan() {
if (process.client) {
const images = document.querySelectorAll(".projects-home img");
const firstImage = images[0];
const imageWatcher = scrollMonitor.create(firstImage, -30);
imageWatcher.enterViewport(() => {
this.isFirstImageVisible = true;
});
}
},
},
};
</script>
In my old computer I have it imported like this :
import { mapGetters } from 'vuex'
import scrollMonitor from 'scrollmonitor'
But when I want to run this in a new one I get an error that window is not defined
So I have started to add this plugin in other way and still not working
I'm running into a strange situation and can't figure out why. Basically in my HTML, if I render 'actor[0]', the test runs fine and the console log shows the entire 'actor' object present in setData
However, if I try to access a property of the 'actor' object, like actor[0].firstname, the test throws a TypeError-can't-read-property-of-undefined.
The weird part is console logging 'wrapper.vm.actor[0].firstname' works fine so it doesn't seem like an async issue.
myapps.spec.js
import { mount } from "#vue/test-utils";
import MyApps from "#/pages/myapps.vue";
import Vuetify from "vuetify";
describe("Testing Myapps", () => {
let vuetify;
beforeEach(() => {
vuetify = new Vuetify();
});
it("Checks SideBarComponent is rendered", async () => {
const wrapper = mount(MyApps, {
// localVue,
vuetify,
mocks: {
$vuetify: { breakpoint: {} }
},
stubs: {
SideBarComponent: true,
FooterComponent: true
}
});
await wrapper.setData({
actor: [
{
firstname: "bob",
lastname: "bob",
group: "actors"
}
]
});
console.log(wrapper.html()); // TypeError: Cannot read property 'first name' of undefined
console.log(wrapper.vm.actor[0].firstname); // "bob" if I set the template back to actor[0] so the test runs
});
});
myapps.vue
<template>
<div>
<v-app>
<v-col cols="3">
<v-btn
text
#click="getAcceptedApplications"
elevation="0"
block
>Accepted {{actor[0].firstname}}</v-btn>
</v-col>
</v-app>
</div>
</template>
<script>
export default {
async asyncData({ params, $axios, store }) {
try {
const body = store.getters.loggedInUser.id;
const [applications, actor] = await Promise.all([
$axios.$get(`/api/v1/apps/`, {
params: {
user: body
}
}),
$axios.$get(`/api/v1/actors/`, {
params: {
user: body
}
})
]);
return { applications, actor };
if (applications.length == 0) {
const hasApps = false;
}
} catch (error) {
if (error.response.status === 403) {
const hasPermission = false;
console.log(hasPermission, "perm");
console.error(error);
return { hasPermission };
}
}
},
data() {
return {
actor: []
};
}
};
</script>
Try not to use setData method, pass data while mounting the component like that:
const wrapper = mount(MyApps, {
vuetify,
mocks: {
$vuetify: { breakpoint: {} }
},
stubs: {
SideBarComponent: true,
FooterComponent: true
}
data: () => ({
actor: [
{
firstname: "bob",
lastname: "bob",
group: "actors"
}
]
})
})
I am creating a settings page, where I fetch some data from the API and I am using Vuex to handle mutations.
I can see that the Vuex completes properly, but value for my dailyCount variable doesn't update in frontend.
This is my Settings component:
<template>
<div>
<div class="row col">
<h1>Settings</h1>
</div>
<div class="row col">
<div class="well">
<form class="form-inline">
<input type="number" v-model="dailyCount" />
{{ dailyCount }}
</form>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'settings',
data () {
return {
dailyCount: 500
};
},
created () {
this.$store.dispatch('settings/fetchSetting');
},
computed: {
isLoading() {
return this.$store.getters['user/isLoading'];
},
hasError() {
return this.$store.getters['user/hasError'];
},
error() {
return this.$store.getters['user/error'];
},
},
}
</script>
I do mutations here:
import SettingsAPI from '../api/settings';
export default {
namespaced: true,
state: {
isLoading: false,
error: null,
settings: null,
},
getters: {
isLoading (state) {
return state.isLoading;
},
hasError (state) {
return state.error !== null;
},
error (state) {
return state.error;
},
user (state) {
return state.user;
},
},
mutations: {
['FETCHING_SETTINGS'](state) {
state.isLoading = true;
state.error = null;
state.settings = null;
},
['FETCHING_SETTINGS_SUCCESS'](state, settings) {
state.isLoading = false;
state.error = null;
state.settings = settings;
},
['FETCHING_SETTINGS_ERROR'](state, error) {
state.isLoading = false;
state.error = error;
state.settings = null;
},
},
actions: {
fetchSetting ({commit}) {
commit('FETCHING_SETTINGS');
return SettingsAPI.get()
.then(res => {commit('FETCHING_SETTINGS_SUCCESS', res.data);})
.catch(err => commit('FETCHING_SETTINGS_ERROR', err));
},
},
}
And call to a server is done here (api/settings.js - it is imported in mutation file):
import axios from 'axios';
export default {
get() {
return axios.get('/user');
},
}
Can you see what am I doing wrong? I am trying to debug it using Vuejs debug toolbar, but all seems to work fine.
You need to get store state from vuex and inject to Vue component, either by this.$store.state or this.$store.getters.
For example:
<script>
export default {
name: 'settings',
data () {
return {
dailyCount: 500
};
},
created () {
this.$store.dispatch('settings/fetchSetting');
},
computed: {
isLoading() {
return this.$store.getters['user/isLoading'];
},
hasError() {
return this.$store.getters['user/hasError'];
},
error() {
return this.$store.getters['user/error'];
},
settings() {
return this.$store.state.settings
}
},
watch: {
settings () {
this.dailyCount = this.settings.dailyCount
}
}
}
</script>
I am trying to map an action to a component using mapActions helper from vuex. Here is my labels.js vuex module:
export const FETCH_LABELS = 'FETCH_LABELS'
export const FETCH_LABEL = 'FETCH_LABEL'
const state = () => ({
labels: [
{ name: 'Mord Records', slug: 'mord', image: '/images/labels/mord.jpg'},
{ name: 'Subsist Records', slug: 'subsist', image: '/images/labels/subsist.jpg'},
{ name: 'Drumcode Records', slug: 'drumcode', image: '/images/labels/drumcode.png'},
],
label: {} // null
})
const mutations = {
FETCH_LABEL: (state, { label }) => {
state.label = label
},
}
const actions = {
fetchLabel({commit}, slug) {
let label = state.labels.filter((slug, index) => {
return slug == state.labels[index]
})
commit(FETCH_LABEL, { label })
},
}
const getters = {
labels: state => {
return state.labels
},
label: (state, slug) => {
}
}
export default {
state,
mutations,
actions,
getters
}
Here is my component _slug.vue page where I want to map the fetchLabel action:
<template>
<div class="container">
<div class="section">
<div class="box">
<h1>{{ $route.params.slug }}</h1>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
data() {
return {
title: this.$route.params.slug
};
},
computed: {
// Research
// labels() {
// return this.$store
// }
...mapGetters({
labels: "modules/labels/labels"
})
},
components: {},
methods: {
...mapActions({
fetchLabel: 'FETCH_LABEL' // map `this.add()` to `this.$store.dispatch('increment')`
})
},
created() {
console.log('created')
this.fetchLabel(this.$route.params.slug)
},
head() {
return {
title: this.title
}
},
layout: "app",
}
</script>
<style>
</style>
However inside the created() lifecycle hook at this.fetchLabel(this.$route.params.slug) it throws the following error in the console:
[vuex] unknown action type: FETCH_LABEL
What am I missing or doing wrong? Please help me solve this.
Note that in Nuxt.js:
Modules: every .js file inside the store directory is transformed as a namespaced module (index being the root module).
You are using:
Here is my labels.js vuex module:
with labels.js as you stated above so you'll need to access everything as namespaced modules so your mapAction helper should be like as such:
methods: {
...mapActions({
nameOfMethod: 'namespace/actionName'
})
}
So you would have this:
...mapActions({
fetchLabel: 'labels/fetchLabel'
})
You could also clean it up by doing so for when you'd like to retain the name of your action as your method name.
...mapActions('namespace', ['actionName']),
...
So you would have this:
...mapActions('labels', ['fetchLabel']),
...
In both cases the computed prop should work without a problem.
Your action name is fetchLabel and not FETCH_LABEL (which is a mutation). In mapActions change to
methods: {
...mapActions({
fetchLabel
})
},