Creating an accordion menu: The submenus are not displayed - javascript

I am creating an accordion menu. The headings are displayed but I don't know how to display the submenus?
Something missing on this part? Because, I don't see where is the problem? If you have a solution, I'm really interested.
<ul class="submenu" #submenu>
<li *ngFor="let submenu of menu.submenu">
<a routerLink="{{ submenu.url }}"> {{ submenu.name }} </a>
</li>
</ul>
HTML file
<div class="sidebar">
<ul id="accordion" class="accordion">
<li *ngFor="let menu of menus; let i = index" [class.active]="menu.active">
<ng-container>
<div class="menu" (click)="selectMenu(menu)">
<i [class]="menu.iconClass"></i> {{ menu.name }}
<i class="fa fa-chevron-down"></i>
</div>
<ul class="submenu" #submenu>
<li *ngFor="let submenu of menu.submenu">
<a routerLink="{{ submenu.url }}"> {{ submenu.name }} </a>
</li>
</ul>
</ng-container>
</li>
</ul>
</div>
Then
export class AppComponent {
menus: Menu[] = [
{
name: 'Markets',
iconClass: 'bx bx-lock-alt',
active: false,
submenu: [
{ name: 'Indice', url: 'https://www.google.be'},
{ name: 'Value', url: '#'}
]
},
{
name: 'Stock',
iconClass: 'bx bx-lock-alt',
active: false,
submenu: [
{ name: 'Currency', url: 'https://www.google.be'},
{ name: 'Devise', url: '#'}
]
},
{
name: 'Traiding',
iconClass: 'bx bx-lock-alt',
active: false,
submenu: [
{ name: 'Day traiding', url: 'https://www.google.be'},
{ name: 'Devise', url: '#'}
]
}
]
selectMenu(parentMenu: { name: string }) : void {
this.menus.forEach(menu => {
if(menu.name !== parentMenu.name){
menu.active = false;
} else {
menu.active = true;
}
})
}
}
There is a reproduction here.
Thank you in advance for your help.

Related

How to pass array length to another component (from child to parent component) in vue.js?

I have two components Dashboard.vue(parent) and DisplayBooks.vue(child). inside child component i have one array addedBooks:[] it should be incremented when user clicks on ADD TO BAG button and decremented when user clicks on ADDED TO BAG button upto this it's working fine ,Now hat my requirement is addedBooks.length (array length)should be transferred from DisplayBooks.vue to the Dashboard.vue component.how to acheive this thing please help me
Dashboard.vue
<template>
<div class="main">
<div class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<img src="../assets/education.png" alt="notFound" class="education-image" />
</div>
<ul class="nav navbar-nav">
<li>
<p class="brand">Bookstore</p>
</li>
</ul>
<div class="input-group">
<i #click="handlesubmit();" class="fas fa-search"></i>
<div class="form-outline">
<input type="search" v-model="name" class="form-control" placeholder='search...' />
</div>
</div>
<ul class="nav navbar-nav navbar-right" id="right-bar">
<li><a> <i class="far fa-user"></i></a></li>
<p class="profile-content">profile</p>
<!-- here i want to display the array length -->
<li><a><i class="fas fa-cart-plus"></i></a></li>
<p class="cart-content">cart</p>
</ul>
</div>
<div class="mid-body">
<h6>Books<span class="items">(128items)</span></h6>
<select class="options" #change="applyOption">
<option disabled value="">Sort by relevance</option>
<option value="HighToLow">price:High to Low</option>
<option value="LowToHigh">price:Low to High</option>
</select>
</div>
<div v-if="flam==false">
<h2>Hello</h2>
</div>
<DisplayBooks v-show="flag==='noOrder'" />
<sortBooksLowtoHigh v-show="flag==='lowToHigh'" />
<sortBooksHightoLow v-show="flag==='highToLow'" />
</div>
</template>
<script>
import sortBooksLowtoHigh from './sortBooksLowtoHigh.vue'
import sortBooksHightoLow from './sortBooksHightoLow.vue'
import DisplayBooks from './DisplayBooks.vue'
export default {
components: {
DisplayBooks,
sortBooksLowtoHigh,
sortBooksHightoLow
},
data() {
return {
flag: 'noOrder',
brand: 'Bookstore',
name: '',
flam: true,
visible: true,
books: [{
}]
}
},
methods: {
flip() {
this.flam = !this.flam;
},
applyOption(evt) {
if (evt.target.value === "HighToLow") {
this.flag = 'highToLow';
} else this.flag = 'lowToHigh';
},
}
}
</script>
DisplayBooks.vue
<template>
<div class="carddisplay-section">
<div v-for="book in books" :key="book.id" class="card book">
<div class="image-section">
<div class="image-container">
<img v-bind:src="book.file" />
</div>
</div>
<div class="title-section">
{{book.name}}
</div>
<div class="author-section">
by {{book.author}}
</div>
<div class="price-section">
Rs. {{book.price}}<label class="default">(2000)</label>
<button v-if="flag" class="btn-grp" type="submit" #click="handlesubmit();Togglebtn();">close</button>
</div>
<div class="buttons">
<div class="button-groups" v-if="!addedBooks.includes(book.id)">
<button type="submit" #click="handleCart(book.id);toggle(book.id);addedBooks.push(book.id)" class="AddBag">Add to Bag</button>
<button class="wishlist">wishlist</button>
</div>
<div class="AddedBag" v-else>
<button class="big-btn" #click="removeFromCart(book.id);addedBooks=addedBooks.filter(id=>id!==book.id)">Added to Bag</button>
</div>
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default {
data() {
return {
flag: true,
state: true,
addedBooks:[], // this array length should be passed to dashboard component
clickedCard: '',
books: [{
id: 0,
file: 'https://images-na.ssl-images-amazon.com/images/I/41MdP5Tn0wL._SX258_BO1,204,203,200_.jpg',
name: 'Dont Make me think',
author: 'Sai',
price: '1500'
}, ]
}
},
methods: {
flip() {
this.state = !this.state;
},
Togglebtn() {
this.flag = !this.flag;
},
handlesubmit() {
service.userDisplayBooks().then(response => {
this.books.push(...response.data);
})
},
handleCart(bookId){
let userData={
id: bookId,
}
service.userUpdateCart(userData).then(response=>{
return response;
})
},
removeFromCart(bookId){
let userData={
id:bookId,
}
service.userRemoveFromCart(userData).then(response=>{
return response;
})
}
}
}
</script>
Watch the array change and emit its length to the parent :
DisplayBooks.vue
data() {
return {
flag: true,
state: true,
addedBooks:[], // this array length should be passed to dashboard component
clickedCard: '',
books: [{
id: 0,
file: 'https://images-na.ssl-images-amazon.com/images/I/41MdP5Tn0wL._SX258_BO1,204,203,200_.jpg',
name: 'Dont Make me think',
author: 'Sai',
price: '1500'
}, ]
}
},
watch:{
addedBooks:{
handler(val){
this.$emit('update-books-count',val.length)
},
deep:true
}
}
Dashboard
<ul class="nav navbar-nav navbar-right" id="right-bar">
<li><a> <i class="far fa-user"></i></a></li>
<p class="profile-content">profile</p>
<p>{{booksCount}}</p>
<li><a><i class="fas fa-cart-plus"></i></a></li>
<p class="cart-content">cart</p>
</ul>
...
<DisplayBooks v-show="flag==='noOrder'" #update-books-count="(n)=>booksCount=n"/>
...
data() {
return {
booksCount:0,
flag: 'noOrder',
brand: 'Bookstore',
name: '',
flam: true,
visible: true,
books: [{
}]
}
},

Vue.js use component in another

I have a component that I would like to use in another in order to make a navigation menu. I want separate the menu from links. How can I display data in a child component in another parent component? I use Vue.js CDN.
I am a newbie in using vue.js. Thank you for your help.
index.html
<div id="navbar"><navbar-div></navbar-div></div>
navbar.js:
Vue.component('navbar-content', {
props: ['name', 'link'],
template: '<a class="navbar-item" v-bind:href="link">{{ name }}</a>'
})
Vue.component('navbar-div', {
props: ['links'],
template: `
<nav class="navbar is-dark">
<div class="navbar-brand">[...]</div>
<div class="navbar-menu">
<nav class="navbar-start">
<navbar-content v-for="item in links"
v-bind:key="item.id"
v-bind:name="item.name"
v-bind:link="item.link">
</navbar-content>
</nav>
</div>
</nav>
`})
new Vue({
el: "#navbar",
data: {
links: [
{id: 1, name: "Item 1", link: "link1"},
{id: 2, name: "Item 2", link: "link2"}
],
}
})
In your HTML you have to bind the links to navbar-div; otherwise it's not going to receive the data as props
Vue.component('navbar-content', {
props: ['name', 'link'],
template: `
<a
class="navbar-item"
:href="link"
>
{{ name }}
</a>
`
})
Vue.component('navbar-div', {
props: ['links'],
template: `
<nav class="navbar is-dark">
<div class="navbar-brand">[...]</div>
<div class="navbar-menu">
<nav class="navbar-start">
<navbar-content
v-for="item in links"
:key="item.id"
:name="item.name"
:link="item.link"
/>
</nav>
</div>
</nav>
`
})
new Vue({
el: "#navbar",
data: {
links: [{
id: 1,
name: "Item 1",
link: "link1"
},
{
id: 2,
name: "Item 2",
link: "link2"
}
],
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="navbar">
<navbar-div :links="links"></navbar-div>
</div>

Vue.js pass a callback as prop to child component and execute on parent when it is clicked

I am trying to create a dropdown component, in which it receives various data and the list items are built dynamically, but I am having difficulty detecting the click on any item in the list
parent
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: editFunction(id)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: deleteFunction(id)
}
]"
/>
child Dropdown.vue
<template>
<div class="dropdown">
<a class="trigger">
<i class="far fa-ellipsis-h"></i>
</a>
<ul class="items">
<li v-for="(item, index) in items" :key="index" class="item">
<a>
<i :class="item.icon"></i>
{{ item.text }}
</a>
</li>
</ul>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
props: {
items: {
type: Array,
default: () => []
}
}
})
</script>
currently in this way, as soon as the parent component is created, the editFunction(id) and deleteFunction(id) methods are executed.
I know it's possible because vuetifyjs it does that way, but I tried to inspect the source code but I got nowhere.
What you want to achieve can be done by
parent.vue
<child-componet :parent-method="thisIsTheMethod" />
...
methods: {
thisIsTheMethod()
{
//here it does something
}
}
note that the method passed inside the prop does not have the parenthesis () because you are passing a reference to it.
To use it inside the child component add the ()
#click="parentMethod()"
To go back to your example, change this:
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: editFunction(id)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: deleteFunction(id)
}
]"
/>
to
<Dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
click: () => editFunction(10)
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
click: () => deleteFunction(20)
}
]"
/>
and leave your editFunction(id) method declaration as is. The argument will be automatically injected.
Despite this solution would work, the best way to achieve this communication between parent and child would be to emit the value in the child and then listen for it
there have betters ways to do it, but about your idea should be as below:
Vue.component('dropdown', {
template: '#dropdown-template',
props: {
items: {
type: Array,
default: () => []
}
}
})
new Vue({
el: '#app',
data() {
return {
users: [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
]
}
},
methods: {
editFunction(id) {
console.warn('edit item ' + id)
},
deleteFunction(id) {
console.warn('delete item ' + id)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<table border="1">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>actions</tr>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>
<dropdown
:items="[
{
text: 'Edit',
icon: 'fal fa-edit',
event: 'edit-function'
},
{
text: 'Delete',
icon: 'fal fa-trash-alt',
event: 'delete-function'
}
]"
#edit-function="editFunction(user.id)"
#delete-function="deleteFunction(user.id)"
/>
</td>
</tr>
</tbody>
</table>
</div>
<script type="text/x-template" id="dropdown-template">
<div class="dropdown">
<a class="trigger">
<i class="far fa-ellipsis-h"></i>
</a>
<ul class="items">
<li v-for="(item, index) in items" :key="index" class="item">
<a #click="$emit(item.event)">
<i :class="item.icon"></i>
{{ item.text }}
</a>
</li>
</ul>
</div>
</script>

Change Page Title Dynamically AngularJS

Below is my code,
i need to change the maintitle dynamically in h2 tag on click of navigation (navlinks) through angularJS.
Thanks in advance..
var portfolioApp = angular.module('portfolioApp', []);
portfolioApp.controller('navCtrl', ['$scope', '$location', function ($scope, $location) {
$scope.navLinks = [{
Title: 'home',
LinkText: 'Home'
}, {
Title: 'about',
LinkText: 'About Us'
}, {
Title: 'portfolio',
LinkText: 'Portfolio'
}, {
Title: 'contact',
LinkText: 'Contact Us'
}];
$scope.navClass = function (page) {
var currentRoute = $location.path().substring(1) || 'home';
return page === currentRoute ? 'active' : '';
};
$scope.maintitle = "Any Title";
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<div ng-app="portfolioApp" ng-controller="navCtrl">
<h2>{{maintitle}}</h2>
<header class="well sidebar-nav">
<ul class="nav nav-list">
<li ng-repeat="navLink in navLinks" ng-class="navClass('{{navLink.Title}}')">
<a href='#/{{navLink.Title}}'>{{navLink.LinkText}}</a>
</li>
</ul>
</header>
</div>
<div ng-app="portfolioApp">
<div ng-controller="navCtrl">
<h2>{{maintitle}}</h2>
<header class="well sidebar-nav">
<ul class="nav nav-list" >
<li ng-repeat="navLink in navLinks" ng-class="navClass('{{navLink.Title}}')">
<a href='#/{{navLink.Title}}'>{{navLink.LinkText}}</a>
</li>
</ul>
</header>
<div>
</div>
and use
$scope.maintitle = 'title'
Check Out This:
var portfolioApp = angular.module('portfolioApp', []);
portfolioApp.controller('navCtrl', ['$scope', '$location', function ($scope, $location) {
$scope.maintitle = "Some Thing";
$scope.navLinks = [{
Title: 'home',
LinkText: 'Home'
}, {
Title: 'about',
LinkText: 'About Us'
}, {
Title: 'portfolio',
LinkText: 'Portfolio'
}, {
Title: 'contact',
LinkText: 'Contact Us'
}];
$scope.navClass = function (page) {
var currentRoute = $location.path().substring(1) || 'home';
return page === currentRoute ? 'active' : '';
};
$scope.goToLink = function(navlink){
$scope.maintitle = navlink.Title;
// fix location service accordin to your requirment
$location.path(navlink.Title);
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<div ng-app="portfolioApp">
<header class="well sidebar-nav">
<ul class="nav nav-list" ng-controller="navCtrl">
<h2>{{maintitle}}</h2>
<li ng-repeat="navLink in navLinks" ng-class="navClass('{{navLink.Title}}')">
<a ng-click="goToLink(navLink)">{{navLink.LinkText}}</a>
</li>
</ul>
</header>
</div>
$scope.maintitle = "text you want";
EDIT: It won't work since it is out of controller. I can advise carrying it into controller, making another controller or searching for ways of manipulating items that are out of controller.

Depending on data attribute how to Hide the tag and display in another div

This is the for each loop to render the data
<ul data-bind="foreach: { data: PersonData, as: 'ref' }">
<li>
<a data-bind="attr: { data: ref.Filter }" class="filterbtn">
<span data-bind="html: ref.Name"></span>
<span data-bind="text: ref.Age" class="age"></span>
</a>
</li>
</ul>
I want to hide if data attribute value is data="people" and display it in another div.
How can I achieve this?
Thanks in advance!
You need to have a computed setup to make things working
view:
<ul data-bind="foreach: { data: PersonData}">
<li> <a data-bind="attr: { data: Filter },visible:Filter!='people'" class="filterbtn">
<span data-bind="html: Name"></span>
<span data-bind="text: Age" class="age"></span>
</a>
</li>
</ul>
<div data-bind="foreach:data"> <span data-bind="html: Name"></span>
<span data-bind="text: Age" class="age"></span>
</div>
viewModel:
var ViewModel = function () {
var self = this;
self.PersonData = ko.observableArray([{
'Filter': 'people',
'Name': 'cool',
'Age': '1'
}, {
'Filter': 'nope',
'Name': 'cooler',
'Age': '2'
}, {
'Filter': 'people',
'Name': 'hotter',
'Age': '3'
}])
self.data = ko.computed(function () {
return ko.utils.arrayFilter(self.PersonData(), function (item) {
return item.Filter === "people"; //do a case check here(if)
});
});
};
ko.applyBindings(new ViewModel());
working fiddle up here

Categories