Drupal & Vue.js: how to work without jQuery

Starting with the 5th version, Drupal has jQuery out of the box. It was an awesome tool for developers. But nowadays we are facing completely different problems that this library can not solve.

Why do I suggest you paying attention to Vue.js? I believe it is not another framework. First, it has a huge number of stars and downloads; a lot of front-end developers have a vivid interest in this project so you are not alone and these people can help you. Secondly, Vue.js has a large and friendly community. Another reason to get to know Vue.js is Laravel. Yes, you are right, this PHP framework supports Vue.js out of the box.

If these reasons don’t persuade you to learn and use Vue.js, please read this article. I will show:

  1. How you can enhance UI without jQuery;
  2. How to work with Vue.js components;
  3. How to integrate Vue.js with third-party libraries;
  4. How to create a simple SPA.

Drupal & Vue.js how to work without jQuery1

Getting Started

Let’s get started by describing the possibilities of how we can use this framework in our projects. The easiest way is including the <script> tag.

<script src="https://unpkg.com/vue"></script>

Also, you can try out npm package or the Drupal module. The Decoupled module still has an issue where users ask to support Vue.js.

Enhance UI

Unfortunately, Drupal doesn’t allow us to develop a modern reactive user interface. It has jQuery in the core and this library can’t build it. I suggest trying Vue.js to enhance some Drupal user elements.

For example, we want to show the "Create new account"  button only if “Email” and “Username” fields are not empty.

<form enctype="multipart/form-data" action="/user/register" method="post" id="user-register-form" accept-charset="UTF-8">

<div id="edit-account" class="form-wrapper">

 <div class="form-item form-type-textfield form-item-name">

   <label for="edit-name">Username <span class="form-required" title="This field is required.">*</span></label>

   <input class="username form-text required" autocapitalize="none" type="text" id="edit-name" name="name" value="" size="60" maxlength="60" v-model="name">

 </div>

<div class="form-item form-type-textfield form-item-mail">

 <label for="edit-mail">Email <span class="form-required" title="This field is required.">*</span></label>

<input type="text" id="edit-mail" name="mail" value="" size="60" maxlength="254" class="form-text required" v-model="mail">

</div>

<div class="form-actions form-wrapper" id="edit-actions"><input type="submit" id="edit-submit"
name="op" value="Create new account" class="form-submit" v-show="name && mail"></div>

</div>

</form>
var app = new Vue({
 el: '#user-register-form',
 data: {
   name: '',
   mail: '',
 }
})

Vue.js components

Everyone knows that a typical developer doesn’t reinvent a wheel, but use ready-made solutions. The Vue.js community can offer a lot of awesome components. Let’s try to use one of them.

I believe that user input data should be validated as on the back-end as on the front-end side. So you have to add

<script src=”https://cdn.jsdelivr.net/vee-validate/2.0.0-beta.25/vee-validate.js”></script>

“vee-validate is a lightweight plugin for Vue.js that allows you to validate input fields, and display errors.” (c)

Also, please change input elements:

<input class="username form-text required" autocapitalize="none" type="text" id="edit-name" name="name" value="" size="60" maxlength="60" v-model="name" v-validate.initial="name" data-rules="required|alpha|min:3">

<input type="text" id="edit-mail" name="mail" value="" size="60" maxlength="254" class="form-text required" v-model="mail" v-validate.initial="email" data-rules="required|email">

As you can see, using Vue.js and its components to enhance existing elements is very easy. And I have not faced with any reasons not to do it in your projects right now. However, I think you have a question how to use this framework with other libraries. OK, let’s continue.

Vue.js and other libraries

In this example, we are integrating a 3rd party jQuery plugin (select2) by wrapping it inside a custom component.

<div id="el"></div>

<!-- using string template here to work around HTML <option> placement restriction -->
<script type="text/x-template" id="demo-template">
  <div>
    <p>Selected: {{ selected }}</p>
    <select2 :options="options" v-model="selected">
      <option disabled value="0">Select one</option>
    </select2>
  </div>
</script>

<script type="text/x-template" id="select2-template">
  <select>
    <slot></slot>
  </select>
</script>
Vue.component('select2', {
  props: ['options', 'value'],
  template: '#select2-template',
  mounted: function () {
    var vm = this
    $(this.$el)
      // init select2
      .select2({ data: this.options })
      .val(this.value)
      .trigger('change')
      // emit event on change.
      .on('change', function () {
        vm.$emit('input', this.value)
      })
  },
  watch: {
    value: function (value) {
      // update value
      $(this.$el).val(value).trigger('change');
    },
    options: function (options) {
      // update options
      $(this.$el).select2({ data: options })
    }
  },
  destroyed: function () {
    $(this.$el).off().select2('destroy')
  }
})

var vm = new Vue({
  el: '#el',
  template: '#demo-template',
  data: {
    selected: 2,
    options: [
      { id: 1, text: 'Hello' },
      { id: 2, text: 'World' }
    ]
  }
});

Vue.js doesn’t behave as if it is alone on the site (yes, I’m talking about React ;) ). You will be able to use other js components and allow them to work with Virtual DOM.

Single Page Application

As usual, the most interesting subject will be discussed at the end. You can find all things to build SPA in vue-router 2. Just include this:

<script src=”https://unpkg.com/vue-router/dist/vue-router.js”></script> 

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- use router-link component for navigation. -->
    <!-- specify the link by passing the `to` prop. -->
    <!-- <router-link> will be rendered as an `<a>` tag by default -->
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <!-- route outlet -->
  <!-- component matched by the route will render here -->
  <router-view></router-view>
</div>
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// Each route should map to a component. The "component" can
// either be an actual component constructor created via
// Vue.extend(), or just a component options object.
// We'll talk about nested routes later.
const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

// You can pass in additional options here, but let's
// keep it simple for now.
const router = new VueRouter({
  routes // short for routes: routes
})

// Make sure to inject the router with the router option to make the
// whole app router-aware.
const app = new Vue({
  router
}).$mount('#app')

Final words

We have gone through all steps together: from the simplest example to SPA. I showed you that Vue.js is very simple and it is useful for any projects. I hope after reading this article you will have a curiosity to practice what you've just read. Please have a look at links below. They can also motivate you and help with some problems. Have a nice trip in this beautiful JS world!

Useful links

  1. Vue.js
  2. Building a Single Page Application with Vue.js [Step-by-step guide]
  3. npm package
  4. The Vue.js module: a bridge between Drupal and Vue.js
  5. The Decoupled Blocks module
  6. "Why we chose Vue.js" by Jacob Schatz
  7.  An example of using VueJS with a contact form 
  8. "A Vue.js introduction for people who know just enough jQuery to get by" by Matt Rothenberg

You might also like