Vue - v-for with modifying values - vue.js

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>

Related

Component works only once using router-link - Laravel vue

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

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.

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

Vue js dynamically render router link path

I'm struggling to find a way to render the path of the router-link dynamically, for example with the test variable. I'm trying to bind :to but unsuccessfully. I don't know if I can use the ternary operator in the binding as shown below:
<template>
<div>
<router-link
:to="{ test ? '/success' : '/fail' }"
tag="button"
class="btn-next">
<span class="btn-text">BUTTON</span>
</router-link>
</div>
</template>
<script>
export default {
// ...
data: function() {
return {
test: false
};
}
}
</script>
Your solution is close (you don't need the curly brackets). It should look like this:
<router-link :to="test ? '/success' : '/fail'">
demo

defining the layout of child component from parent in Vue js

I'm new to Vue and using Vue 2.2.1. I am wondering if it's possible to create a reusable component that can have its layout defined by its parent. For example, consider the following pseudo code:
// Parent template
<template>
<ul>
<li v-for="item in items">
<item-component :id="item.id">
<h1><item-title /></h1>
<p>
<item-description />
</p>
</item-component>
</li>
</ul>
</template>
// Child definition
<script>
export default {
data() {
return {
title: '',
description: ''
}
}
create() {
// do some async fetch
fetch(this.id)
.then((result) {
this.$data.title = result.title
this.$data.description = result.description
})
}
}
</script>
So, the use case is that the child component is responsible for the fetching of the data by id, but the parent is responsible for laying out the data. This way, I can keep the fetch logic in one place, but reformat the data however I want in various places.
Not sure if this is possible or not. I suppose I can extract the child's fetching functionality out into a mixin, but then I'd have to create a new component for each layout variation. What is the recommended way to handle this in Vue?
In general, when you want the parent to include content in the child, the means to do it is via a slot. Inside, a typical slot, however, the scope is the parent's scope, meaning it does not have access to data inside the child.
In your case, you would want to use a scoped slot, which is where the child is able to pass some information back to the parent to use.
// Parent template
<template>
<ul>
<li v-for="item in items">
<item-component :id="item.id">
<template scope="props">
<h1>{{props.title}}</h1>
<p>
{{props.description}}
</p>
</template>
</item-component>
</li>
</ul>
</template>
// Child definition
<script>
export default {
template:"<div><slot :title='title' :description='description'></slot></div>",
data() {
return {
title: '',
description: ''
}
}
create() {
// do some async fetch
fetch(this.id)
.then((result) {
this.$data.title = result.title
this.$data.description = result.description
})
}
}
</script>