Vue2 + Webpack + Foundation6 integration


#1

Hi guys!

Anyone can please give me some advice or hint how to set up a new vue2 project using webpack and integrating Foundation6 (js+sass)?

In fact I currently built up my project but I’m not really sure if I done it well. What I done yet:

-1. npm install --save foundation-sites
-2. created a theme.scss file to configure styling. There is no any setup there yet just 2 lines:

@import “~foundation-sites/scss/foundation”;
@include foundation-everything;

-3. added both the .js file of the foundation and my theme.scss to my ‘vendor’ entry of webpack:

entry: {
vendor: [‘foundation-sites’, ‘./source/public/styles/theme.scss’],

}

-4. configured webpack commonschunk plugin to process that vendor (and my engine - which contains all vue related js) entry:

new webpack.optimize.CommonsChunkPlugin({
names: [‘engine’,‘vendor’],
filename: ‘public/script/[name].js’
}),

-5. configured all .css and .scss processing via ExtractTextWebPackPlugin using css,postccs and sass loader.

-6. added jquery to the webpack.ProvidePlugin settings:

new webpack.ProvidePlugin({
$: “jquery”,
jQuery: ‘jquery’,
“window.jQuery”: “jquery”
}),

At this point I can successfully build my project. It results in multiple files:

  • vendor.js, engine.js, project.js
  • vendor.css, project.css

What about the actual app? Well, I just started my project so it contains nothing yet but a simple test if everything works fine:

import Vue from 'vue';
import Main from './Main.vue';

export const app = new Vue({
  el: '#main',
  data: {
    message: 'Hello'
  },
  ...Main,
  mounted: function()  {
    $(this.$el).foundation();
  }
});

And here is my Main.vue file:

<template>
  <div>
    <ul class="accordion" data-accordion>
      <li class="accordion-item is-active" data-accordion-item>
        <a href="#" class="accordion-title">Accordion 1</a>
        <div class="accordion-content" data-tab-content>
          First content
        </div>
      </li>
      <li class="accordion-item" data-accordion-item>
        <a href="#" class="accordion-title">Accordion 1</a>
        <div class="accordion-content" data-tab-content>
          Second content
        </div>
      </li>
      <li class="accordion-item" data-accordion-item>
        <a href="#" class="accordion-title">Accordion 1</a>
        <div class="accordion-content" data-tab-content>
          Third content
        </div>
      </li>
    </ul>
  </div>
</template>

Finally the project built fine and the accordion (which uses both css and js) from Foundation6 also works fine.

What do you think about the above steps? Especially about call $(this.$el).foundation(); within the mounted() function?


Issues with Foundation JS and Vue
#2

The webpack setup looks okay from what I can tell.

The usage of Foundation’s JS with Vue (or any SPA where elements are dynamically rendered and removed again) it tricky, though:

Running $(this.$el).foundation() will only work for elements that are in the DOM at that moment. If you have components that are rendered later, these elements won’t be controled by Foundation.

The quick (and dirty way) way is to run this command in the mount funciton of any component that contains Foundation elements that need JS. But that won’t properly clean up event listeners when the component gets removed again.

The clean way, IMHO, would be to use custom directives as thin wrappers for each JS plugin:

<ul class="dropdown menu" v-fdropdown-menu data-click-open>
  <li><a href="#">Item 1</a></li>
  <li><a href="#">Item 2</a></li>
  <li><a href="#">Item 3</a></li>
  <li><a href="#">Item 4</a></li>
</ul>
Vue.directive('fdropdown-menu', {
  bind(el) {
    new Foundation.DropdownMenu(el)
  },
  unbind(el) {
    $(el).foundation.destroy()
  }
}

That wraps the activation logic nicely and takes care of properly cleaning up Event Listeners etc. when the element gets destroyed.

You can also probably create one directive to use for all, but that would mean you have to define the data-dropdown-menu attribute on the element as well as the v-foundation directive:

<ul class="dropdown menu" v-foundation data-dropdown-menu data-click-open>
  <li><a href="#">Item 1</a></li>
  <li><a href="#">Item 2</a></li>
  <li><a href="#">Item 3</a></li>
  <li><a href="#">Item 4</a></li>
</ul>
Vue.directive('fdropdown-menu', {
  bind(el) {
    $(el).foundation()
  },
  unbind(el) {
    $(el).foundation.destroy()
  }
}

A question of personal preference, I suppose.


Getting Foundation.Toggler to work via custom directive
#3

Thank you for the hint about importance of the destroy process.

Anyway, I tried to create a custom component - instead of a directive - using your first mention.
I just added a mounted() handler which contains:

mounted: function() {
  new Foundation.Accordion($(this.$el));
}

This shows up an accordion and auto-opens the first accordion-item (which has an is-active attribute). But nothing happens when I click on any other item. So it seems like doesn’t work.

When I do this:

mounted: function() {
  //new Foundation.Accordion($(this.$el)); NOT WORKING!
  $(this.$el).foundation(); //WORKS!
}

Everything works fine. According to the F6 documentation the .foundation() call initializes all plugins. I need only the accordion plugin here. How can I use the new instance correctly?

var elem = new Foundation.Accordion(element, options);

I also noticed that the resulted var elem will be an F6 object which contains for example a destroy() function. So I think I can use exactly that instance inside the vue’s beforeDestroy() handler:

elem.destroy();

#4

The directive worked out nicely for me. Thanks!


#5

When I try @LinusBorg’s first directive method, I always get: Uncaught TypeError: this.$element.data is not a function

I’m using single-file components, not sure if that matters. Anyone have any ideas?

EDIT: I should note that it’s coming from new Foundation.DropdownMenu(el)


#6

I figured it out. I needed to pass in the jQuery version of the element, like so: new Foundation.DropdownMenu($(el))

If there’s a better way to pass the jQuery version of the element straight into the directive, please let me know.

The thing I’m struggling with now is I’m trying to use Foundation.Toggler on a button but keep getting: Cannot read property '0' of undefined - See my new post about this here: Getting Foundation.Toggler to work via custom directive


#7

I’m an occasional user of VueJs, mainly for the purpose of prototyping ideas. I love the idea of Components and building parts of the system where my skills allow me to and then handing off to a team when it goes beyond my abilities.

After spending a lot of time getting Foundation and VueJs to work, I had a botched hack and didn’t really know why it worked. I ended up asking some friends who just set up and configured VueJs’ CLI with Foundation properly for me.

I hope it’s useful: https://github.com/JSSolutions/vuejs-foundation-ui