Vue.js and Drupal

Karl Kedrovsky
VML
karl.kedrovsky@vml.com
karl@kedrovsky.com
@karlkedrovsky

Overview

  • Why Vue.js
  • Overview of Vue.js
  • Drupal Integration

Thanks Ryan!

What is Vue.js?

The goal of Vue.js is to provide the benefits of reactive data binding and composable view components with an API that is as simple as possible.

Why Vue.js

  • Stands on the shoulders of giants
  • Shallow learning curve
  • You can ignore the internals
  • Component model
  • Awesome community and adoption within PHP community

The Goal

Vue.js

Data Binding

Data Binding


Message:

Message:
{{ message }}

Data Binding


new Vue({
  el: '#app',
  data: {
    message: ''
  }
});
            

Data Binding

Demo

Methods


Count: {{ count }}

Methods


new Vue({
  el: '#app',
  data: {
    count: 0
  },
  methods: {
    add: function() {
      this.count++;
    }
  }
});
            

Methods

Demo

Components

  • Extends HTML (new tags)
  • Encapsulates reusable code

Components


Components


var Foo = Vue.extend({
  template: '

The Foo Component

' }); Vue.component('foo', Foo); var Bar = Vue.extend({ template: '

The Bar Component

' }); Vue.component('bar', Bar); new Vue({ el: '#app' });

Components

Demo

Child Components


Child Components


var SpawnOfFoo = Vue.extend({
  template: '

The Spawn Of Foo Component

' }); var Foo = Vue.extend({ template: '

The Foo Component

', components: { 'spawn-of-foo': SpawnOfFoo } }); Vue.component('foo', Foo); var Bar = Vue.extend({ template: '

The Bar Component

' }); Vue.component('bar', Bar); new Vue({ el: '#app' });

Child Components

Demo

Single File Components

  • Groups template, scripts, and styles in a single file
  • Requires a pre-processor (typically Webpack)
  • Templates can be HTML or other language like Jade
  • Scripts can be Javascript, CoffeeScript, etc.
  • Styles can be CSS, Sass, etc.

Single File Components


Single File Components





            

Single File Components


import Vue from 'vue';

import Demo from './components/demo.vue';

new Vue({
  el: '#app',
  components: {
    Demo
  }
});
            

Single File Components


gulp.task('webpack', function() {
  return gulp.src('js_sfc/sfc.js')
    .pipe(webpack({
      module: {
        loaders: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /\.vue$/,
            loader: 'vue'
          }
        ]
      },
      babel: {
        presets: ['es2015'],
        plugins: ['transform-runtime']
      },
      resolve: {
        modulesDirectories: ['node_modules']
      },
      output: {
        filename: 'sfc.js'
      }
    }))
    .pipe(gulp.dest('web/js/'));
});
            

Single File Components

Demo

Drupal Integration

  • Creating custom component blocks and content items
  • Using Drupal 8's built in REST API support

Reminder - The Goal

Adding Components To A Drupal Site

  • Custom block type
  • Custom content type
  • Add Vue.js to your theme

Custom Block Type

Custom Content Type

Theme Files - Field


{# field--field-component-name.html.twig #}
{% for item in items %}
  <{{ item.content }}></{{ item.content }}>
{% endfor %}
            

Theme Setup

vue.info.yml


name: Vue.js and Drupal
type: theme
description: Demo theme for Vue.js and Drupal
base theme: bartik
core: 8.x
libraries:
  - vue/main
            

Theme Setup

vue.libraries.yml


main:
  version: 1.x
  js:
    js/vue.js: {}
    js/main.js: {}
            

Theme Setup

gulpfile.js


gulp.task('webpack', function() {
  return gulp.src('src/main.js')
    .pipe(webpack({
      module: {
        loaders: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel'
          },
          {
            test: /\.vue$/,
            loader: 'vue'
          }
        ]
      },
      babel: {
        presets: ['es2015'],
        plugins: ['transform-runtime']
      },
      resolve: {
        modulesDirectories: ['node_modules']
      },
      output: {
        filename: 'main.js'
      }
    }))
    .pipe(gulp.dest('js/'));
});
            

Directory Structure For Our JS

src
|-- components
|   |-- app.vue
|   |-- cat_counter.vue
|   |-- cats.vue
|   |-- cat.vue
|   |-- demo.vue
|-- main.js
            

Adding Components

demo.vue




            

Adding Components

main.js


import Vue from 'vue';

import Demo from './components/demo.vue';

new Vue({
  el: '#page',
  components: {
    Demo
  }
});
            

Adding Components

Demo

Events and Resource

Events

  • Notifications and data between components
  • Child components pass events up to parents
  • Parents pass events down to children

Resources

  • Vue plugin to handle web requests

Integrating REST Services

  • Use Vue Resource library for getting data from Drupal
  • Events and methods can be used for updating data

Integrating REST Services

The Cat Content Type

Integrating REST Services

A Cat Page - cat.vue




            

Integrating REST Services

A Cat Page - main.js


import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

import Demo from './components/demo.vue';
import Cat from './components/cat.vue';

new Vue({
  el: '#page',
  components: {
    Demo,
    Cat
  }
});
            

Integrating REST Services

Demo

Integrating REST Services

A List Of Cats

Integrating REST Services

The Cat List API View

Integrating REST Services

The Cat List - cats.vue





            

Events

Integrating REST Services

The Cat List - main.js


import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

import Demo from './components/demo.vue';
import Cat from './components/cat.vue';
import Cats from './components/cats.vue';

new Vue({
  el: '#page',
  components: {
    Demo,
    Cat,
    Cats
  },
  methods: {
    ready() {
      this.$on('new-cat', function(cat) {
        this.$broadcast('new-cat', cat);
      }, (response) => {
        console.log('Error');
      });
    }
  }
});
            

Integrating REST Services

Demo

Adding The Counter

Another Event Listener

Cat Counter - cat_counter.vue





            

Cat Counter - main.js


import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

import Demo from './components/demo.vue';
import Cat from './components/cat.vue';
import Cats from './components/cats.vue';
import CatCounter from './components/cat_counter.vue';

new Vue({
  el: '#page',
  components: {
    Demo,
    Cat,
    Cats,
    CatCounter
  },
  methods: {
    ready() {
      this.$on('new-cat', function(cat) {
        this.$broadcast('new-cat', cat);
      }, (response) => {
        console.log('Error');
      });
    }
  }
});
            

Cat Counter

Demo

Tips

  • Prefer Vue functionality over jQuery, etc.
  • Use Vue Resource for "ajax" integration
  • Use Vue Router for Single Page Apps
  • Use Vuex for complicated state management
  • Learn Javascript (es6/es2015)
  • Check out Laravel Elixer

Resources

Thank You!