Component works only once using router-link - Laravel vue - vue.js

I am working on a project that uses Laravel Vue SPA, and I got a problem accessing the data of the single product, it's only working when I click once, but when I select again with other product, I can't get the product data, but the URL is correct it's changes the ID but can't access the data.
It is working when I click another link like Services then select product, it can display the product data.
Here is my HTML
<ul >
<li v-for="product in products">
<router-link :to="{ name: 'edit-product', params: { id: product.id } }">
{{ product.title }}
</router-link>
</li>
</ul>
My Vue Routes
const EditProduct = require('./components/EditProduct').default;
{
path: '/edit/product/:id',
component: EditProduct,
name: 'edit-product'
}
my EditProduct component
<template>
<div>
<h1>this.$route.params.id</h1>
</div>
</template>
Cheers

Look at this answer. You have to watch the productId and execute your logic again.
Possible solution

try to define a computed property inside your EditProduct component for get product id
computed: {
productId(){
return this.$route.params.id
}
}
and use productId inside your <h1> tag
<template>
<div>
<h1>{{ productId }}</h1>
</div>
</template>
update
solution :
add this watcher to your EditProduct component for reacting with params change
watch: {
$route(to) {
console.log(to.params.id)
// you can call your method here and pass product id to them for example: this.getProductDetails(to.params.id)
},
beforeRouteEnter (to, from, next) {
next(vm => {
//also call your method here => vm.getProductDetails(to.params.id)
});
},
you can read more about params changes here: Reacting to Params Changes

Related

Nuxt.js dynamic nested views and params

I have a question about dynamic and nested routes in Nuxt.
I will create a kind of calculator. The user can choose a theme and get on the first dynamic routes - called _slug.vue - a dynamic navigation from the API about this theme and inside this navigation the user get the detail information about this theme.
On the _slug.vue page is also a dynamic navigation, from the REST API that trigger on the same page the view like: http://localhost/slug/slug2
first question:
user trigger a theme on startpage, get the navigation on _slug.vue
How can I set the first menu item to active so that its content is displayed directly without the user having to click on the menu item first?
the child-view has to be changed if user click to ther menu items
child view can enter directly on browser and is also available
second question
I need to parse in the second step many params to create all user choices, to share the link to other users.
I will get this by creating vuex store "cart"
how can i implement this with the dynamic routes?
like:
http://localhost/slug/slug2?param1=test&param2=test2
http://localhost/slug/slug10?param1=test&param2=test2
You can see that the secondary slug is dynamic, the first slug is only dynamic by choosing this on startpage.
Here is my code:
pages/index.vue
<template>
<nav class="navigation">
<ul>
<li
v-for="(link, index) in links"
:key="index"
>
<nuxt-link
:to="link.shortName"
:data-code="link.shortName"
>
{{ link.name }}
</nuxt-link>
</li>
</ul>
</nav>
</template>
pages/_slug.vue
<template>
<div class="calculator">
<nav class="navigation">
<ul>
<li
v-for="(link, index) in $store.state.calculatorNavigation"
:key="index"
>
<nuxt-link
:to="{ path: '/' + $store.state.carResult.id + '/' + link.id}"
:title="link.name"
append
>
{{ link.shortName }}
</nuxt-link>
</li>
</ul>
</nav>
<nuxt-child></nuxt-child>
</div>
</template>
pages/slug/_category.vue
<template>
<div>
here show the data for every menu point from the first /slug like /slug/slug2
</div>
</template>
Nuxt.js adding special classnames to all nuxt-link instances depending on the current location. By default it sets nuxt-link-active if current location starts with the URL of the nuxt-link and nuxt-link-exact-active if it is exact match (classnames can be adjusted in config).
In Dynamic Pages to get the params of the route you must use asyncData:
<!-- pages/_slug.vue -->
<template>
<h1>{{ this.slug }}</h1>
</template>
<script>
export default {
async asyncData({ params }) {
const slug = params.slug // When calling /abc the slug will be "abc"
return { slug }
}
}
</script>
The same way you could get the query string:
<!-- pages/_slug.vue -->
<template>
<h1>{{ this.slug }} / {{ this.param1 }}</h1>
</template>
<script>
export default {
async asyncData({ params, query }) {
const slug = params.slug
const param1 = query.param1 // When calling /abc?param1=test the param will be "test"
return { slug, param1 }
}
}
</script>
In case you need to access route's params and query on the client-side as well, you can still use $route builtin (ie. for data fetching).
<script>
export default {
mounted() {
const slug = this.$route.params.slug
const param1 = this.$route.query.param1
// some fetching here
}
}
</script>
So, now the structure of your pages should be like this:
pages/
├── _slug/
│ ├── index.vue --> ie. http://localhost/slug/
│ └── _category.vue --> ie. http://localhost/slug/slug2
└── index.vue --> http://localhost/
So, in _slug.vue router parameter slug will be available. And in _category.vue both slug and category params will be available.

Vue - v-for with modifying values

Template:
<template>
<nav>
<router-link :to="key" v-for="(value, key) in state.menu" :key="key">{{value}} | </router-link>
</nav>
</template>
Code:
export default {
setup() {
const menu = ['home', 'news', 'about'];
const state = reactive({
menu: {}
});
menu.map(item => {
state.menu[item] = Common.locs[item];
});
return {
Common,
state,
}
}
}
I want to update
:to="key"
to run it through some filter or function to modify it to add the prefix something like "/other/", so instead of rendered
<a href="/home" ...>
I would have
<a href="/other/home" ... >
(Of course, const menu values are mocked for the sake of example; those are fetched from the service so I have no saying in that, and I don't want to modify them after fetching for other reasons)
So in general, my question is not regarding proper routing, but how do you modify v-for data (in my case: key) in the runtime if you need to?
(I guess I could modify mapping line into
state.menu["/other/" + item] = Common.locs[item];
but I want to preserve state.menu as I wrote. I want change to happen during the v-for rendering.)
Have you tried the template literal:
<template>
<nav>
<router-link :to="`/other/${key}`" v-for="(value, key) in state.menu" :key="key">{{value}} | </router-link>
</nav>
</template>

VueJS making API calls for every item in v-for and returning them to the right position

Thank you in advance.
So I am fetching list of blog categories via API and rendering it in a list using v-for.
I also need to fetch the amount of blogs in every category and place them beside the category.
But the issue is I am calling a method that calls the api.
<li v-for="item in sidebar" :key="item.identifier">
<nuxt-link
tag="a"
:to="{
name: 'blog-page',
query: { category: item.identifier }
}"
>{{ $localize(item.translations).title }}
{{ getBlogCount(item.identifier) }}
</nuxt-link>
</li>
You know what it shows already example is Animals [Object Promise]
methods: {
async getBlogCount(identifier) {
axios
.get(
"https://example.com/posts?fields=created_at&filter[category.category_id.identifier]=" +
identifier +
"&meta=*"
)
.then(count => {
return count.data.meta.result_count;
});
}
}
What is the best way to handle this kinda thing?
You better call async methods in mounted or created hooks, and set the result to data, and then, use that data in template.
I'd suggest handling this in Script, instead of HTML Template.
What you can do is, depending on when the sidebar is initialized (maybe in the mounted hook), call getBlogCount method to fetch blog counts for each item in sidebar and store that may be in an array or object (or as a separate key-value pair to that same sidebar item object) and then use that data structure to display count values in the template.
Assuming the sidebar is populated in mounted hook and that it's an array of objects, you can do the following:
<template>
<li v-for="item in sidebar" :key="item.identifier">
<nuxt-link
tag="a"
:to="{
name: 'blog-page',
query: { category: item.identifier }
}"
>{{ $localize(item.translations).title }}
{{ item.blogCount }}
</nuxt-link>
</li>
</template>
<script>
mounted () {
// after the sidebar is populated
this.sidebar = this.sidebar.map(async item => {
item.blogCount = await this.getBlogCount(item.identifier)
return item
})
}
</script>
Hope this helps you out

how to redirect to specific component in vue js

I want to redirect inside a URL without page refresh, without using a router link as below :
<router-link to="/about us " active-class="active">foo</router-link>
I want to print routes like below:
<li class="nav-item phone">
<a class="nav-link" href="contact-us.html">
اتصل بنا
</a>
</li>
My route:
const routes = [
{ path: '/aboutus/', component: AboutUs }
]
Try this
this.$router.push('about')
You may need a workaround for this.
This solution won't change the url either :)
Set an html in the data
data: () => {
return {
html: null
}
}
Get the content of your html file using any request and assign to the html in data section. You can fetch this from any life cycle hook. Here I'm using beforeMount.
beforeMount() {
this.fetchAllEmployees();
axios.get('contact-us.html')
.then(response => {
this.html = response.data;
})
}
Now you can show the html content in your component like this
<template>
<div>
<div v-html="html"></div>
</div>
</template>
To show only when clicking the a tag, you can add another variable in the data which can be used to toggle the value.

Where should route meta data be loaded in a Vue app?

I'm in the process of setting up a VueJs SPA. I'm using vue-router and I'm trying to find the best solution to the following problem. I have a series of routes. Each of which needs to call an API to get the meta data for the given ID.
/industry/:id/overview
/industry/:id/top-stories
/industry/:id/top-tweets
/brand/:id/overview
/brand/:id/top-stories
/brand/:id/top-tweets
I've been looking at using created or beforeRouteEnter/beforeRouteUpdate and I'm a bit lost. Ideally, I would only fetch new data when a new /industry/:id is reached, not when navigating between pages within the same ID. Also, I'd like to avoid having to define the fetch to grab data in every page component. Also don't want to over complicate this, so my question is, Is there a standard method for tackling this issue?
Clarification:
When I say meta here, I mean data returned from an API about the given industry or brand which I pull using the ID in the route. The api call includes the name of the industry/brand which I want to have on page as soon as the page is presented to the user.
I have something similar. I tackle this using the following approach:
I use the same component for all /industry/:id Vue likes to reuse components wherever it can so if two routes (for example /industry/:id/overview and /industry/:id/top-stories) are using the same component it will stay the same.
What does change, however, is the route meta. So if you add a page key to the meta object in the route objects, and probably add a computed property called page that return this.$route.meta.page, you can use v-if attributes to conditionally render any component. So you might have something like <div v-if="page === 'overview'"></div><div v-else-if="page==='top-stories'"></div>
What this allows you to do is fetch all the data from the API during created or mounted lifecycle and store it as the state. Since the route change doesn't reload the component the state stays the same.
Here is a code example
// router.js
const Project = () =>
import(/* webpackChunkName: "projects" */ "./views/projects/_id");
export default new Router({
mode: "history",
routes: [
{
path: "/projects/:project_id/views",
name: "ViewProject",
component: Project,
meta: {
page: "views",
}
},
{
path: "/projects/:project_id/export",
name: "ExportProject",
component: Project,
meta: {
page: "exports"
}
},
{
path: "/projects/:project_id/recommendations",
name: "ProjectRecommendations",
component: Project,
meta: {
page: "recommendations"
}
},
]
});
And here is the template
<template>
<div v-if="project">
<h1>{{ project.name }}</h1>
<router-link :to="/project/someid/views">Views</router-link>
<router-link :to="/project/someid/exports">Exports</router-link>
<router-link :to="/project/someid/recommendations">Recommendations</router-link>
<ul v-if="page==='views">
<li v-for="(view, i) in project.views" :key="i">{{ views }}</div>
</ul>
<ul v-else-if="page==='exports">
<li v-for="(export, i) in project.exports" :key="i">{{ export }}</div>
</ul>
<ul v-else-if="page==='recommendations">
<li v-for="(recommendation, i) in project.recommendations" :key="i">{{ recommendation }}</div>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
project: null
}
},
computed: {
page() {
return this.$route.meta.page;
}
},
mounted() {
this.getProject()
},
methods: {
getProject() {
axios
.get(`/projects/someid`)
.then(res => this.project = res.data)
}
}
}
</script>