Class and Style binding on certain conditions - vue.js

I'm making a navigation component with nuxt.js, vue and storyblok. In this navigation i have a few items. i'm getting my navi items out of the storyblok API. i want to style certain navi items differently when the 'highlighted' attribute in the API equals to true or false.
My problem is that i dont exactlty know how to do that. this is what i have at this point.
div in my navigation component:
<div v-if="items" class="main-nav">
<nav>
<ul>
<li v-bind:class="{ highlighted: item.highlighted === isHighlighted, not_highlighted: item.highlighted === isNotHighlighted}" v-editable="items" :key="index" v-for="(item, index) in items">
<LinkType class="nav-link" :link="item.link" :linkText="item.name">{{ item.name }}</LinkType>
</li>
</ul>
</nav>
</div>
this is how i retrieve my data:
data() {
return {
items: this.$store.state.settings.main_nav ? this.$store.state.settings.main_nav : [],
isHighlighted: true,
isNotHighlighted: false
}
}
whenever i try to console.log item.hightlighted it gives back an undefined error. i would appreciate some help.

Try as below =>
<div v-if="items" class="main-nav">
<nav>
<ul>
<li v-bind:class="item.highlighted === isHighlighted ? 'highlighted' : 'not_highlighted'" v-editable="items" :key="index" v-for="(item, index) in items">
<LinkType class="nav-link" :link="item.link" :linkText="item.name">{{ item.name }}</LinkType>
</li>
</ul>
</nav>
</div>
You can also remove isNotHighlighted: false from data.

Related

List rendering in vue

I'm running through a list of cart items in vue and need to get the index of each item. I thought you'd simply do something like this
<ul class="crt-Push_Items">
<li class="crt-Push_Item" v-for="(lineItem, index) in lineItems" :key="lineItem.key">
<PushCartItem :lineItem="lineItem" />
</li>
</ul>
And
<p class="crt-PushItem_Price"><span class="money" data-line-index="{{ index }}">{{ formatMoney(lineItem.line_price) }} </span></p>
But this isn't displaying and I'm getting the error
[Vue warn]: Property or method "index" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I'm assuming that this line
<p class="crt-PushItem_Price"><span class="money" data-line-index="{{ index }}">{{ formatMoney(lineItem.line_price) }} </span></p>
is the PushCartItem component, so in order to receive the index property, you need to use props, because otherwise you can't have access to it, like this:
<template>
<p class="crt-PushItem_Price">
<span class="money" :data-line-index="index">
{{ formatMoney(lineItem.line_price) }}
</span>
</p>
</template>
<script>
export default {
name: 'PushCartItem',
props: {
index: {
type: Number,
required: true
},
lineItem: {
type: Object,
required: true,
}
},
methods: {
formatMoney(price) {
// do something with the price
}
}
}
</script>
This answer is based in my assumptions, if you still having the error, please give me more information or details about it to help you.
You need to pass the index from the parent component like this.
<ul class="crt-Push_Items">
<li class="crt-Push_Item" v-for="(lineItem, index) in lineItems" :key="lineItem.key">
<PushCartItem :lineItem="lineItem" :index="index" />
</li></ul>
Try this:
<ul class="crt-Push_Items">
<li class="crt-Push_Item" v-for="(lineItem) in lineItems" :key="lineItem.key">
<PushCartItem :lineItem="lineItem" />
</li>
</ul>
Or this:
<ul class="crt-Push_Items">
<li class="crt-Push_Item" v-for="(lineItem, index) in lineItems" :key="index">
<PushCartItem :lineItem="lineItem" />
</li>
</ul>

How to make vuejs navbar link active on click without route

I have a vue bootstrap navbar. All of the nav items have a route except one. The one that doesn't (Contact) launches a modal on click. When an item is selected I want the item to be white.
I do this and it works for items that have a corresponding route:
:active='$route.name ==""'
Navbar
<ul class="navbar-nav">
<li class="nav-item">
<router-link class="nav-link" :to="{
path: '/',
hash: 'home',
}" :active='$route.name =="home"'>Home</router-link>
</li>
<li class="nav-item">
<router-link to="/About" :active='$route.name =="about"' class="nav-link">About</router-link>
</li>
<li class="nav-item">
<a class="nav-link" v-b-modal.modal-contact>Contact</a>
</li>
</ul>
How do I get the Contact item to be active on click?
I had the same problem as you and solved it like this.
First, you define a method that will use the name property of this.$route
export default {
name: 'App',
methods: {
getActiveNavLink(name) {
//This is for the navbar classes, you can modify them as
//as you need. (This will be assigned every-time we call this
//function).
let classString = "nav-item nav-link "
//We compare the given name with the route current name.
if (this.$route.name === name) {
//If it is true, we append to the class string the "active" value
classString += "active"
}
//Return the class string.
return classString;
}
}
}
Then in your navbar you do something like this
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">The navegation bar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<router-link
:to="{ name: 'home'}"
:class="getActiveNavLink('home')" >
Home
</router-link>
<router-link
:to="{ name: 'secondRoute'}"
:class="getActiveNavLink('secondRoute')"><!--class="nav-item nav-link">-->
To the second route!
</router-link>
<router-link :to=...>
...
</router-link>
</div>
</div>
</div>
</nav>
That way, everytime you call the getActiveNavLink you pass the name of the route to compare, and if the name of the route is the same as your $route.name you will get an active navbar element!
Also, dont worry about the router-link classes being overwritten, they are not, the classes that we add, are appended, not overwritten.
its works for me for add class attributte and methods calling
You could use router.push(). When you open the modal, push to that path, when you leave the modal, push to the previous path.
https://router.vuejs.org/guide/essentials/navigation.html

Show Div When Hover Based on ID in Vue JS

I have a div that when i want hover to it, it will show other div. However, my first div is dynamic, it has an ID. So how will i able to hover to ID based on its ID?
It should be #mouseenter="hoverService{{services.id}} = true" but it causes error. So i made the code below to just static.
Here's my code below:
<template>
<div
class="col-md-3"
v-for="(services, index) in servicesFiltered"
:key="index"
#mouseenter="hoverService = true"
#mouseleave="hoverService = false"
>
<div class="service_background" v-if="hoverService">
<div class="mb-1" v-for="(sub_services, index) in services.menu_items" :key="index">
<router-link
:to="{ path: `/${sub_services.data}`}"
>
<a
href="#"
class="btn btn-outline-primary w-100 services_button"
>{{sub_services.text }}</a>
</router-link>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
hoverService: false
};
}
};
</script>
Try this code
https://codesandbox.io/s/y7p9qyyovz
You need to maintain hover for each item you can not manipulate using single variable for multiple items.

Is possible to pass data from slot-component to parent in vueJS?

I'm new in VueJS and I'm try to do something like this
I have 3 components, siderbar ; siderbar-item and sidebar-drop
siderbar.vue is a wrapper when you can put siderbar-item and sidebar-drop, and inside sidebar-drop you can put sidebar-item too
for example:
<sidebar>
<sidebar-item url="myurl" title="Dashboard" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
<sidebar-drop title="News" icon="fas fa-tachometer-alt">
<sidebar-item url="myurl_news" title="News list" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
<sidebar-item url="myurl_news_add" title="Add News" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
</sidebar-drop>
<sidebar-drop title="Categories" icon="fas fa-tachometer-alt">
<sidebar-item url="myurl_categories" title="Categories List" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
</sidebar-drop>
</sidebar>
sidebar.vue is very simple:
<aside>
<slot></slot>
</aside>
sidebar-item.vue
<template>
<ul class="menu-list" >
<li>
<a :href="url" :class="{'is-active': is_active }">
<span class="icon"><i v-if="icon" :class="icon"></i></span>
{{ title }}
<span v-if="badge" class="button badge is-info is-small is-pulled-right "> {{ badge }}</span>
</a>
</li>
</ul>
</template>
<script>
export default {
props: ['title', 'url', 'icon', 'badge'],
data() {
return {
is_active: this.$current_url === this.url
}
}
}
</script>
and sidebar-drop.vue
<template>
<ul class="menu-list" v-if="title">
<li #click="openMenu" :class="{'is-open': is_open}">
<a class="">
<span v-if="icon" class="icon"><i class="fas fa-tachometer-alt"></i></span>
{{ title }}
<span class="badge is-pulled-right" style=""> <i class="fas fa-chevron-left"></i> </span>
</a>
<slot></slot>
</li>
</ul>
</template>
<script>
export default {
props: ['title', 'icon'],
data() {
return {
is_open: false,
}
},
methods: {
openMenu(){
this.is_open = !this.is_open;
},
}
}
</script>
It works great, but I have a little problem. If you notice, sidebar-item has a data called "is_active", it only put a CSS class that this item is active. Great.
Now I'm trying to get into sidebar-drop if a least one of his children has data "is-active" in true, change the is_open sidebar-drop parent data to true.
Example: I click "News" item.
Ul li News is active... works!
Ul li parent drop is open... don't work
Is possible make that?
Thanks everyone!
Update:
I put it in Fiddler to more clear:
https://jsfiddle.net/h35qy2zs/
I do in sidebar-item a random is_active.
If any child of sidebar-drop (sidebar-item's) data is_active:true, sidebar-drop data is_open is true too.
So, it show in color yellow and open
Result:
But now, both are closed at refresh page.

Skip object items if the value is null

I have a nested for ... in loop in vue js. What I'm trying to to is to skip elements if the value of the element is null. Here is the html code:
<ul>
<li v-for="item in items" track-by="id">
<ol>
<li v-for="child in item.children" track-by="id"></li>
</ol>
</li>
</ul>
null elements may be present in both item and item.children objects.
For example:
var data = {
1: {
id: 1,
title: "This should be rendered",
children: {
100: {
id: 100,
subtitle: "I am a child"
},
101: null
}
},
2: null,
3: {
id: 3,
title: "Should should be rendered as well",
children: {}
}
};
With this data data[1].children[101] should not be rendered and if data[1].children[100] becomes null later it should be omitted from the list.
P.S. I know this is probably not the best way to represent data but I'm not responsible for that :)
Edit: Actually, a simple v-if might work:
<li v-for="item in items" v-if="item !== null" track-by="id">
Give it a try. If not, do this:
You can add a filter for that (in main.js before App instance):
Vue.filter('removeNullProps',function(object) {
// sorry for using lodash and ES2015 arrow functions :-P
return _.reject(object, (value) => value === null)
})
then in the template:
<li v-for="item in items | removeNullProps" track-by="id">
<ol>
<li v-for="child in item.children | removeNullProps" track-by="id"></li>
</ol>
</li>
In Vue 2, filters have been deprecated in v-fors.
Now you should use computed properties. Demo below.
new Vue({
el: '#app',
data: {
items: [
'item 1',
'item 2',
null,
'item 4',
null,
'item 6'
]
},
computed: {
nonNullItems: function() {
return this.items.filter(function(item) {
return item !== null;
});
}
}
})
<script src="https://unpkg.com/vue#2"></script>
<div id="app">
Using a computed property:
<ul>
<li v-for="item in nonNullItems">
{{ item }}
</li>
</ul>
<hr>
Using v-if:
<ul>
<li v-for="item in items" v-if="item !== null">
{{ item }}
</li>
</ul>
</div>
I would advise you against using v-if and v-for in the same element. What I found worked and didn't affect performance is this :
<li v-for="(value, key) in row.item.filter(x => x !== null)" :key="key"{{value}}</li>
You just need to run the filter function on the array you are going through. This is a common use in c# and found it was no different in JavaScript. This will basically skip the nulls when iterating.
Hope this helps (3 years later).
Just use v-if to do with it. But the first, do not use track-by="id" because of the null item and null child. You can check the demo here https://jsfiddle.net/13mtm5zo/1/.
Maybe the better way is to deal with the data first before the render.
VueJs style guide tell us to :
"Never use v-if on the same element as v-for."
How to handle v-if with v-for properly according to the vue style guide :
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
See more on how to handle v-if with v-for here : https://v2.vuejs.org/v2/style-guide/#Avoid-v-if-with-v-for-essential