June 16, 2017

VueJS ❤️ Craft CMS - Single Page App: Bootstrap the app

You came back! Yay!

Given how much I've got to cover in this, I'll need to make some assumptions about your own Craft site if you want to follow along. Those assumptions will be:

  1. you have a local instance of a Craft website running
  2. your local Craft instance has a user(s) and some entry data (I'll be basing mine on an existing jobs channel)
  3. you have Node JS & NPM installed on your machine - at time of writing I have Node v8.1.2 and npm 5.0.3
  4. you have some JavaScript experience
  5. you're comfortable using terminal
  6. installed Vue Dev Tools
  7. you'll forgive me any errors and help correct me 😀

Digging in

First things first. Version control. Make sure you're using something. I'll leave the flavour up to you but please, just use something.

Delete all of your Craft templates. Whilst we'll ultimately ignore all but one Craft template, it's easier just to wipe them all for a clean slate. I'll commit those changes to Git, initialise Laravel Mix within Craft (if you already have a package.json file, you can ignore npm init -y) and then create directories for my source JS and Sass.

rm -rf craft/templates/*
git add .
git commit -m "removed all Craft templates"
npm init -y
npm i laravel-mix --save-dev
cp -r node_modules/laravel-mix/setup/** ./
mkdir src && mkdir src/js && mkdir src/sass
touch src/js/main.js && touch src/sass/app.scss

With that, I'm almost ready to start writing some code, I just need to make a change to webpack.mix.js and set my JS entry points and output directories

let mix = require('laravel-mix')

mix.setPublicPath('public')
  .js('src/js/main.js', 'js/')
  .sass('src/sass/app.scss', 'css/')

Next, I want to start configuring Craft. Although I'll be using Vue Router for the routing - I still need to handle how Craft handles its routing.

To do that, I want to edit craft/config/general.php and add 'siteRoutesSource' => 'file' - this will allow me to configure routes from craft/config/routes.php rather than via the control panel. It's also nicer I find to have that versioned vs stored in the database.

Then, I want to route all of my Craft requests through a single template so craft/config/routes.php becomes

<?php

return array(
  (?!api\/).*' => 'index
);

Thanks to Brandon Kelly for helping out on this as it had me in a spin for a while and I'll come back to another piece of this puzzle later in another post.

Templates

I only need 2 templates _layout.twig and index.twig and I'm going to keep mine super simple here; _layout.twig becomes:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Craft App</title>

{% if craft.config.devMode %}
<link rel="stylesheet" href="http://localhost:8080/css/app.css">
{% else %}
<link rel="stylesheet" href="/css/app.css">
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
</head>
<body>
{% block content %}{% endblock %}
{% if craft.config.devMode %}
<script src="http://localhost:8080/js/main.js"></script>
{% else %}
<script src="/js/main.js"></script>
{% endif %}
</body>
</html>

and then index.twig becomes

{% extends '_layouts/_master' %}

{% block content %}
  <div id="app"></div>
{% endblock %}

All things being equal, if you fire up your browser, you'll see nothing. Inspect the source code and you should at least have an empty div <div id="app"></div> ?

That's as much as we'll do with Craft at this point. Now we'll look to start scaffolding our Vue app.

Clearly, a Vue app doesn't work without Vue, so within my terminal npm i vue --save to install the dependency

Then in src/js/main.js

import Vue from 'vue'

new Vue({
  el: '#app'
})

With that, I can run npm run hot and the Webpack dev server will kick in.

All being well, if you refresh the page and pop open the web inspector, you'll see a Vue tab looking a little like this

Vue dev tools inspector in Chrome

I'm almost there for this part, stick with me 😎

I'll create my first Vue component called App.vue and it'll live within src/js alongside main.js

Editing main.js to become

import Vue from 'vue'
import App from './App'

new Vue({
  el: '#app',
  template: '<App />',
  components: { App }
})

I've imported App.vue as a component to the Vue instance and I'll use its template as the wrapper for the rest of the app.

To show something in the browser at this point, App.vue looks something like

<template>
  <div id="app">
    <h1>{! welcomeMessage !}</h1>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      welcomeMessage: 'Hello World'
    }
  }
}
</script>

<style lang="scss">
@import "../../node_modules/bootstrap/scss/bootstrap"
</style>

Now, one very important thing to note if you're copying the code above, my code embed tool doesn't like the Vue braces in template render. So where I have {! welcomeMessage !} inside an <h1> it should use Vue's standard “Mustache” syntax (double curly braces).

With that in place, your app should render a lovely 'Hello World'

One thing I did omit was that I installed Bootstrap npm install bootstrap@4.0.0-alpha.6 --save for styling, imported into my app on line 19.

And that's me, you did it 👍🏻 - you bootstrapped the basis of the Vue app within Craft.

Have a great weekend 🍻

Leave Your Comments