secondman
secondman

Reputation: 3277

Creating Active Class on Menu in Vue

I'm trying to access an anchor element in a computed property in order to determine whether is should be set to active or remain as is.

I've seen examples using v-for and looping through data properties, but this is a static menu like so:

<template>
<div class="panel panel-default">
    <div class="panel-heading">Application Menu</div>
    <ul class="list-group">
        <a :href="route('applications.index')" 
           :class="activeClass" 
           class="list-group-item">Applications</a>
        <a :href="route('applications.repository')" 
           :class="" 
           class="list-group-item">Token Repository</a>
        <a :href="route('applications.notifications')" 
           :class="" 
           class="list-group-item">Notifications</a>
    </ul>
</div>
</template>

My activeClass computed prop would compare the href of the current anchor to the current location of the browser and return active if they match.

I've consulted all the documentation on the Vue website and Googled extensively, and I've not yet found a way to access the href attribute of the anchor calling the computed property.

Upvotes: 1

Views: 3904

Answers (3)

secondman
secondman

Reputation: 3277

Final solution:

<template>
<div class="panel panel-default">
    <div class="panel-heading">Application Menu</div>
    <ul class="list-group">
        <a :href="route('applications.index')" 
           :class="activeClass('applications')" 
           class="list-group-item">Applications</a>
        <a :href="route('applications.repository')" 
           :class="activeClass('repository')" 
           class="list-group-item">Token Repository</a>
        <a :href="route('applications.notifications')" 
           :class="activeClass('notifications')" 
           class="list-group-item">Notifications</a>
    </ul>
</div>
</template>

<script>

export default {
    data () {
        return {
            currentLink: location.href,
        }
    },
    computed: {
        routes() {
            return window.routes
        },
    },
    mounted() {
        this.setCurrentLink()
    },
    methods: {
        route(url) {
            return this.routes.route(url)
        },
        activeClass(segment) {
            return segment == this.currentLink ? 'active' : ''
        },
        setCurrentLink() {
            this.currentLink = new URL(location.href).pathname.split('/').pop();
        }
    }
}
</script>

Works perfectly without the need to iterate through a loop. I call this a rare use case, but for something small and static it fits the bill.

Thanks to all who contributed.

Upvotes: 1

Roy J
Roy J

Reputation: 43881

You are thinking about it wrong. You should not be trying to access the attributes of DOM elements as if they are data. The DOM is not a data store. Data should be in your viewmodel and be used in the DOM.

You should have an array of objects that describe your items. (Also, the only permitted children of a ul are li.) You could do something like this:

<ul class="list-group">
    <li v-for="item in menuItems">
      <a :href="item.route" 
         :class="isActive(item)" 
         class="list-group-item">{{model.label}}</a>
    </li>
</ul>

If you want to have a computed that works for each of your items, you would need to make a component for each item. See vuejs using computed property for array elements

Upvotes: 2

Ken Kinder
Ken Kinder

Reputation: 13120

You can't do that, and here's why. A computed property should be universal. That is to say, its value is not determined by who is accessing it. If you access this.activeClass from inside a method, its value should be the same as activeClass inside a template, assuming the state information it depends on is the same. (And who is accessing it is not state information)

If you're using Vue-Router, you can however just compare the value of $route.path and use a different class. For example:

<a :href="route('applications.index')" 
   :class="$route.path === 'whatever' ? 'active-class' : 'inactive-class'">Applications</a>

Upvotes: 2

Related Questions