vsupalov

Vue.js In A Django Template

December 02, 2018

What to do when you’re ready to level up the interactivity of your Django app, without having to fiddle around with jQuery? Do you have to build a full-fledged SPA, delete all your Django templates and reimplement everything with Django Rest Framework?

You don’t have to give up everything you know and love from your Django projects. In fact, you can get the interactivity and convenience of Vue.js, without all the overhead of a SPA setup.

Here’s a step-by step example, how you can switch from vanilla Django templates without JavaScript (or jQuery), towards adding progressive enhancements with Vue.

The Beginning

Let’s focus on the parts which are relevant to adding Vue.js into a Django project. We start with a basic Django view, and a corresponding template.

view.py:

import random

from django.shortcuts import render

def index(request):
    names = ("bob", "dan", "jack", "lizzy", "susan")

    items = []
    for i in range(100):
        items.append({
            "name": random.choice(names),
            "age": random.randint(20,80),
            "url": "https://example.com",
        })

    context = {}
    context["items"] = items

    return render(request, 'list.html', context)

The view is very simple. It generates some dummy data - 100 dict entries are added to a list, and the list is passed on inside a context dictionary to the template. The name and age always vary due to some randomness.

list.html:

{% if items %}
  <ul>
    {% for item in items %}
    <li>
      <a href="{{item.url}}">{{ item.name }}</a> <button>Hey!</button>
    </li>
    {% endfor %}
  </ul>
{% else %}
  <p>No items available.</p>
{% endif %}

Inside the list.html template, each item from the items list get rendered inside a for loop. Nothing fancy. There’s a button, but it’s not doing anything.

Getting dynamic with Vue

How can we go from a basic template, to using Vue.js?

There’s a few steps we have to take, in the most simple case:

  • Add the data into the template in JSON format.
  • Add the Vue.js library into the template (like you would jQuery).
  • Configure Vue to not interfere with the Django template syntax (both use double-curly-brackets as delimiters).
  • Add Vue.js markup which does what we want.

First, render out the data

We don’t need to add a new endpoint to Django, or anything fancy. The most simple approach will require only a simple modification to our view.py file: we’ll need to import json and save the items list as a rendered JSON string into the context dictionary: ‚

import json

# ...
    context = {}
    context["items_json"] = json.dumps(items)
# ...

That’s it!

Sidenote: Will this work when using Class Based Views?

Yes, absolutely. As long as you can pass data to your template, you’ll be fine with either FBV or CBV. If you’re unsure about which style of views will be best for you, take a look at this article.

The template - a complete picture

The next change happens inside the template. As the template is going to change quite a bit, I created a new one, and called it vue_list.html (don’t forget to make your view use the new one!):

Here’s the complete template, so you can see everything at once before diving into details. We’re going to go through all relevant parts of it step-by-step later on, don’t worry!

vue_list.html:

{% if items %}
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script type='text/javascript'>
    var people = {{ items_json|safe }};
  </script>

  <div id="app">
    [[message]]

    <ul>
      <li v-for="person in people">
        <a v-bind:href="person.url">[[ person.name ]]</a> <button v-on:click="greet(person.name)">hey</button>
      </li>
    </ul>
  </div>

  <script>
    var app = new Vue({
      delimiters: ['[[', ']]'],
      el: '#app',
      data: {
          message: 'Hello Vue!',
          people: people,
      },
      methods: {
          greet: function(name) {
              console.log('Hello from ' + name + '!')
          }
      }
    });
  </script>
{% else %}
  <p>No items available.</p>
{% endif %}

Once again, there’s a lot happening here. Let’s go through it step by step.

Add the Vue.js library

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

In the above line, we load Vue.js from a CDN. That’s all you have to do. That’s no more effort, than including jQuery into your template.

Render our JSON data as JavaScript

  <script type='text/javascript'>
    var people = {{ items_json|safe }};
  </script>

Here, we render out the items_json string without modifying it. This way, we get a JavaScript variable people which is being set to the content of items_json in a usable form.

The safe filter prevents escaping of html-endangering characters, which the Django template would otherwise do by default. This way, the JSON string is considered safe, and will be put into the template 1:1. Here’s what it will approximately look like in the rendered page:

  <script type='text/javascript'>
    var people = [{"name": "bob", "age": 32, "url": "https://example.com"}, (...)]
  </script>

The (...) is where I got lazy typing any more. Just imagine 99 more entries like the first one in a single line.

Add the HTML element which Vue will modify

  <div id="app">
    (...)
  </div>

This is where vue will make changes. Inside it, we will specify all of our vue templating. I left out the content in this case, and replaced it with (...), as it doesn’t matter for now.

Configure Vue

The complete configuration of Vue happens in this script block:

  <script>
    var app = new Vue({
      delimiters: ['[[', ']]'],
      el: '#app',
      data: {
          message: 'Hello Vue!',
          people: people,
      },
      methods: {
          greet: function(name) {
              console.log('Hello from ' + name + '!')
          }
      }
    });
  </script>

This is where it all comes together.

The most important part, are the delimiters. Vue normally uses the same syntax as Django templates to denote expressions to be replaced: two curly brackets.

We change those to double-square-brakcets, so Django templating leaves our Vue template code alone. This happens in the line:

      delimiters: ['[[', ']]'],

Rember the people variable we set in the very first script tag? We are going to pass it on into Vue. In addition, we also pass a message - a plain string, as in the basic Vue tutorial.

      data: {
          message: 'Hello Vue!',
          people: people,
      },

The methods entry, defines a function which will be easily callable from inside of our Vue app. Let’s look at the Vue template to see where it’s invoked.

Time for Vue templating

The last part, happens inside the div which we chose as our app container. Once the page is loaded, and Vue comes to life, it will go into that element and replace template markup with actual values.

As you are familiar with the Django templating language, you won’t have a hard time getting started and understanding what happens here:

    [[message]]

    <ul>
      <li v-for="person in people">
        <a v-bind:href="person.url">[[ person.name ]]</a> <button v-on:click="greet(person.name)">hey</button>
      </li>
    </ul>

We render out the value of the message data entry, which we added to the Vue config.

We use v-for to iterate over each entry in the people array, and add a new a element for each. The loop content is limited to the content of the li element.

Inside of the loop, we set the href attribute dynamically, render out the name of the person and add an on-click listener to the button. When the button is clicked, the function defined in the Vue object will be called with the person’s name.

All done!

That’s it already. If the example seems overwhelming, that’s okay. There’s a lot of stuff going on, and I made sure to include everything which you will need to get started.

If the Vue templating seems unclear, or you want to know more about Vue, I can’t recommend the official getting started guide enough. They are very thorough and a pleasure to work with.

In particular, you might want to take a look at:

Join the Mailing List

Subscribe to get weekly updates and my latest articles by email.

    (About the content, privacy, analytics and revocation).

    We won't send you spam. Unsubscribe at any time.

    Powered By ConvertKit