Dropdown menu in VueJS (toggling of active menu) - javascript

This is the menu and below my navigation menu and method that on click should open the subitems inside nav item on click but I cant seem to be find where I'm lacking
[![dropdown_menu][1]][1]
new Vue({
el: '#app',
data: {
//menu
"menu_title": "a",
"child_routes": [{
"path": "/a1",
"menu_title": "a1"
},
{
"path": "/a2",
"menu_title": "a2"
}
]
},
{
"menu_title": "b",
"child_routes": [{
"path": "/b1",
"menu_title": "b1"
},
{
"path": "/b2",
"menu_title": "b2"
},
{
"path": "/b1",
"menu_title": "b3"
}
]
},
{
"menu_title": "c",
"child_routes": [{
"path": "/c1",
"menu_title": "c1"
},
{
"path": "/c2",
"menu_title": "c2"
},
{
"path": "/c3",
"menu_title": "c3"
}
]
}
},
methods: {
navlinks() {
var navItemParent = document.querySelector("nav-item");
var navLink = document.querySelector(".idb-nav .nav-item .nav-link");
var navItem = document.querySelector(".idb-nav .nav-item");
navItem.classList.toggle("active");
if (navItem.contains("active")) {
navItem.classList.remove("active");
navLink.classList.toggle("active");
}
}
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<nav class="idb-sidebar-nav">
<ul class="idb-nav list-unstyled m-15 p-0">
<template v-for="(menu, index) in sideBarLinks.routes">
<li class="nav-item" v-if="menu.child_routes!=null" :key="index">
<a href="javascript:void(0)" class="nav-link" #click="navlinks" >
<i :class="menu.menu_icon" class="menu-icon"></i>
<span class="menu-title">{{menu.menu_title}}</span>
</a>
<ul class="list-unstyled sub-menu">
<router-link
:to="subMenu.path"
tag="li"
v-for="(subMenu, key) in menu.child_routes"
:key="key"
>
<a href="javascript:void(0)" class="sub-menu-nav-link">
<span>{{subMenu.menu_title}}</span>
</a>
</router-link>
</ul>
</li>
<router-link
:key="index"
:to="menu.path"
tag="li"
class="nav-item"
v-else
>
<a class="nav-link">
<i :class="menu.menu_icon" class="mr-15"></i>
<span class="menu-title">{{menu.menu_title }}</span>
</a>
</router-link>
</template>
</ul>
</nav>
</div>

The classes toggling should be managed as part of the data. Try this and see if it works for you:
HTML
<div id="app">
<nav class="idb-sidebar-nav">
<ul class="idb-nav list-unstyled m-15 p-0">
<template v-for="(menu, index) in sideBarLinks.routes" :key="index">
<li class="nav-item"
v-if="menu.child_routes && menu.child_routes.length">
<a :class="[ 'nav-link', { 'active': menu.active } ]"
href="javascript:void(0);"
#click="toggleMenu(index)">
<i :class="menu.menu_icon" class="menu-icon"></i>
<span class="menu-title">{{menu.menu_title}}</span>
</a>
<ul class="list-unstyled sub-menu">
<router-link tag="li"
v-for="(subMenu, key) in menu.child_routes"
:key="key"
:to="subMenu.path">
<a href="javascript:void(0);" class="sub-menu-nav-link">
<span>{{subMenu.menu_title}}</span>
</a>
</router-link>
</ul>
</li>
<router-link v-else
:key="index"
:to="menu.path"
tag="li"
class="nav-item">
<a :class="[ 'nav-link', { 'active': menu.active } ]"
href="javascript:void(0);"
#click="toggleMenu(index)">
<i :class="menu.menu_icon" class="mr-15"></i>
<span class="menu-title">{{menu.menu_title }}</span>
</a>
</router-link>
</template>
</ul>
</nav>
</div>
routes.json
For improved readability and maintainability, you'd best be moving the list to separate file.
[
{
"menu_title": "a",
"child_routes": [
{
"path": "/a1",
"menu_title": "a1"
},
{
"path": "/a2",
"menu_title": "a2"
}
],
active: true // First item opened by default (if you like)
},
{
"menu_title": "b",
"child_routes": [
{
"path": "/b1",
"menu_title": "b1"
},
{
"path": "/b2",
"menu_title": "b2"
},
{
"path": "/b1",
"menu_title": "b3"
}
],
active: false
},
{
// ...
}
]
app.js
import * as routes from './routes.json';
const vm = new Vue({
el: '#app',
data() {
return {
sideBarLinks: {
routes
}
}
},
methods: {
toggleSidebar(index) {
this.sideBarLinks
.routes
.forEach((menu, i) => menu.active = (i === index));
}
}
});

Related

Multilevel menu using children condition in Vue js

This navbar has a 1 level dropdown menu. I want to create up to 3 levels but when adding a grandchildren condition to the children it won't work. With this setting, how would I be able to create a 3 level dropdown menu?
Component Header.vue
<template>
<!-- Top menu -->
<nav
id="mainNav"
:class="$route.path==='/home' ? '' : 'bg-primary'"
class="navbar navbar-expand-md navbar-dark fixed-top"
>
<!-- <div class="rtl-layout" #click="addToggleClass()">RTL</div>-->
<div class="container ">
<router-link class="navbar-brand" routerLinkActive="active-link" to="/home">
<!-- <img src="/static/img/zemle-logo.png" class="img-fluid" width="110" height="37">-->
<h3 class="text-white">Logo</h3>
</router-link>
<button
aria-controls="navbarCollapse" aria-expanded="true" aria-label="Toggle navigation" class="navbar-toggler"
data-target="#navbarCollapse" data-toggle="collapse" type="button">
<span class="navbar-toggler-icon"></span>
</button>
<div id="navbarCollapse" class="collapse navbar-collapse">
<ul id="main" class="navbar-nav ml-auto main-menu list-unstyled">
<li
v-for="(menuItem,i) in menus"
:id="'menuNo'+i"
:key="i"
class="parent nav-item"
#click="menuToggleLink('menuNo'+i);"
>
<router-link
v-if="menuItem.type == 'link'"
:to="menuItem.state"
class="nav-link"
#click.native="removeCollapseInClass();"
>
{{ menuItem.name }}
</router-link>
<a
v-if="menuItem.type == 'sub'"
class="nav-link"
href="javascript:void(0)"
>
{{ menuItem.name }}
<i :class="menuItem.icon" #click.stop="menuToggle('menuNo'+i)"></i>
</a>
<ul
v-if="menuItem.type == 'sub' && menuItem.children"
class="sub-menu arrow-up list-unstyled">
<li
v-for="(grandchildItem,i) of menuItem.children"
:key="i"
class="nav-item"
>
<router-link
v-if="grandchildItem.type == 'link'"
:to="grandchildItem.state"
class="nav-link"
#click.native="removeCollapseInClass()"
>
{{ grandchildItem.name }}
</router-link>
</li>
</ul>
</li>
<li>
<div
class="search-form"
click-outside
>
<span
data-target="#search-style-simple"
#click="showSearch()"
>
<i class="fa fa-search"></i>
</span>
<div
id="search-style-simple"
:class="{'search-active': searchactive}"
class="module-container"
>
<form action="javascript:void(0);" class="search-box" method="get" role="search">
<input class="form-control" name="" placeholder="Search" type="search" value=""/>
<button type="submit"><i class="fa fa-search"></i></button>
</form>
</div><!-- /module-container -->
</div><!-- /module -->
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
export default {
data() {
return {
searchactive: false,
menus: [
{
state: "/home",
name: "Home",
type: "link"
},
{
state: "",
name: "Products",
type: "sub",
icon: 'fa fa-caret-down',
children: [
{state: 'apparel', name: 'Apparel', type: "link"},
{state: 'bathroom', name: 'Bathroom', type: "link"},
{state: 'bedding', name: 'Bedding', type: "link"},
{state: 'decorative-details', name: 'Decorative Details', type: "link"},
]
},
{
state: "",
name: "Shop",
type: "sub",
icon: 'fa fa-caret-down',
children: [
{state: 'product-grid', name: 'Product Grid', type: "link"},
{ state: 'product-cart', name: 'Product Cart', type:"link"},
{ state: 'product-checkout', name: 'Product Checkout', type:"link"},
{state: 'product-detail', name: 'Product Detail', type: "link"}
]
},
{
state: "",
name: "Blog",
type: "sub",
icon: 'fa fa-caret-down',
children: [
{state: 'blog-column3', name: 'Blog Column1', type: "link"},
{state: 'blog-masonry3', name: 'Blog Masonry', type: "link"},
{state: 'blog-sidebar', name: 'Blog Sidebar', type: "link"},
{state: 'blog-detail', name: 'Blog Detail', type: "link"}
]
}
]
}
},
mounted() {
this.onScrollEvent();
},
methods: {
showSearch() {
this.searchactive = !this.searchactive;
},
menuToggleLink(id) {
if (document.getElementById(id).classList.contains("open")) {
document.getElementById(id).classList.remove("open");
} else if (!document.getElementById(id).classList.contains("open")) {
let elements = document.querySelectorAll(".parent");
for (var i = 0; i < elements.length; i++) {
elements[i].classList.remove('open');
}
document.getElementById(id).classList.add("open");
}
},
addToggleClass() {
document.querySelector("body").classList.toggle("rtl-enable");
},
removeCollapseInClass() {
document.getElementById("navbarCollapse").classList.remove('show');
},
onScrollEvent() {
var headerSticky = document.getElementById('mainNav');
window.onscroll = function () {
if (window.pageYOffset >= 100) {
headerSticky.classList.add("scrollHeader");
} else {
headerSticky.classList.remove("scrollHeader");
}
}
}
}
}
</script>
I have tried modifying the children and adding a grandchildren to it but it won't display the 2nd or 3rd level menu.

Creating a 2 level navbar using Vue js

I am trying to create a 2 level navigation bar with this code but when I try to add another children to one of the children in the scrip it won't work. The idea is for the user to go to a second level where he can select other links. It should hover on link in navbar, then go to the 1st level menu and then hover on one of the option to get the 2nd level menu.
<template>
<!-- Top menu -->
<nav
id="mainNav"
class="navbar navbar-expand-md navbar-dark fixed-top"
:class="$route.path==='/home' ? '' : 'bg-primary'"
>
<!-- <div class="rtl-layout" #click="addToggleClass()">RTL</div>-->
<div class="container ">
<router-link class="navbar-brand" to="/home" routerLinkActive="active-link">
<!-- <img src="/static/img/zemle-logo.png" class="img-fluid" width="110" height="37">-->
<h3 class="text-white" >Sospes Logo</h3>
</router-link>
<button
class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="true" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul id="main" class="navbar-nav ml-auto main-menu list-unstyled">
<li
#click="menuToggleLink('menuNo'+i);"
class="parent nav-item"
v-for="(menuItem,i) in menus"
:id="'menuNo'+i"
:key="i"
>
<router-link
class="nav-link"
:to="menuItem.state"
v-if="menuItem.type == 'link'"
#click.native="removeCollapseInClass();"
>
{{ menuItem.name }}
</router-link>
<a
v-if="menuItem.type == 'sub'"
class="nav-link"
href="javascript:void(0)"
>
{{ menuItem.name }}
<i :class="menuItem.icon" #click.stop="menuToggle('menuNo'+i)"></i>
</a>
<ul
v-if="menuItem.type == 'sub' && menuItem.children"
class="sub-menu arrow-up list-unstyled" >
<li
class="nav-item"
v-for="(grandchildItem,i) of menuItem.children"
:key="i"
>
<router-link
class="nav-link"
:to="grandchildItem.state"
v-if="grandchildItem.type == 'link'"
#click.native="removeCollapseInClass()"
>
{{ grandchildItem.name }}
</router-link>
</li>
</ul>
</li>
<li>
<div
class="search-form"
click-outside
>
<span
data-target="#search-style-simple"
#click="showSearch()"
>
<i class="fa fa-search"></i>
</span>
<div
class="module-container"
:class="{'search-active': searchactive}"
id="search-style-simple"
>
<form role="search" method="get" class="search-box" action="javascript:void(0);">
<input type="search" class="form-control" placeholder="Search" value="" name="" />
<button type="submit"><i class="fa fa-search"></i></button>
</form>
</div><!-- /module-container -->
</div><!-- /module -->
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
export default{
data(){
return{
searchactive:false,
menus: [
{
state: "/home",
name: "Home",
type:"link"
},
{
state:"",
name:"Pages",
type:"sub",
icon: 'fa fa-caret-down',
children: [
{ state: 'about', name: 'About', type:"link"},
{ state: 'features', name: 'Features', type:"link"},
{ state: 'contact', name: 'Contact', type:"link"},
{ state: 'pricing', name: 'Pricing', type:"link"},
{ state: 'search', name: 'Search', type:"link"},
{ state: 'portfolio-v1', name: 'Products V1', type:"link"},
{ state: 'portfolio-v2', name: 'Products V2', type:"link"},
{ state: 'portfolio-v3', name: 'Products V3', type:"link"}
]
},
{
state:"",
name:"Features",
type:"sub",
icon: 'fa fa-caret-down',
children: [
{ state: 'login', name:'Login', type:"link"},
{ state: 'sign-up', name: 'Sign Up', type:"link"},
{ state: 'thank-you', name: 'Thank You', type:"link"},
{ state: 'maintenance', name: 'Maintenance', type:"link"},
{ state: 'not-found', name: '404', type:"link"}
]
},
{
state:"",
name:"Shop",
type:"sub",
icon: 'fa fa-caret-down',
children: [
{ state: 'product-grid', name:'Product Grid', type:"link"},
{ state: 'product-cart', name: 'Product Cart', type:"link"},
{ state: 'product-checkout', name: 'Product Checkout', type:"link"},
{ state: 'product-detail', name: 'Product Detail', type:"link"}
]
},
{
state:"",
name:"Blog",
type:"sub",
icon: 'fa fa-caret-down',
children: [
{ state: 'blog-listing-sidebar', name:'blog column ', type:"link"},
{ state: 'blog-column3', name: 'Blog Column1', type:"link"},
{ state: 'blog-masonry3', name: 'Blog Masonry', type:"link"},
{ state: 'blog-sidebar', name: 'Blog Sidebar', type:"link"},
{ state: 'blog-detail', name: 'Blog Detail', type:"link"}
]
}
]
}
},
mounted(){
this.onScrollEvent();
},
methods:{
showSearch(){
this.searchactive = !this.searchactive;
},
menuToggleLink(id){
if (document.getElementById(id).classList.contains("open")){
document.getElementById(id).classList.remove("open");
} else if(!document.getElementById(id).classList.contains("open")) {
let elements = document.querySelectorAll(".parent");
for (var i = 0; i < elements.length; i++) {
elements[i].classList.remove('open');
}
document.getElementById(id).classList.add("open");
}
},
addToggleClass(){
document.querySelector("body").classList.toggle("rtl-enable");
},
removeCollapseInClass(){
document.getElementById("navbarCollapse").classList.remove('show');
},
onScrollEvent(){
var headerSticky = document.getElementById('mainNav');
window.onscroll = function() {
if (window.pageYOffset >= 100) {
headerSticky.classList.add("scrollHeader");
} else {
headerSticky.classList.remove("scrollHeader");
}
}
}
}
}
</script>
<style>
.scrollHeader{
background-color: #0B3242;
}
</style>

filter JSON result by attribute if the obj. attribute is true with React.js

Im trying to retrieve and filter JSON data with React.js and maybe hooks but I cant come with any good way to do it. Just now I have my JSON data local, so it does not come from restAPI or similar (for now)... and what Im trying is to create a menu with buttons that when I click on them I get filtered data. I know... it should not be very difficut to do it (cause Im doing it already in a veeeery basic way for a conditional that show different icones depending on if certain attribute is true.) but anyway im struggling with it.
Here is my code:
import React, {setState, useState} from 'react';
export const ByCourse = () => {
const projects = window.projects.aaData;
console.log('window.projects = ', projects);
return (
<div className="section-component">
<h2 className="mt-2 mb-4">By Course</h2>
<ul className="nav nav-tabs">
<li className="nav-item">
<a className="nav-link active" href="#">
All
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">
Favorite
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">
Recent
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">
Assigned
</a>
</li>
</ul>
<div className="container">
{projects.map(project => {
return(
<div className="row">
<div className="w-100">
<div className="d-flex border mt-1 mb-1 p-3">
<div className="col-1">
{project.favorite ? <i className="fa fa-star"></i> : <i className="fa fa-star-o"></i>}
</div>
<div className="col-11">
<div className="font-weight-bold"> {project.projectTitle}</div>
<div className="font-weight-normal"> {project.ouName}</div>
</div>
</div>
</div>
</div>
)})}
</div>
</div>
);};
and my data looks like this:
projects = {"aaData":
[
{
"index":0,
"projectTitle":"123",
"userTitle":"VVS-teknik grund",
"ouName":"fertest1",
"orgUnitId":1022,
"projectId":2014,
"favorite":false,
"recent":false,
"userAssigned":false,
"projectStatus":"ACTIVE",
"viewLink":"http://xxxxxx
},
{
"index":1,
"projectTitle":"AAA",
"userTitle":"AAA",
"ouName":"fertest1",
"orgUnitId":1022,
"projectId":2002,
"favorite":false,
"recent":true,
"userAssigned":false,
"projectStatus":"ACTIVE",
"viewLink":"http://xxxxxx
},
{
"index":2,
"projectTitle":"Activity with image and text",
"userTitle":"asdas",
"ouName":"ferfer AB",
"orgUnitId":1004,
"projectId":1892,
"favorite":false,
"recent":false,
"userAssigned":true,
"projectStatus":"NEW",
"viewLink":"http://xxxxxx"
}]
I tried using a solution from here: Filtering JSON results in categories - React Hooks
but I get a WEBPACK_1 ERROR and cant fix it...
Im also doing a very basic filtering here:
{project.favorite ? <i className="fa fa-star"></i> : <i className="fa fa-star-o"></i>}
and basically what Im looking for is:
if attribute "favorite" is true -> show results with attr favorite true
else
if attribute "recent" is true -> just show results with attribute recent == true
Thank you very much in advance, any help will be very very welcome.
Here's a working example using useState to set a filter:
https://codesandbox.io/s/crazy-turing-jd3ym?fontsize=14&hidenavigation=1&theme=dark
First of all, the filtering or those functions is not about React.js. It's about JavaScript.
I will try go to be honest, i don't undestand anything about your problem. Could you please write only your problem?
Note: Don't mix the conditional keyword or something like that.
If you write the problem clearly we will (i) will help you.
Bonus: I refactor your code.
Here changes list:
1- I removed the unnecessary import
2- Seperate your code. Make component.
3- Indentation
4- Short condition (Fix for duplicate code)
import React from 'react';
const Project = data => (
<div className="row">
<div className="w-100">
<div className="d-flex border mt-1 mb-1 p-3">
<div className="col-1">
<i className={data.favorite ? "fa fa-star" : "fa fa-star-o"} />
</div>
<div className="col-11">
<div className="font-weight-bold"> {data.projectTitle}</div>
<div className="font-weight-normal"> {data.ouName}</div>
</div>
</div>
</div>
</div>
);
export const ByCourse = () => {
const projects = window.projects.aaData;
console.log('window.projects = ', projects);
return (
<div className="section-component">
<h2 className="mt-2 mb-4">By Course</h2>
<ul className="nav nav-tabs">
<li className="nav-item">
<a className="nav-link active" href="#">
All
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">
Favorite
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">
Recent
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">
Assigned
</a>
</li>
</ul>
<div className="container">
{projects.map(project => <Project data={project} />)}
</div>
</div>
);
};
I am assuming you want to show the objects where favorite OR recent: true,
1) A simple way to do this is:
let projects = {
"aaData": [{
"index": 0,
"projectTitle": "123",
"userTitle": "VVS-teknik grund",
"ouName": "fertest1",
"orgUnitId": 1022,
"projectId": 2014,
"favorite": true,
"recent": false,
"userAssigned": false,
"projectStatus": "ACTIVE",
"viewLink": "http://xxxxxx"
},
{
"index": 1,
"projectTitle": "AAA",
"userTitle": "AAA",
"ouName": "fertest1",
"orgUnitId": 1022,
"projectId": 2002,
"favorite": false,
"recent": true,
"userAssigned": false,
"projectStatus": "ACTIVE",
"viewLink": "http://xxxxxx"
},
{
"index": 2,
"projectTitle": "Activity with image and text",
"userTitle": "asdas",
"ouName": "ferfer AB",
"orgUnitId": 1004,
"projectId": 1892,
"favorite": false,
"recent": false,
"userAssigned": true,
"projectStatus": "NEW",
"viewLink": "http://xxxxxx"
}
]
}
for (let iterator = 0; iterator < projects.aaData.length; iterator++) {
if (projects.aaData[iterator].favorite || projects.aaData[iterator].recent) {
console.log(projects.aaData[iterator])
} else {
continue;
}
}
2) Other way to do this is:
let projects = {
"aaData": [{
"index": 0,
"projectTitle": "123",
"userTitle": "VVS-teknik grund",
"ouName": "fertest1",
"orgUnitId": 1022,
"projectId": 2014,
"favorite": true,
"recent": false,
"userAssigned": false,
"projectStatus": "ACTIVE",
"viewLink": "http://xxxxxx"
},
{
"index": 1,
"projectTitle": "AAA",
"userTitle": "AAA",
"ouName": "fertest1",
"orgUnitId": 1022,
"projectId": 2002,
"favorite": false,
"recent": true,
"userAssigned": false,
"projectStatus": "ACTIVE",
"viewLink": "http://xxxxxx"
},
{
"index": 2,
"projectTitle": "Activity with image and text",
"userTitle": "asdas",
"ouName": "ferfer AB",
"orgUnitId": 1004,
"projectId": 1892,
"favorite": false,
"recent": false,
"userAssigned": true,
"projectStatus": "NEW",
"viewLink": "http://xxxxxx"
}
]
}
let filteredArrayWithNull = projects.aaData.filter((object) => {
return object.favorite || object.recent ?
object : null
})
let finalFilteredArray = filteredArrayWithNull.filter((object) => object!=null)
console.log(finalFilteredArray);
To read more about filter(); here is a comprehensive guide

Vue.js dynamic classes

I have some html using Vue.js here:
<div id="app">
<ul class="navbar-nav">
<li class="nav-item" v-for="tab in tabs">
{{ tab.name }}
</li>
</ul>
</div>
I have the javascript here:
var app = new Vue({
el: '#app',
data: {
tabs: [
{ name: "Home", active: "" },
{ name: "Challenges", active: "active" },
{ name: "Scoreboard", active: "" },
{ name: "About", active: "" }
]
}
});
I want to set class to tabs.active since I'm using bootstrap. How can I do that?
Add
v-bind:class="tab.active"
So your code should look like this:
var app = new Vue({
el: '#app',
data: {
tabs: [{
name: "Home",
active: ""
},
{
name: "Challenges",
active: "active"
},
{
name: "Scoreboard",
active: ""
},
{
name: "About",
active: ""
}
]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul class="navbar-nav">
<li class="nav-item" v-for="tab in tabs" v-bind:class="tab.active">
{{ tab.name }}
</li>
</ul>
</div>
Use a class binding like this:
<li class="nav-item" :class="{active: tab.active === 'active'}" v-for="tab in tabs">
new Vue({
el: '#app',
data: {
tabs: [
{ name: "Home", active: "" },
{ name: "Challenges", active: "active" },
{ name: "Scoreboard", active: "" },
{ name: "About", active: "" }
]
}
});
.active .nav-link {
color: white;
background: blue;
}
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<ul class="navbar-nav">
<li class="nav-item" :class="{active: tab.active === 'active'}" v-for="tab in tabs">
{{ tab.name }}
</li>
</ul>
</div>

Filter ul list by input text Vue.js 2

I want to filter one ul list when a user types rather the name or the initials on an input filed. I am working with vue.js 2. The list is being rendered calling a store variable, a JSON file.
STORE VARIABLE
$store.coin.coin
JSON example
{
"coin": [
{
"initials": "DLR",
"price": "0.727282",
"volume": "1212",
"change": "+1.22",
"name": "dollar"
}, ...
]
}
HTML
<li>
<div class="input-group search-box">
<input id="filter-coins" v-model="search" placeholder="Filter" class="input-group-field" type="text">
</div>
</li>
<li class="tabs-title" v-for="item in filteredItems" :key="item.initials" >
<a href="#coinList" aria-selected="true">
<div class="grid-x">
<div class="auto cell">{{ item.initials }}</div>
<div class="auto cell">{{ item.price }}</div>
<div class="auto cell">{{ item.volume }}</div>
<div class="auto cell">{{ item.change }}</div>
<div class="auto cell">{{ item.name }}</div>
</div>
</a>
</li>
METHOD
export default {
name: 'coins',
search: '',
computed: {
filteredItems() {
return this.$store.coin.coin.filter(item =>
(item.name.toLowerCase().indexOf(this.search.toLowerCase()) > -1));
},
},
};
store.js
import Vue from 'vue';
import Vuex from 'vuex';
import coin from '../data/coins-btc.json';
Vue.use(Vuex);
export default {
state: {
coin,
},
getters: {
coin: state => state.coin,
},
};
Here is an example filter that works with either the initials or the name.
computed:{
filteredItems(){
let coins = this.$store.getters.coin.coin
// if there is no filter, return everything
if (!this.search) return coins
// setup the filter function
let searchValue = this.search.toLowerCase()
let filter = coin => coin.initials.toLowerCase().includes(searchValue) ||
coin.name.toLowerCase().includes(searchValue)
return coins.filter(filter)
}
}
And here is an example of it in action.
console.clear()
const store = new Vuex.Store({
state: {
coin: {
"coin": [
{
"initials": "DLR",
"price": "0.727282",
"volume": "1212",
"change": "+1.22",
"name": "dollar"
},
{
"initials": "EUR",
"price": "0.727282",
"volume": "1212",
"change": "+1.22",
"name": "euro"
},
{
"initials": "AUS",
"price": "0.727282",
"volume": "1212",
"change": "+1.22",
"name": "australian"
}
]
}
},
getters:{
coin: state => state.coin
}
})
new Vue({
el: "#app",
store,
data:{
search: null
},
computed:{
filteredItems(){
let coins = this.$store.getters.coin.coin
// if there is no filter, return everything
if (!this.search) return coins
// setup the filter function
let searchValue = this.search.toLowerCase()
let filter = coin => coin.initials.toLowerCase().includes(searchValue) ||
coin.name.toLowerCase().includes(searchValue)
return coins.filter(filter)
}
}
})
<script src="https://unpkg.com/vue#2.4.2"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.4.0/vuex.js"></script>
<div id="app">
<ul>
<li>
<div class="input-group search-box">
<input id="filter-coins" v-model="search" placeholder="Filter" class="input-group-field" type="text">
</div>
</li>
<li class="tabs-title" v-for="item in filteredItems" :key="item.initials" >
<a href="#coinList" aria-selected="true">
<div class="grid-x">
<div class="auto cell">{{ item.initials }}</div>
<div class="auto cell">{{ item.price }}</div>
<div class="auto cell">{{ item.volume }}</div>
<div class="auto cell">{{ item.change }}</div>
<div class="auto cell">{{ item.name }}</div>
</div>
</a>
</li>
</ul>
</div>

Categories

Resources