0
votes

I'm using Rails 5.1 + Webpack running Vue 2. I'm fairly new to Vue, not so new to Rails.

I'm building a navbar component starting from the Bulma navbar. My objective is to be able to dynamically create links in the navbar by simply adding a navlink element nested within.

In my application.html.erb I have already created this structure (navlink nested inside navbar).

In hello_vue.js I'm initializing the Vue components and assigning them to a root element (one for the navbar and one for the rest of the content).

In navbar.vue, I'd like to iterate through all the and create a menu item accordingly. In order to do so, I added an empty navlinks data to navbar that I want to populate once it gets mounted() by fetching all its children (the nested navlink tags).

This is however not working, whenever I reference this.$children I get an empty array. Any idea why is this happening?

This is all the code.

application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>RailsVue</title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= stylesheet_pack_tag 'hello_vue' %>
  </head>

  <body>
    <div id="navbar-container">
        <navbar>
            <navlink href="http://google.com">
                Link 1
            </navlink>
            <navlink href="http://facebook.com">
                Link 2
            </navlink>
        </navbar>
    </div>
    <%= yield %>
    <%= javascript_pack_tag 'hello_vue' %>
  </body>
</html>

hello_vue.js

import Vue from 'vue/dist/vue.esm'
import App from './app.vue'
import Navlink from './components/navlink.vue'
import Navbar from './components/navbar.vue'

const navbar = new Vue({
    el: "#navbar-container",
    components: { Navbar, Navlink }
})

const app = new Vue({
    el: '#hello',
    data: {
      message: "Can you say hello?"
    },
    components: { App }
})

navbar.vue

<template>
  <div>
    <nav class="navbar is-transparent">
      <div class="navbar-brand">
        <a class="navbar-item" href="http://bulma.io">
          <img src="http://bulma.io/images/bulma-logo.png" alt="Bulma: a modern CSS framework based on Flexbox" width="112" height="28">
        </a>

        <div class="navbar-burger burger" data-target="navMenuTransparentExample">
          <span></span>
          <span></span>
          <span></span>
        </div>
      </div>

      <div id="navMenuTransparentExample" class="navbar-menu">
        <div class="navbar-start">
          <a v-for="navlink in navlinks" class="navbar-item" :href="navlink.href">
            <slot></slot>
          </a>
        </div>
      </div>
    </nav>
  </div>
</template>

<script>
import Navlink from './navlink.vue'
export default {
  name: "Navbar",
  data: function () {
    return {
      navlinks: []
    }
  },
  created: function(){
    // let children = this.$children;
    console.log(this.$children);
    // this.navlinks = this.$children;
    // this.navlinks = this.$children;
  }
}
</script>

navlink.vue

<template>
    <div class="navlink">
        <slot></slot>
    </div>
</template>

<script>
    export default {
      name: "Navlink"
    }
</script>
1

1 Answers

1
votes

Instead of created hook, use mounted hook, you can able to get this.$children

mounted : function(){
    // let children = this.$children;
    console.log(this.$children);
    // this.navlinks = this.$children;
    // this.navlinks = this.$children;
  }

You can modify components to achieve your requirement as follows

application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>RailsVue</title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= stylesheet_pack_tag 'hello_vue' %>
  </head>

  <body>
    <div id="navbar-container">
         <navbar>
      <navlink>
        <a  class="navbar-item" href="http://google.com">
          Link 1
        </a>

      </navlink>
      <navlink>
        <a  class="navbar-item" href="http://facebook.com">
          Link 2
        </a>
      </navlink>
    </navbar>
    </div>
    <%= yield %>
    <%= javascript_pack_tag 'hello_vue' %>
  </body>
</html>

hello_vue.js

import Vue from 'vue/dist/vue.esm'
import App from './app.vue'
import Navlink from './components/navlink.vue'
import Navbar from './components/navbar.vue'

const navbar = new Vue({
    el: "#navbar-container",
    components: { Navbar, Navlink }
})

navbar.vue

<template>
  <div>
    <nav class="navbar is-transparent">
      <div class="navbar-brand">
        <a class="navbar-item" href="http://bulma.io">
          <img src="http://bulma.io/images/bulma-logo.png" alt="Bulma: a modern CSS framework based on Flexbox"
               width="112" height="28">
        </a>

        <div class="navbar-burger burger" data-target="navMenuTransparentExample">
          <span></span>
          <span></span>
          <span></span>
        </div>
      </div>
      <slot></slot>
    </nav>
  </div>
</template>

<script>
  // import Navlink from './navlink.vue'

  export default {
    name: 'Navbar',
    data: function () {
      return {
        navlinks: []
      }
    },
    mounted: function () {
      // let children = this.$children;
      // console.log(this)
      console.log(this.$children)
      // console.log(this.$root.$children[0])
      // this.navlinks = this.$children
      // this.navlinks = this.$children;
    }
  }
</script>

navlink.vue

<template>
  <div class="navlink">
    <slot></slot>
  </div>
</template>

<script>
  export default {
    name: 'Navlink'
  }
</script>