How to make dynamic routes on the vue router? - vue.js

My MainNavBar component like this :
<template>
...
<v-list-item
v-for="(item, index) in listMenu"
:key="index"
#click="goTo(item)"
>
<v-list-item-content>
<v-list-item-title>{{ item }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
...
</template>
<script>
export default {
...
methods: {
goTo(key) {
this.$router.push({ name: key });
},
...mapActions("dataStore", ["getMenu"])
},
computed: {
...mapGetters("dataStore", ["listMenu"])
}
};
</script>
listMenu taken from API. It is a menu list
My router configuration like this :
import Vue from "vue";
import Router from "vue-router";
import Application from "#/pages/Application"
import MainNavbar from "#/layout/MainNavbar";
...
import { APPLICATION_ROUTE as RouteConfig } from "./route-config";
Vue.use(Router);
export default new Router({
mode: 'history',
routes: [
{
path: "/menu-first",
name: RouteConfig.APPLICATION_NAME_1.NAME,
components: { default: Application, header: MainNavbar }
},
{
path: "/menu-two",
name: RouteConfig.APPLICATION_NAME_2.NAME,
components: { default: Application, header: MainNavbar }
},
...
]
});
My RouterConfig like this :
export const APPLICATION_ROUTE = {
APPLICATION_NAME_1: {
NAME: "menu-first"
},
APPLICATION_NAME_2: {
NAME: "menu-two"
},
...
};
And my application component like this :
<template>
<v-flex xs12>
<v-card dark color="light-blue darken-4">
<v-card-title>
<v-flex xs4>
<p class="title">Name</p>
<p class="body-2 margin-sub-text">{{detailMenu.name}}</p>
</v-flex>
<v-flex xs4>
<p class="title">URL</p>
<p class="body-2 margin-sub-text">{{detailMenu.url}}</p>
</v-flex>
<v-flex xs4>
...
</v-flex>
</v-card-title>
</v-card>
</v-flex>
</template>
<script>
import { mapActions, mapState, mapGetters } from "vuex";
export default {
..
created() {
this.getDetailMenu(this.$route.path);
},
computed: mapState({
data: state => state.dataStore.data,
...mapGetters("dataStore", ["detailMenu"])
}),
methods: {
...mapActions("dataStore", ["getDetailMenu"]),
},
watch: {
$route() {
this.getDetailMenu(this.$route.path);
}
}
};
</script>
From the configuration router, my router is not dynamic. I want to make my router dynamic. So the path in the router configuration is taken from listMenu (API)
How do I do that?

I suppose getDetailMenu is calling API method to get listMenu.
You can create route dynamically using addRoutes method
Pseudo code
created() {
this.getDetailMenu(this.$route.path)
.then((listMenu) => {
// you need to return listMenu from getDetailMenu
listMenu.forEach((item, index) => createAndAppendRoute(item, index))
})
},
methods: {
createAndAppendRoute: (item, index) => {
console.log(item, index)
// Base on your data, you should be able to create router name and path from item and index
// here is just an example
let newRoute = {
path: `/${item}`,
name: `${item}_${index}`,
components: { default: Application, header: MainNavbar },
}
this.$router.addRoutes([newRoute])
}
}

This link should help :
https://router.vuejs.org/guide/essentials/navigation.html
goTo(key) {
this.$router.push({ path: key });
}

you can use the router.addRoutes method (https://router.vuejs.org/api/#router-addroutes):
router.addRoutes(routes: Array<RouteConfig>)
Dynamically add more routes to the router. The argument must be an Array using the same route config format with the routes constructor option.

Related

Vue-apollo refetch/update data after prop change

I'm having a problem with refeching data when my prop(id) is changing.
-- moveing between profiles (profile/1 to profile/2) --
I scour through the internet to find some information. Tried this.$forceUpdate, some methods too but nothing seems to resolve my problem. Adding :key to components only refresh first one "profile-header" but not working on other in "section".
Any idea to how to solve it? I will appreciate any help.
Im new to vue.
profile.vue
<template>
<div
v-if="Object.keys(user).length"
id="user-profile"
>
<profile-header
:header-data="user"
/>
<!-- profile info -->
<section id="profile-info">
<b-row>
<b-col
lg="3"
cols="12"
order="2"
order-lg="1"
>
<profile-about
:about-data="user"
/>
<profile-suggested-pages
:pages-data="user"
/>
</b-col>
<b-col
lg="6"
cols="12"
order="1"
order-lg="2"
>
<profile-post
:posts="user"
/>
</b-col>
</b-row>
</section>
</div>
</template>
<script>
import gql from 'graphql-tag'
import ProfileHeader from './ProfileHeader.vue'
import ProfileAbout from './ProfileAbout.vue'
import ProfileSuggestedPages from './ProfileSuggestedPages.vue'
import ProfilePost from './ProfilePost.vue'
export default {
components: {
ProfileHeader,
ProfileAbout,
ProfileSuggestedPages,
ProfilePost,
},
data() {
return {
routeParam: this.$route.params.id,
user: { },
}
},
watch: {
$route(from) {
console.log(this.$route.params.id)
console.log(this.user.id)
},
},
apollo: {
user: {
query: gql`
query User($id: ID!) {
user(id: $id) {
// query //
`,
variables() {
return { id: this.routeParam }
},
},
},
}
</script>
routes
{
path: '/profil/:id',
name: 'profile',
component: () => import('#/views/profile/Profile.vue'),
props: true,
}

Vue.js 2 data not displayed in the template

I'm using vue.js 2 / vue-cli with axios. I'm facing a problem with the display of my posts (in the wall). I've made my axios request, have gotten my data (in the console), I've written my template and ... nothing was displayed ... I really appreciate your help
my template :
<template>
<v-layout column>
<v-flex xs4>
<panel title="The Wall">
<p v-for="post in posts" :key="post.index">{{ post.title }} - {{ post.content }}</p>
</panel>
</v-flex>
</v-layout>
</template>
my script :
<script>
import Panel from '../components/Panel'
import PostService from "../services/PostService.js";
export default {
components: {
Panel
},
data() {
return {
posts: null
}
},
async mounted() {
this.posts = (await PostService.getAllPosts()).data;
}
}
</script>
Add a data property called cidItem for example and bind it to your props as follows
<template>
<div id="app" class="container" :cid="cidItem">
<Images :cid="cidItem" />
<Video :cid="cidItem" />
<TextType :cid="cidItem" />
<Card :cid="cidItem" />
</div>
</template>
<script>
import axios from 'axios';
import Images from "./components/Images";
import Video from "./components/Video";
import TextType from "./components/TextType";
import Card from "./components/Card";
export default {
name: 'app',
props: ["cid"],
components: {
Images,
Video,
TextType,
Card
},
mounted() {
axios({method: "GET", "url": this.contentDeliveryUrl}).then(result => {
// eslint-disable-next-line
this.content = amp.inlineContent(result.data)[0];
console.log(this.content)
}, error => {
console.error(error);
});
},
data() {
return {
contentDeliveryUrl: 'https://c1.adis.ws/cms/content/query?fullBodyObject=true&query=%7B%22sys.iri%22%3A%22http%3A%2F%2Fcontent.cms.amplience.com%2F${this.cid}%22%7D&scope=tree&store=testing',
content: [],
cidItem:'7e4301de-9c6e-4fab-9e68-3031b94d662d'
}
}
}
</script>
Since your component have the same structure i recommend to use mixins, create a file named myMixins.js and add the following code inside it :
const myMixins = {
props:['cid'],
mounted() {
axios({
method: "GET",
"url": this.contentDeliveryUrl
}).then(result => {
// eslint-disable-next-line
this.content = amp.inlineContent(result.data)[0];
console.log(this.content)
}, error => {
console.error(error);
});
},
data() {
return {
contentDeliveryUrl: 'https://c1.adis.ws/cms/content/query?fullBodyObject=true&query=%7B%22sys.iri%22%3A%22http%3A%2F%2Fcontent.cms.amplience.com%2F${this.cid}%22%7D&scope=tree&store=testing',
content: []
}
}
}
export default mixins;
and inside each component add this :
import myMixins from './myMixins'
export default{
....
mixins: [myMixin]
}

add Base64UploadAdapter for ckeditor

I use component locally method for use ckeditor and add Base64UploadAdapter plugin in config file.
but i have error ckeditor-duplicated-modules
how can i add truly Base64UploadAdapter plugin to ckeditor in nuxt component for Reuse this component.
//ckEditorNuxt
<template>
<ckeditor
:editor="editor"
:value="value"
:config="config"
:tagName="tagName"
:disabled="disabled"
#input="event => $emit('input', event)"
/>
</template>
<script>
let Classiceditor
let CKEditor
if (process.client) {
Classiceditor = require('#ckeditor/ckeditor5-build-classic')
CKEditor = require('#ckeditor/ckeditor5-vue2')
} else {
CKEditor = {component: {template: '<div></div>'}}
}
export default {
name: "ckEditorNuxt",
components: {
ckeditor: CKEditor.component
},
...and more
and use in this code.
<template>
<v-card height="100%">
<h4 class="pa-2">{{ title }}</h4>
<client-only placeholder="loading...">
<ckEditorNuxt v-model="contentHolder" :config="editorConfig"/>
</client-only>
</v-card>
</template>
<script>
import ckEditorNuxt from "#/components/ckEditor/ckEditorNuxt";
import {Base64UploadAdapter} from "#ckeditor/ckeditor5-upload";
export default {
name: "PersianCKEditor",
components: {
ckEditorNuxt,
},
props: {
title: String
},
data() {
return {
editorConfig: {
plugins: [
Base64UploadAdapter
],
placeholder: this.title,
removePlugins: ['Title'],
and more...

NuxtJS component - component renders without data

I have a component that uses fetch to bring data via Axios and return data to render. I am using the store to set/get the data. The component renders with empty data and after debugging I see that data() method is called before fetch() method.
How to fix the problem and bring the data before the component is rendered
here is the component code:
<template>
<v-card
class="mx-auto"
max-width="500"
>
<v-sheet class="pa-4 primary lighten-2">
<v-text-field
v-model="search"
label="Search Company Directory"
dark
flat
solo-inverted
hide-details
clearable
clear-icon="mdi-close-circle-outline"
></v-text-field>
<v-checkbox
v-model="caseSensitive"
dark
hide-details
label="Case sensitive search"
></v-checkbox>
</v-sheet>
<v-card-text>
<v-treeview
:items="items"
:search="search"
:filter="filter"
:open.sync="open"
>
<template v-slot:prepend="{ item }">
<v-icon
v-if="item.children"
v-text="mdi-${item.id === 1 ? 'home-variant' : 'folder-network'}"
></v-icon>
</template>
</v-treeview>
</v-card-text>
</v-card>
</template>
<script>
export default {
async fetch(){
console.log("Hi from Fetch !!!")
let response = await this.$axios.get('/items/tasks', {baseURL: 'http://localhost:8055'});
let tasks = response.data.data;
debugger
this.$store.commit('SET_ASSIGNMENTS', tasks);
},
data () {
debugger
console.log("data assignments: ", this.$store.state.assignments);
return {
items: this.$store.state.assignments,
open: [1, 2],
search: null,
caseSensitive: false,
}
},
computed: {
filter () {
return this.caseSensitive
? (item, search, textKey) => item[textKey].indexOf(search) > -1
: undefined
},
}
}
</script>
For this I use vuex this way:
const appStore = {
state () {
return {
data: [],
}
},
getters: {
data(state) {
return state.data
},
},
mutations: {
SET_ASSIGNMENTS(state, payload) {
state.data = payload
},
},
actions: {
async getData({ commit }, {fromDate, toDate}) {
let response = await this.$axios.get('/items/tasks', {baseURL: 'http://localhost:8055'});
let tasks = response.data.data;
commit("SET_ASSIGNMENTS", tasks);
}
}
}
export default appStore
Component code is like this:
<template>
. . .
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: "MyComponent",
components: {
. . .
},
computed: {
...mapGetters({
data: 'data'
})
},
mounted(){
this.getData();
},
methods: {
getData() {
this.$store.dispatch('getData')
}
}
}
</script>
data is not reactive, you can create a computed property that returns your items
ex:reactiveItems() {return this.items}

Vuejs - Same component with different data

I am trying to re-use the same component, but load the components with different data. I thought simply providing unique id's would do the trick, but no luck. I switched from a Vuex store for this data, to using a dataShare This is what I'm doing:
The components:
<logoHeader :panel=0 title="Add your logo / header" id="top" topPadding="pt-10" />
<logoHeader :panel=1 title="Add your main image" id="main" topPadding="pt-0"/>
So its the exact same component, with some different props and different ids
This is the logoHeader component:
<template>
<v-row
:class="topPadding"
align="center"
justify="center"
>
<v-col
align="center"
justify="center"
cols="12"
>
<v-hover v-slot:default="{ hover }">
<v-card
:elevation="hover ? 12 : 0"
class="mx-auto"
max-width="600"
>
<v-img
v-if="showImage"
:src="imageUrl"
max-width="600px"
class="pointer"
#click="panel = 0"
>
</v-img>
<v-icon
v-if="!showImage"
class="my_dark_purple_text"
size="100"
#click="sendToPanel"
>add_box</v-icon>
<p class="my_dark_purple_text">{{ title }}</p>
<p>URL {{ imageUrl }}</p>
<p>Show image? {{ showImage }}</p>
</v-card>
</v-hover>
</v-col>
</v-row>
</template>
<script>
import {mapGetters} from 'vuex';
import {mapActions} from 'vuex';
import {dataShare} from '../../../packs/fresh-credit.js';
export default {
props: ['panel', 'title', 'topPadding'],
data() {
return {
imageUrl: "",
showImage: false,
}
},
created() {
dataShare.$on('imageUrl', (data) => {
this.imageUrl = data;
});
dataShare.$on('showImage', (data) => {
this.showImage = data;
});
},
computed: {
...mapGetters('emailPanel', [
'returnPanel'
]),
},
methods: {
...mapActions('emailPanel', [
'updatePanel'
]),
sendToPanel() {
this.updatePanel(this.panel);
},
},
}
</script>
And then finally this is where the data enters the system:
<template>
<v-expansion-panel-content>
<h1 class="subtitle-1 font-weight-bold">Only images files accepted</h1>
<v-file-input
v-model="image"
accept="image/*"
label="Image Upload"
prepend-icon="add_a_photo"
color='#68007d'
></v-file-input>
<v-btn
:disabled="disableUpload"
color="#68007d"
class="white--text"
#click="sendImage"
>Submit</v-btn>
</v-expansion-panel-content>
</template>
<script>
import axios from 'axios';
import {dataShare} from '../../../../packs/fresh-credit.js';
export default {
data() {
return {
image: null,
disableUpload: true,
}
},
watch: {
image: function() {
if(this.image.size > 0){
this.disableUpload = false;
}
else{
this.disableUpload = true;
}
}
},
computed: {
},
methods: {
sendImage() {
let formData = new FormData();
formData.append('file', this.image);
axios.post('/admin/features/images', formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(response => {
dataShare.$emit('imageUrl', response.data);
dataShare.$emit('showImage', true);
});
}
},
}
</script>
Where did I go astray?
Add the key property to the components and set it to different values (for example 1 and 2). If the key has different values Vue will differentiate them when rendering. Here is a basic explanation.