I have a URL: https://local.test/real-price?city_id=2
Vue template
<template>
<ul>
<li
v-for="city in states"
:key="city.id"
:id="city.id"
v-bind:class="{ active: (city.id === city_id) }"
>
{{ city.name }}
</li>
</ul>
</template>
<script>
export default {
data: function() {
return {
states: [],
city_id: 0,
};
},
created() {
this.states = [{id: 1, name: "City A"}, {id: 2, name: "City B"}, {id: 3, name: "City C"}];
},
mounted() {
this.city_id = this.$route.query.city_id ? this.$route.query.city_id : 0
},
}
</script>
Result not auto add class "active" when city_id=2
This is because the city_id variable is a number in your code while it's a string in the url.
Try parsing it in your mounted
this.city_id = this.$route.query.city_id ? parseInt(this.$route.query.city_id) : 0
Another way of solving this is to use ==
v-bind:class="{ active: (city.id == city_id) }"
Related
What is an appropriate way to achieve a nested "n" levels of routes based on currently selected category ?
I would like to properly access the routes, so my URL would look like this:
/categories
/categories/outfit/
/categories/outfit/jackets/
/categories/outfit/jackets/mountains/
/categories/outfit/jackets/mountains/you_get_the_idea
I have a list of categories like so:
const categories = [
{
name: 'outfit',
childs: [
{ name: 'jackets',
childs: [
{ name: 'mountains' }
]
},
{ name: 'pants', childs: ['many subcategories'] },
{ name: 'boots', childs: ['many subcategories'] }
]
},
{
name: 'knives',
childs: [
{ name: 'hunting' },
{ name: 'souvenirs' },
{ name: 'kitchen' },
{ name: 'skinning' },
{ name: 'throwing' }
]
}
]
I have a main page of Categories (TOP LEVEL):
<div class="col-6" v-for="category in categories" :key="category.id">
<div class="block q-pa-md">
<router-link :to="'/categories/' + category.slug">
<h4>{{ category.name }}</h4>
</router-link>
</div>
</div>
And there is a nested page for 1st level of childs:
<h1>{{ category.name }}</h1>
<q-img :src="category.image"/>
<p>{{ category.description }}</p>
<div class="row q-col-gutter-md">
<div class="col-md-4 col-xs-6" v-for="subcategory in category.childs" :key="subcategory.id">
<div class="block q-pa-md">
<router-link :to="'/categories/' + subcategory.id">
<h4>{{ subcategory.name }}</h4>
</router-link>
</div>
</div>
</div>
How would I do some repeating nested childs ?
Dynamic Route Matching feature of Vue router allows to define a dynamic parameter which captures more than one segment of the path by defining your route as /categories/:categoryId+ (router uses path-to-regex to match)
When defined this way, route as /categories/outfit/jackets/mountains will have a categoryId parameter with value outfit/jackets/mountains, which can be passed into the component, easily parsed and worked with...
See my example below:
Vue.config.devtools = false
Vue.config.productionTip = false
const categories = [{
name: 'outfit',
childs: [{
name: 'jackets',
childs: [{
name: 'mountains'
}]
},
{
name: 'pants',
childs: [{
name: 'short'
}, {
name: 'long'
}]
},
{
name: 'boots',
childs: [{
name: 'barefoot'
}, {
name: 'mountains'
}]
}
]
},
{
name: 'knives',
childs: [{
name: 'hunting'
},
{
name: 'souvenirs'
},
{
name: 'kitchen'
},
{
name: 'skinning'
},
{
name: 'throwing'
}
]
}
]
const cat = Vue.component('categories', {
data: function() {
return {
categories: categories // in real life, this data is shared for example using Vuex
}
},
template: `
<div>
<template v-for="cat in categories">
<router-link :to="'/categories/'+cat.name" :key="cat.name"> {{ cat.name }} </router-link>
</br>
</template>
</div>
`
})
const catView = Vue.component('category-view', {
data: function() {
return {
categories: categories // in real life, this data is shared for example using Vuex
}
},
props: ['categoryId'],
template: `
<div>
<hr>
<router-link :to="parentCategoryPath"> <- Back </router-link>
<div>Params: {{ categoryId }}</div>
<hr>
<template v-if="categoryDefinition && categoryDefinition.childs">
<template v-for="cat in categoryDefinition.childs">
<router-link :to="$route.path+ '/' +cat.name" :key="cat.name"> {{ cat.name }} </router-link>
</br>
</template>
</template>
</div>
`,
computed: {
categoryDefinition() {
let subCategory = this.categories.find(cat => cat.name === this.categoryId[0]);
for (i = 1; i < this.categoryId.length; i++) {
subCategory = subCategory.childs.find(cat => cat.name === this.categoryId[i])
}
return subCategory
},
parentCategoryPath() {
return '/categories/' + this.categoryId.slice(0, -1).join('/')
}
}
})
const router = new VueRouter({
base: '/js',
mode: 'history',
routes: [{
path: '/',
redirect: '/categories',
},
{
path: '/categories',
component: cat,
},
{
path: '/categories/:categoryId+',
component: catView,
props: route => ({
categoryId: route.params.categoryId.split('/')
})
}
]
})
const vm = new Vue({
el: '#app',
router,
data: function() {
return {}
}
})
hr {
border: none;
border-top: 3px double #333;
color: #333;
overflow: visible;
text-align: center;
height: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.5.1/vue-router.min.js" integrity="sha512-c5QVsHf336TmWncPySsNK2ncFiVsPEWOiJGDH/Le/5U6q1hU6F5B7ziAgRwCokxjmu4HZBkfWsQg/D/+X3hFww==" crossorigin="anonymous"></script>
<div id="app">
{{ $route.path }}
<router-view></router-view>
</div>
We are trying to select another item from drop down options but it doesn't changing the value:
<b-dropdown aria-role="list">
<b-button
icon-right="caret-down"
class="dropdown"
size="is-small"
slot="trigger"
>
<span>{{selectedProduct}}</span>
</b-button>
<b-dropdown-item
v-for="product in products"
:key="product.id"
aria-role="listitem"
#click="setItem(selectedProduct)"
v-model="selectedProductTag"
>
{{ product.name }}
</b-dropdown-item>
</b-dropdown>
**js part**
editProduct(){
...
...
this.selectedProductTag = {
id: selectedProductId,
name: product.name,
tag: product.tag,
};
}
setItem(selectedProduct) {
this.selectedProduct = selectedProduct
},
How to make it change both the selected option labels as well as v-model object data?
I see you're using a bootstrap-vue component, but, for what you're doing, I would suggest you to use the select component instead. But anyways, using the dropdown component:
In your HTML
<b-dropdown aria-role="list" v-bind:text="(selectedProduct.id == 0? 'Select here' : selectedProduct.name)">
<b-dropdown-item
v-for="(product, index) in products"
:key="index"
aria-role="listitem"
#click="setItem(product)"
>{{ product.name }}</b-dropdown-item>
</b-dropdown>
In your data section
data() {
products: [
{
id: 1,
name: "test1",
value: 1,
etc: "..."
},
{
id: 2,
name: "test2",
value: 2,
etc: "..."
}
//....
],
selectedProduct: {
id: 0,
name: "",
value: 0,
etc: "..."
}
//Make sure not to set selectedProduct to `null`, as it could result on an error
}
In your methods section
setItem: function(product) {
this.selectedProduct = product;
},
I have some routes which are newly added. the sidebar is dynamically created based on links added to the routes.
I am able to print the route name in plain text but when assigned to the vue-route it simple gives localhost:8080 so where am i going wrong.
configroutes file:
const routes = [
{
path: 'create_schedule',
name: 'activate.create_schedule',
meta: {
_routeName: 'activate_create_schedule',
sectionName: 'Create Schedule'
},
component: createSchedule,
},
]
Main Routes File
import ConfigureRoutes from './configureRoutes.js';
const routes = [
...ConfigureRoutes,
];
export default routes;
export const getActivateConfigRoutes = function () {
return routes;
};
dashboard Component file
data() {
const configRoutes = getActivateConfigRoutes();
const sidebarRoutes = [
{
name: '/',
meta: {
sectionName: 'CONFIGURE'
},
redirect: {
name: 'activate.create_schedule'
},
children: [
...configRoutes
]
},
];
return {
sidebarRoutes
};
}
}
</script>
aside bar:
<aside class="menu">
<ul class="menu-list --campaign-sidebar">
<li class="main-section-menu" v-for="(sidebarRoute, index) in sidebarRoutes" :key="sidebarRoute.id">
<router-link :to="{ name: sidebarRoute.name, params: { campaign_id: currentCampaign.id }}" class="sidebar-link" active-class="is-active">
{{ sidebarRoute.meta.sectionName }}
</router-link>
<ul class="sub-menu-list" v-if="sidebarRoute.children.length > 0">
<li v-for="childRoute in sidebarRoute.children" :key="childRoute.id">
<router-link :to="{ name: childRoute.name , params: { campaign_id: currentCampaign.id }}" class="sidebar-sub-link" active-class="is-active-submenu router-link-active">
{{ childRoute.meta.sectionName }} {{ childRoute.name }}
</router-link></li>
</ul>
<span class="base" v-if="((sidebarRoutes.length - 1) === index)"></span>
</li>
</ul>
</aside>
you can see what when I try to print childRoute.name, it gives me the name and so that data is passed properly to the loop. then what is the issue here ? can someone help on the same ?
[vue-router] Route with name 'activate.create_schedule' does not exist vue-router.esm.js:16
So I have a button dropdown which is working as expected but I have a bug where I can't reuse the same component as they both dont work independently of each other but instead when one is clicked the other changes too.
Please find a JSFiddle below and my code.
Thanks
<div id="app">
<div v-on:click="menuVisible = !menuVisible" class="dropdownBtn">
<div v-bind:class="{ active : menuVisible }"class="dropdownBtn__title">
<span v-if="!btnTitle">exploring</span>
<span v-else>{{ btnTitle }}</span>
</div>
<div v-show="menuVisible" class="dropdownBtn__content">
<ul v-for="reason in reasons">
<li v-on:click="updateTitle(reason)">{{ reason.title }}</li>
</ul>
</div>
</div>
<div v-on:click="menuVisible = !menuVisible" class="dropdownBtn">
<div v-bind:class="{ active : menuVisible }"class="dropdownBtn__title">
<span v-if="!btnTitle">exploring</span>
<span v-else>{{ btnTitle }}</span>
</div>
<div v-show="menuVisible" class="dropdownBtn__content">
<ul v-for="reason in reasons">
<li v-on:click="updateTitle(reason)">{{ reason.title }}</li>
</ul>
</div>
</div>
</div>
new Vue({
el: '#app',
data() {
return {
menuVisible: false,
btnTitle: false,
reasons: [{
title: 'family fun',
value: 1
},
{
title: 'relaxing',
value: 2
},
{
title: 'dining',
value: 3
},
{
title: 'meetings & events',
value: 4
},
{
title: 'what\'s on',
value: 5
},
{
title: 'gold',
value: 6
}]
}
},
methods: {
updateTitle($event) {
this.btnTitle = $event.title
}
}
})
So, for one, you haven't actually made a reusable component for the dropdown. You need to define that using Vue.component. Then you can use the tag for the custom component in the root template.
Secondly, if you are binding to the same data, then that is what will be reflected in both templates. You'll still need to pass separate data to the two separate dropdown components.
Vue.component('dropdown', {
template: `
<div v-on:click="menuVisible = !menuVisible" class="dropdownBtn">
<div v-bind:class="{ active : menuVisible }"class="dropdownBtn__title">
<span v-if="!btnTitle">exploring</span>
<span v-else>{{ btnTitle }}</span>
</div>
<div v-show="menuVisible" class="dropdownBtn__content">
<ul v-for="reason in reasons">
<li v-on:click="updateTitle(reason)">{{ reason.title }}</li>
</ul>
</div>
</div>
`,
props: ['menuVisible', 'btnTitle', 'reasons'],
methods: {
updateTitle($event) {
this.btnTitle = $event.title
}
}
})
new Vue({
el: '#app',
data() {
return {
fooReasons: [
{ title: 'family fun', value: 1 },
{ title: 'relaxing', value: 2 },
{ title: 'dining', value: 3 },
{ title: 'meetings & events', value: 4 },
{ title: 'what\'s on', value: 5 },
{ title: 'gold', value: 6 }
],
barReasons: [
{ title: 'family bar', value: 1 },
{ title: 'bar relaxing', value: 2 },
{ title: 'bar dining', value: 3 },
{ title: 'meetings & bars', value: 4 },
{ title: 'bar\'s on', value: 5 },
{ title: 'gold bar', value: 6 }
]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<dropdown :menu-visible="false" :btn-title="false" :reasons="fooReasons"></dropdown>
<dropdown :menu-visible="false" :btn-title="false" :reasons="barReasons"></dropdown>
</div>
My vue component is like this :
<a :href="baseUrl+'/message/inbox'"
:class="{ 'active': currentPath === '/message/inbox' }"
>
Message
</a>
If they meet the conditions then the message menu will be active
But, I want to make it like this :
<a :href="baseUrl+'/message/inbox'"
:class="{ 'active': currentPath in array ('/message/inbox', '/message/inbox/detail') }"
>
Message
</a>
So it will check currentPath in the array
How can I do it?
Update :
If I have menu again like this :
<a :href="baseUrl+'/store/sale'"
:class="{ 'active': currentPath in array ('/store/sale', '/store/sale/detail') }"
>
Sale
</a>
Or more menu
How to implement it?
Update 2
<a :href="baseUrl+'/message/inbox'"
:class="{ 'active': isActive }"
>
Message
</a>
<a :href="baseUrl+'/store/sale'"
:class="{ 'active': isActiveSale }"
>
Message
</a>
computed: {
isActive () {
return ['/message/inbox', '/message/inbox/detail'].indexOf(this.currentPath) > -1
},
isActiveSale () {
return ['/store/sale', '/store/sale/detail'].indexOf(this.currentPath) > -1
}
}
You can use computed properties :
computed: {
currentPathInInbox: function() {
var arrayInbox = ['/message/inbox', '/message/inbox/detail'];
return arrayInbox.indexOf(this.currentPath) > -1;
}
}
and in template :
:class="{ 'active': currentPathInInbox }"
or with no computed properties :
:class="{ 'active': (currentPath === '/message/inbox' || (currentPath === '/message/inbox/detail') }"
UPDATED :
I think you need component :
Vue.component( 'linkWithPath', {
template: '<div><a :href="baseUrl + relativeUrl"' +
':class="{ \'active\': isActive }">' +
'<slot>Link name</slot></a></div>',
props: {
baseUrl: { type: String },
currentPath: { type: String, default: '' },
relativeUrl: { type: String }
},
computed: {
isActive: function() {
return [ this.relativeUrl, this.relativeUrl + '/detail'].indexOf(this.currentPath) > -1;
}
}
});
Vue.component( 'listOfLinksWithPath', {
template: '<div><link-with-path v-for="menuItem in menuArray"' +
':key="menuItem" :base-url="baseUrl" :current-path="currentPath"' +
':relative-url="menuItem.url">{{ menuItem.name }}</link-with-path></div>',
props: {
baseUrl: { type: String },
currentPath: { type: String },
menuArray: { type: Array }
}
});
new Vue({
el: "#app",
data: function() {
return {
baseUrl: 'http://www.CHANGETHISURL.com',
currentPath: '/message/inbox',
menuArray: [ { name: 'Message', url: '/message/inbox' },
{ name: 'Sale', url: '/store/sale' } ]
}
},
methods: {
changeCurrentPath: function() {
this.currentPath = '/store/sale'
}
}
});
a.active{
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.5/vue.js"></script>
<div id="app">
<p>Link is active when it is red.</p>
<list-of-links-with-path :base-url="baseUrl" :menu-array="menuArray" :current-path="currentPath"></list-of-links-with-path>
<br />
<button #click="changeCurrentPath" type="button">Change current path</button>
<br />
currentPath : {{ currentPath }}
</div>
Add a computed property.
computed: {
isActive () {
return ['/message/inbox', '/message/inbox/detail'].indexOf(this.currentPath) > -1
}
}
So you'll be able to use:
<a :href="baseUrl+'/message/inbox'"
:class="{ 'active': isActive }"
>
Message
</a>