2

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:

  • 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?
  • why isn't the first item's class set to active upon creation?
  • how can the nav items be given directly to <nav-bar>?

I do not want to use bootstrap button-groups.

2
  • I did not mean to give this impression. I have read the tutorial at vue.org, though not to the end. Nevertheless, I will try to refine my question. Commented Sep 16, 2018 at 21:43
  • I read more documentation, hoping that the example now contains the basics of Vue.js. Commented Sep 16, 2018 at 22:59

1 Answer 1

1

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>

Sign up to request clarification or add additional context in comments.

3 Comments

Wow! Would never have used nextTick()... Thanks a lot!
Do you also have an idea how to substitute $(".nav-link").click(function()... by $(".nav-bar").click(function()... as <a class="nav-link"> is not visible from the outside? The click event does not seem to bubble upwards to its parent, if I am doing this substitution...
@Markus No problem :) Regarding the second problem, it would be best to ask that as a new question, so that someone familiar with the topic could answer you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.