Andrew Welch · Insights · #craftcms #vuejs #frontend

Published , updated · 5 min read ·


For more tools, technologies, and techniques, check out the devMode.fm podcast!

Using VueJS 2.0 with Craft CMS

Vue­JS is a sim­ple yet pow­er­ful library for build­ing mod­ern web inter­faces, and it works great with Craft CMS

Nice View

While the cur­rent state of the JavaScript world is indeed bor­der­line insan­i­ty, occa­sion­al­ly a project comes along that mer­its a look. Vue­JS is one of them, specif­i­cal­ly the recent­ly released Vue­JS 2.0.

So what is it, exact­ly? Essen­tial­ly, it’s a way to cre­ate inter­ac­tive, data-dri­ven web inter­faces using JavaScript. It’s not a replace­ment for jQuery, but you may find that you don’t need jQuery any­more if you use it. It’s most sim­i­lar to React or Angu­lar, at least in terms of the prob­lems that it tries to solve.

jQuery is all about DOM manip­u­la­tion; Vue is about mak­ing inter­ac­tive inter­faces. Or as the cool kids are call­ing it, reac­tive. There’s def­i­nite­ly some over­lap, but Vue pro­vides an archi­tec­ture for build­ing fron­tend inter­faces, where­as jQuery pro­vides a toolk­it for DOM manipulation.

The appealing thing about VueJS is that despite the fact that it scales well, the learning curve isn’t that steep at all. And it makes frontend JavaScript fun again.

First, the bad news. I’m not going to teach you Vue­JS in this arti­cle. There are plen­ty of resources for that which do a far bet­ter job than I ever will, from the offi­cial Vue­JS doc­u­men­ta­tion to some com­plete­ly free Lara­casts Learn Vue 2: Step By Step on the sub­ject. There’s a real­ly good paid course Vue JS 2 — The Com­plete Guide, too (no affil­i­a­tion, etc.), and a nice sum­ma­ry arti­cle Why you should start using Vue.js.

But what I am going to do is show you how I replaced jQuery on this very web­site you’re read­ing with Vue­JS, and how you can inte­grate it with Craft CMS.

The sim­plis­tic exam­ples pro­vid­ed here bare­ly begin to scratch the sur­face of what Vue can do. How­ev­er, they do present a real-world exam­ple of how you can adapt it to the web­sites you build. It’s not just for web apps.”

Link Diving Into the Code

The first thing we need a bit of JavaScript for is the lit­tle ham­burg­er” menu in the mast­head ban­ner. When peo­ple click on it, it should show the menu, and then hide it when they click on it again. Pret­ty sim­ple stuff. Using jQuery, we might do some­thing like this:

$("#nav-icon").click(function() {
  $(this).toggleClass("open");
});

All this does is add the class .open when our #nav-icon is clicked on, and it removes it when it’s clicked on again. The .open class looks like this:

ul.nav-items.open {
  height: 100%;
  opacity: 1;
}

So okay, how do we do this in Vue? The HTML markup looks like this:

<div id="nav-icon" v-bind:class="{ open: menuOpen }" v-on:click="toggle">

Or we can make it even more con­cise using the short­cuts for v-bind and v-on:

<div id="nav-icon" :class="{ open: menuOpen }" @click="toggle">

And then we just include a lit­tle bit of Vue JavaScript to do the work for us:

new Vue({
    el: '#nav-menu',
    data: {
        menuOpen: false,
    },
    methods: {
        toggle: function() {
            this.menuOpen = !this.menuOpen;
        }
    },
});

We are cre­at­ing the new Vue object, set­ting the val­ue of the menuOpen data to false ini­tial­ly, and pro­vid­ing a toggle method that tog­gles the val­ue of menuOpen.

What we did in our HTML is we told Vue that we want­ed to bind the class .open to the val­ue of our menuOpen data. So when menuOpen is true, our #nav-icon has the class .open applied, and when menuOpen is false, it doesn’t.

Then we told Vue that we want­ed to bind the click event to our toggle method. That’s it! Not so bad, is it? If you don’t get it” right away, don’t feel bad. I’m just try­ing to show you how it works from a gestalt per­spec­tive, you don’t need to learn the seman­tics now.

So okay, big deal, you might say. The jQuery ver­sion looks just as easy, and makes sense to me already! The big deal comes in when you are mak­ing some­thing of sub­stance, where the archi­tec­ture that Vue pro­vides makes it eas­i­er and quick­er. And more robust.

Build­ing a front-end UI in jQuery can be done; but it’s akin to play­ing a game of Jen­ga: even­tu­al­ly, it’s going to fall over. Vue gives you scaf­fold­ing to ensure that what you’re build­ing has a sol­id foun­da­tion, so that adding a pent­house suite at the top is painless.

Link It’s All About the Mustaches

So the oth­er thing we need a bit of JavaScript for is the check­box­es on the home page. We need to ensure that only two of them can ever be checked at any giv­en time, and we want to dis­play cus­tom mes­sage depend­ing on the com­bi­na­tion of check­box­es that are selected.

Nor­mal­ly, Vue­JS uses {{ }} as delime­ters to dis­play vari­ables on the fron­tend, so for instance if we did:

<h1>{{ menuOpen }}</h1>

…it’d print out the val­ue of our menuOpen data. Yep, you can mix this into your HTML code just like a tem­plat­ing lan­guage! Vue has a vir­tu­al DOM that it tracks behind the scenes, so that when changes are made it intel­li­gent­ly cal­cu­lates the dif­fer­ence, and push­es only those small changes into the real DOM that is dis­played in the web brows­er. This is how the {{ menuOpen }} state­ment can dis­play some­thing on the webpage.

We want to use this func­tion­al­i­ty to out­put our spe­cial mes­sage that’s based on the check­box­es the user has select­ed. Aha, but wait! That’s what Twig uses as delim­iters, too! We have one too many mus­tach­es here…

Mustache

So does this mean that we can’t use Vue with Craft CMS? Of course not, oth­er­wise it’d make the title of this arti­cle pret­ty spe­cious. As it turns out, all we have to do is tell Vue we want it to use dif­fer­ent delim­iters, like this:

new Vue({
    el: '#nav-menu',
    delimiters: ['${', '}'],
    data: {
        menuOpen: false,
    },
    methods: {
        toggle: function() {
            this.menuOpen = !this.menuOpen;
        }
    },
});

And then we can out­put things via Vue like this ${ menuOpen } and we can mix and match it with our nor­mal Twig {{ }} syn­tax. When a fron­tend request hap­pens, Craft CMS fields the request, then uses Twig to ren­der the tem­plate, and final­ly Vue is instan­ti­at­ed in the brows­er (along with all of your oth­er JavaScript), and does its fron­tend rendering.

Anoth­er way you could do it is use the Twig {% verbatim %} block tag, which caus­es Twig to not process any­thing with­in it. Just wrap your Vue­JS code in {% verbatim %} {% endverbatim %} and away you go.

I told you I wasn’t going to teach you Vue­JS, and I’m going to stick to that. Plen­ty of peo­ple with far more exper­tise than me have cre­at­ed some won­der­ful resources for learn­ing Vue. But I will show you the code for the check­box­es on the web­site. Here’s the HTML markup:

<div class="checkbox-wrapper">
    <input :value="0" v-model="picked" type="checkbox" id="fast-checkbox"  />
    <label for="fast-checkbox" class="check-box"></label>
    <h1 @click="select(0)" class="siteSlogan">Fast.</h1>
</div>
<div class="checkbox-wrapper">
    <input :value="1" v-model="picked" type="checkbox" id="cheap-checkbox" />
    <label for="cheap-checkbox" class="check-box"></label>
    <h1 @click="select(1)" class="siteSlogan">Cheap.</h1>
</div>
<div class="checkbox-wrapper">
    <input :value="2" v-model="picked" type="checkbox" id="quality-checkbox" />
    <label for="quality-checkbox" class="check-box"></label>
    <h1 @click="select(2)" class="siteSlogan">Quality.</h1>
</div>
<h4 class="siteSlogan"><br /></h4>
<h4 id="slogan-subtitle" class="siteSlogan">&nbsp;<span v-cloak>${ message }</span>&nbsp;</h4>

And here is the Vue JavaScript code to imple­ment them:

new Vue({
    el: '#checkboxes',
    delimiters: ['${', '}'],
  data: {
    picked: [],
  },
  methods: {
    select(id) {
     if ( this.picked.indexOf(id) == -1 ) {
        this.picked.push(id)
     } else {
        this.picked.splice(this.picked.indexOf(id), 1)
     }
    }
  },
  watch: {
    picked(value) {
      if (value.length > 2) {
        this.picked.shift()
      }

    }
  },
  computed: {
    message() {
        if ( this.picked.length < 2 ) {
            return '(check what you need)'
        } else if ( this.picked.indexOf(0) === -1 ) {
            return '(cheap & quality will take a long time)'
        } else if ( this.picked.indexOf(1) === -1 ) {
            return '(fast & quality costs money)'
        } else if ( this.picked.indexOf(2) === -1 ) {
            return '(fast & cheap can never be good)'
        }
    }
  }
});

I’m sure there are even more effi­cient ways to accom­plish this; I’m still learn­ing Vue myself! I’d love to hear any feed­back on how to make it bet­ter or more concise.

Link Give Vue a View!

Learn­ing Vue­JS is not hard, and it’s some­thing that could very much improve the projects you’re work­ing on right now. And it works great with Craft CMS & Twig, so there’s no excuse!

I also think that you will very like­ly be hear­ing a lot more about Vue in the future. It’s real­ly a hid­den jewel.

We haven’t even got­ten into the real­ly cool stuff like Vue­JS serv­er-side ren­der­ing, Vue com­po­nents, and Vue’s fan­tas­tic per­for­mance. But we have to leave some­thing for future articles…