How to set active menu option with Vue - javascript

I have a two column layout. The left column has a stacked menu, and when the user selects an option the content displays in the right. What's the best way to set the active class on the selected menu link?
I have come up with a solution below, but it seems kind of verbose. Each menu item has a class assignment which is conditional upon data.activeTab. A click event handler updates the value of data.activeTab.
<template>
<div>
<div class="row">
<div class="col-md-3">
<ul class="nav nav-pills nav-stacked">
<li role="presentation" :class="{active : activeTab == 'option1'}">
<a :href="'/#/sales/' + uuid + '/option1'" #click="updateMenu">Option 1</a>
</li>
<li role="presentation" :class="{active : activeTab == 'option2'}">
<a :href="'/#/sales/' + uuid + '/option2'" #click="updateMenu">Option 2</a>
</li>
</ul>
</div>
<div class="col-md-9">
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
export default {
data : function () {
return {
activeTab : 'option1'
}
},
props : [
'uuid'
],
methods : {
updateMenu : function (event) {
var str = event.target.toString();
this.activeTab = str.substr(str.lastIndexOf('/') + 1);
}
}
}
</script>

Use router-link instead of your manual anchor tags and set the linkActiveClass in your router construction options.
I can't see your routes object, so this is a best guess based on what you have above. Refer to the router link documentation I linked above to determine the best way to set up your to attributes.
<router-link tag="li" :to="{ path: 'sales', params: { uuid : uuid, option: "option1" }}">
<a>Option 1</a>
</router-link>
<router-link tag="li" :to="{ path: 'sales', params: { uuid : uuid, option: "option2" }}>
<a>Option 2</a>
</router-link>
Then, wherever you initialized your router,
new VueRouter({
routes,
linkActiveClass: "active"
});

Related

How to target an item inside ngFor with introjs

I need to use introjs to tour new features on an angular 6 app.
Inside the tour some step target items in ngFor in a child component of the component where I start introjs. The library is able to target child component html elements but seems to not be able to target element inside ngFor. The usage I want is simple I have a li that represent a card item I loop on as :
<div *ngFor="let source of sourcesDisplay; let idx = index" class="col-lg-5ths col-md-4 col-sm-6 col-xs-12">
<!-- item -->
<div class="item blue-light">
<div class="header">
<div class="categories">
<div class="truncat">{{source.categories.length > 0? source.categories : 'NO CATEGORY'}}</div>
</div>
<a routerLink="{{ organization?.name | routes: 'sources' : source.id }}" class="title">
<div class="truncat">{{source.name}}</div>
</a>
<div class="corner-ribbon blue-light" *ngIf="isARecentSource(source.createdAt)">New</div>
<div class="corner-fav" [class.is-active]="isSourceFavorite(source.id)"
(click)="toggleFavorite(source.id)"><em class="icon-heart-o"></em></div>
</div>
<a class="content" routerLink="{{ organization?.name | routes: 'sources' : source.id }}">
<div class="icon">
<em [ngClass]="source.icon? 'icon-'+source.icon : 'icon-cocktail3'"></em>
</div>
</a>
<div class="actions">
<ul class="buttons three">
<li class="icon-font" ><a (click)="openDashboardModal(source)"
[class.disable]="source.powerbi.length === 0" class="btn-effect"><em
class="icon-chart-bar"></em></a></li>
<li class="icon-font"><a
(click)="openDatalakeModal(source)" [class.disable]="source.datalakePath.length === 0"
class="btn-effect"><em class="icon-arrow-to-bottom"></em></a>
</li>
<li class="icon-font"><a routerLink="{{ organization?.name | routes: 'sources' : source.id }}"
class="btn-effect"><em class="icon-info-circle"></em></a></li>
</ul>
</div>
</div>
</div><!-- /item -->
And I want to target part of this card like a button for example as :
<li class="icon-font" id="step4{{idx}}">
<a (click)="openDashboardModal(source)" [class.disable]="source.powerbi.length === 0" class="btn-effect">
<em class="icon-chart-bar"></em>
</a>
</li>
and then in my component :
const intro = IntroJs.introJs();
intro.setOptions({
steps: [
{
element: document.querySelector('#step40'),
intro: 'Welcome to the Data Portal ! Explore and download data accross any division. Let\'s discover the new home page.'
}]})
intro.start();
Is there something I am doing wrong ? is introjs not able to do it at all ? is there another lib that can do it?
Thank you in advance
The sections generated by the *ngFor loop may not be rendered yet in the AfterViewInit event. To detect the presence of these elements, you should use #ViewChildren and monitor the QueryList.changes event.
In the template, define a template reference variable #step instead of id:
<li class="icon-font" #step>
<a (click)="openDashboardModal(source)" [class.disable]="source.powerbi.length === 0" class="btn-effect">
<em class="icon-chart-bar"></em>
</a>
</li>
In the code, retrieve the elements with #ViewChildren, and subscribe to the QueryList.changes event. You may have to convert the QueryList to an array to access an element with a specific index.
#ViewChildren("step") steps: QueryList<ElementRef>;
ngAfterViewInit() {
this.startIntroJs(); // Maybe already present
this.steps.changes.subscribe((list: QueryList<ElementRef>) => {
this.startIntroJs(); // Created later
});
}
private startIntroJs(): void {
if (this.steps.length > 40) {
const intro = IntroJs.introJs();
intro.setOptions({
steps: [
{
element: this.steps.toArray()[40].nativeElement,
intro: 'Welcome to the Data Portal...'
}
]
});
intro.start();
}
}

toggle active class on list menu items - vue js

I want that, clicking on a menu item, the active class is triggered only for that specific item and removed for the others, until now I wrote this:
<template>
<nav class="navbar">
<div class="navbar__brand">
<router-link to="/">Stock Trader</router-link>
</div>
<div class="navbar__menu">
<ul class="navbar__menu--list">
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
<router-link to="/portfolio">Portfolio</router-link>
</li>
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
<router-link to="/stocks">Stocks</router-link>
</li>
</ul>
</div>
<div class="navbar__menu--second">
<ul class="navbar__menu--list">
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
End Day
</li>
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
Save / Load
</li>
</ul>
</div>
</nav>
</template>
<script>
export default {
data(){
return{
isActive: false
}
}
}
</script>
now of course, when I click on one item the active class is inserted/removed for all the items, what is the best solution for making that only a specific item, on clicking on it, receives the active class?
You'll want some sort of identifier for each clickable item and set that to your data property. For example
data() {
return { active: null }
}
and in your list items (for example)
<li #click="active = 'portfolio'"
class="navbar__menu--item"
:class="{active:active === 'portfolio'}">
In this example, the identifier is "portfolio" but this could be anything, as long as you use a unique value per item.
You could keep an object of links you have and handle a click on each of items. E.g.
data() {
return {
links: [
{
title : 'Portfolio',
to : '/portfolio',
isActive : false,
location : 'first',
},
{
title : 'Stocks',
to : '/stocks',
isActive : false,
location : 'first',
},
{
title : 'End Day',
to : '#',
isActive : false,
location : 'second',
},
{
title : 'Save / Load',
to : '#',
isActive : false,
location : 'second',
},
]
};
},
methods: {
handleNavClick(item) {
this.links.forEach(el, () => {
el.isActive = false;
});
item.isActive = true;
}
},
I do this in vue3.
the template is:
<li
v-for="(title, index) in titles"
class="title"
:key="index"
:class="{ active: active === index }"
#click="updateActive(index)"
>
{{ title }}
</li>
and the script is
<script lang="ts" setup>
import { ref } from "vue"
const titles = ["title1","title2","title3"]
const active = ref(-1)
function updateActive(val: number) {
active.value = val
}
</script>
If you have several ul => use title instead of index. For example :
<ul>
<div>
Applicants
</div>
<li
v-for="(title, index) in applicantMenuTitles"
:key="index"
:class="{ active: active === title }"
#click="updateActive(title)"
>
{{ title }}
<div
v-if=" active === title "
class="cursor"
/>
</li>
</ul>
<ul>
<div>
Offices
</div>
<li
v-for="(title, index) in officeMenuTitles"
:key="index"
:class="{ active: active === title }"
#click="updateActive(title)"
>
{{ title }}
<div
v-if=" active === title "
class="cursor"
/>
</li>
</ul>
And on script :
...
const active = ref('')
function updateActive(title: string) {
active.value = title
}

Vue.js To add class active when click and remove the previous one

I want to achieve this active link on my div element here you can see the example that i want to do with my code
http://jsfiddle.net/fiddleyetu/9ff79/
$(function() {
$( 'ul.nav li' ).on( 'click', function() {
$( this ).parent().find( 'li.active' ).removeClass( 'active' );
$( this ).addClass( 'active' );
});
});
in here using vue.js i can't do the active link on my div elements
here is my code for the elements on which i have to do the links active
<div class="conversion">
<div class="file has-text-centered icon-active-color" v-on:click="activeiconc">
<img src="../imgs/png.png"/>
<h4>.PNG</h4>
</div>
<div class="file has-text-centered" v-on:click="activeicon" v-bind:class="{ 'icon-active-color':activeiconc,}">
<img src="../imgs/pdf.png"/>
<h4>.PDF</h4>
</div>
<div class="file has-text-centered" v-on:click="activeicon" v-bind:class="{ 'icon-active-color':activeiconc }">
<img src="../imgs/jpg.png"/>
<h4>.JPG</h4>
</div>
<div class="file has-text-centered" v-on:click="activeicon" v-bind:class="{ 'icon-active-color':activeiconc }">
<img src="../imgs/psd.png"/>
<h4>.PSD</h4>
</div>
</div>
js
export default {
components: {
MainLayout
},
data: function(){
return {
logo: false,
color:false,
list:true,
grid:false,
deletebtn:false,
isImageModalActive: false,
activerow: false,
activeiconc:false,
}
},
methods:{
showgrid:function(){
this.grid = true;
this.list = false;
},
showlist:function(){
this.list ^=true;
this.grid = false
},
showLogo:function(){
this.logo ^= true;
this.color = false;
this.deletebtn ^= true;
this.activerow ^= true
},
showColor:function(){
this.color ^= true;
this.logo = false;
},
activeicon:function(){
this.activeiconc ^= true;
}
}
}
I'm new to Vue but an easy way to turn your JQuery example into Vue.js is this:
Jsfiddle demo
Basically, you need to store the active element in your Vue data, and set the class based on in. You could use a v-for to render the list.
The HTML part:
<div id="app">
<ul>
<li #click="activate(1)" :class="{ active : active_el == 1 }">Link 1</li>
<li #click="activate(2)" :class="{ active : active_el == 2 }">Link 2</li>
<li #click="activate(3)" :class="{ active : active_el == 3 }">Link 3</li>
</ul>
</div>
The Vue.js:
var app = new Vue({
el:"#app",
data:{
active_el:0
},
methods:{
activate:function(el){
this.active_el = el;
}
}
});
If you want to active directly via code, VueJS allows you to bind the active of the anchor tag directly to the index of the li element so that when the vuejs variable bound to the index changes, the active also changes. Check these two links for more details
This is the crux of the solution
:class="{current:i == current}
available on the fiddle below and another post that explains in story format how anchor active can be dynamically controlled in vuejs
https://jsfiddle.net/Herteby/kpkcfcdw/
https://stackoverblow.wordpress.com/2021/04/03/how-modern-javascript-makes-click-simulation/
You can use more easily way like below instead of adding and removing active class:
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
This paradigm provides dynamically set multiple different class for different scenarios.
This is from Vue 2 official documentation. There are many ways.

ng-class does not change class unless I refresh the page

I'm using ng-class in my view to change the class of and element and it works fine on page load. Here is my code:
<ul>
<li ng-class="'{{account.currency}}' == '{{currency}}' ? 'active': 'inactive'" ng-repeat="account in accounts" bind="accounts">
<a href="#/deposits/{{account.currency}}" ng-click="changeClass('{{account.currency}}', '{{currency}}')">
{{currency}}
</a>
</li>
<ul>
As you can see, it takes the second currency from the URL and compares it with another to decide which class it should assign to my li element.
The trouble now is that when someone clicks on the link and the URL changes, the class assigned does not change. Any idea how I can get the class assigned by ng-class to change when the
You don't need to call a function for this, this can be easily achieved inline
try like this:
<li ng-class="{'active' : account.currency == currency, 'inactive':
account.currency != currency}">
OR
<li ng-class="{true: 'active', false: 'inactive'}[account.currency == currency]">
If you're using angular v.1.1.4+
<li ng-class="account.currency == currency ? 'active': 'inactive'}">
Try this,
var app = angular.module('app', []);
app.controller('myctrl', function() {
var vm = this;
vm.accounts = [{
currency: 'Doller'
}, {
currency: 'Pound'
}, {
currency: 'Euro'
}];
vm.changeClass = function(account) {
vm.active = account.currency;
}
});
.active {
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body>
<div ng-app="app" ng-controller="myctrl as ct">
<ul>
<li ng-class="{'active': ct.active === account.currency}" ng-repeat="account in ct.accounts">
<a href="#" ng-click="ct.changeClass(account)">
{{account.currency}}
</a>
</li>
</ul>
</div>
</body>
Make the ng-class= {'active': account.currency === currency}
in the css file you should have the .active{} class. And let the inactive class be the default behaviour.
P.S The answers of Dhaval should work also

Creating ember components

I have the following code here for an embeddable component using emberjs which consists of a tab view and different tab panes. It tab-pane has a different handlebars template :
App.obj = Ember.Object.create({
"items": data['item-list']
});
var templates = [{
name: 'list',
list: '
<div class="tab-pane">
<ul class="item-list">
{{#with App.obj}}
{{#each item in items}}
<li class="c-item">
<a href="">
<div class="cc-item-a">{{item.bar}}</div>
<div class="cc-item-b">{{item.title}}</div>
<div class="cc-item-c">{{item.foo}}</div>
</a>
</li>
{{/each}}
{{/with}}
</ul>
</div>'
}, {
name: 'cal',
cal: ' <div class="tab-pane">calendar</div>'
}, {
name: 'add',
add: '<div class="tab-pane">add events</div>'
}, {
name: 'dashboard',
dashboard: '{{outlet tab}}\
<ul class="tabrow">
<li><a href="#" data-tab="list" {{action selectTab "tabA"}}>list</a></li>
<li><a href="#" data-tab="cal" {{action selectTab "tabB"}}>cal</a></li>
<li><a href="#" data-tab="add" {{action selectTab "tabC"}}>add</a></li>
</ul>'
}];
for (var i = 0; i < templates.length; i++) {
$('.container').append('<script type="text/x-handlebars" data-template-name="' + templates[i].n
ame + '">' + templates[i][templates[i].name] + "</script>");
};
The templates are appended one after another to the container. Is there a better approach to it the ember-way ? Something like -
<script type="text/x-handlebars">
{{my-component}}{{/my-component}}
</script>
The code is meant to be embeddable, so minimal no. of templates are required in the html file, that's why I wrapped it in the javascript.
Does ember has a better approach to it ?

Categories

Resources