Markus
Markus

Reputation: 624

Creating a vue.js component for a button group

I want to create a webpage containing several button groups. The existing code is

$(document).ready(function() {
  $(".nav-link").click(function() {
      console.log(this);
  });
});

Vue.component('nav-bar', {
    props: ['navs'],
    template: `<ul class="nav">
        <li v-for="nav in [{id: 0, text: 'Vege'}, {id:1, text: 'Ch'}]"
            v-bind:item="nav"
            v-bind:key="nav.id">
                <a class="nav-link" v-on:click="setactive($event)">{{ nav.text }}</a></li>
    </ul>`,
    methods: {
        setactive: function(event) {
            $(event.target).closest('ul').find('a.active').removeClass('active');
            $(event.target).addClass('active');
        },
    created: function() {
        $(this).find('a').first().addClass('active');
    }
    }
});

var app = new Vue({
    el: '#app',
    data: {
        navs: [
            { id: 0, text: 'Vegetables' },
            { id: 1, text: 'Cheese' },
            { id: 2, text: 'Milk' }
        ]
    }
});
.nav {
    display: flex;
    flex-wrap: wrap;
    list-style: none;
}
.nav-link.active {
    color: #fff;
    background-color: #007bff;
}
.nav-link {
    color: #4183c4;
    text-decoration: none;
    background-color: transparent;
    border-radius: .25rem;
    padding: .5rem 1rem;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="app">
    <nav-bar></nav-bar>
</div>

My problems are:

I do not want to use bootstrap button-groups.

Upvotes: 2

Views: 2501

Answers (1)

tony19
tony19

Reputation: 138286

if changing <li v-for="nav in [{id: 0, text: 'Vege'}, {id:1, text: 'Ch'}]" to <li v-for="nav in navs", nothing is displayed at all. Whats wrong with that?

That depends on the value of navs. We'd need to see the exact code to troubleshoot.

how can the nav items be given directly to <nav-bar>?

To pass the value of navs from App to <nav-bar>, use v-bind:

<div id="app">
  <nav-bar v-bind:navs="navs" />

  <!-- OR shorthand for v-bind -->
  <nav-bar :navs="navs" />
</div>

why isn't the first item's class set to active upon creation?

The DOM hasn't yet been stamped in the created lifecycle hook, so jQuery isn't able to find the element to set active. Since the elements you're querying are dynamically added (via the navs prop), you'd have to:

  1. Wait until the navs prop changes (instead of using a lifecycle hook). Use a watcher for this:

    Vue.component('nav-bar', {
      watch: {
        navs: {
          handler() { /* SEE NEXT STEP */ },
          immediate: true, // call handler immediately in case `navs` data is already available
        }
      }
    })
    
  2. Wait until the elements are added to the DOM in $nextTick.

    // in watch:
    handler() {
      this.$nextTick(() => {
        $(this).find('a').first().addClass('active');
      });
    },
    

Vue.component('nav-bar', {
  props: ['navs'],
  template: `<ul class="nav" ref="list">
        <li v-for="nav in navs"
            v-bind:item="nav"
            v-bind:key="nav.id">
          <a class="nav-link" v-on:click="setactive($event)">{{ nav.text }}</a>
        </li>
      </ul>`,
  methods: {
    setactive: function(event) {
      $(event.target).closest('ul').find('a.active').removeClass('active');
      $(event.target).addClass('active');
    },
  },
  watch: {
    navs: {
      immediate: true,
      handler() {
        this.$nextTick(() => {
          $(this.$refs.list).find('a').first().addClass('active');
        });
      }
    }
  }
});

var app = new Vue({
  el: '#app',
  data: {
    navs: [{
        id: 0,
        text: 'Vegetables'
      },
      {
        id: 1,
        text: 'Cheese'
      },
      {
        id: 2,
        text: 'Milk'
      }
    ]
  }
});
.nav {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
}

.nav-link.active {
  color: #fff;
  background-color: #007bff;
}

.nav-link {
  color: #4183c4;
  text-decoration: none;
  background-color: transparent;
  border-radius: .25rem;
  padding: .5rem 1rem;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="app">
  <nav-bar :navs="navs"></nav-bar>
</div>

Upvotes: 1

Related Questions