How to integrate a Vue.js app in django CMS

I want to display data from another Django project that uses another database than the django CMS project, but is on the same VM. The approach I want to try now is to integrate a Vue.js SPA. It took some time, but finally I managed to create an app with vue-cli, using Apollo for connecting to the GraphQL API of the database server which uses graphene-django.

vue-cli uses Webpack, and so I get a bundle of files that work well together when they are used together as webpack creates them: an index.html, a css and js folder with minified files and maybe pictures . So putting them into a static folder and then calling its URL works fine.

But I want to integrate it into the CMS as a web hook or plug-in. Using an iframe is not really an option, then I wouldn’t need the API approach at all. So I need to use index html as a template, but then it doesn’t find the other files, mainly css and js. Using a <base> tag would be an idea, but this would also affect the files that are needed to display the css, js, pictures of the base template.

Do you have an ideas how to tackle this problem or have you already done something similar?

Hi @Jens-Erik

We normally deliver vue applications as simple plugins, it can then be added to any CMS page by the editor. This is simpler than using apphook apps. This is how it looks in the backend:

src/custom_plugins/sample_vue_app/templates/sample_vue_app/plugins/sample-vue-app.html

{% load staticfiles %}

<div class="sample-vue-app"
     data-attribute3="{{ instance.attribute3 }}"
     data-attribute2="{{ instance.attribute2 }}"
     data-attribute1='{
        "utm_source": "{% get_utm_source %}",
        "utm_medium": "{% get_utm_medium %}",
        "utm_campaign": "{% get_utm_campaign %}",
        "utm_content": "{% get_utm_content %}"
     }'
></div>

{% if settings.WEBPACK_DEV_BUNDLE %}
    <script data-cms-reload defer src="{{ settings.WEBPACK_DEV_BUNDLE_BASE_URL }}sample-vue-app.bundle.js"></script>
    <link rel="stylesheet" href="{{ settings.WEBPACK_DEV_BUNDLE_BASE_URL }}sample-vue-app.css">
{% else %}
    <script data-cms-reload defer src="{% static 'dist/sample-vue-app.js' %}"></script>
    <link rel="stylesheet" href="{% static 'dist/sample-vue-app.css' %}">
{% endif %}



Here are some hints about the frontend setup based on the webpack frontend as per https://github.com/django-cms/djangocms-template/tree/master/frontend).

You will have to add vue-loader to your existing webpack config as such: https://vue-loader.vuejs.org/guide/#vue-cli

Here is a sample vuejs application, it would be inside frontend/sample-vue-app/ (i.e. index.js, app.vue, app.scss, app.html and app.js)

It has to be added to webpack.config.js as an entrypoint:

webpack.config.js

...
 entry: {
        "global": './frontend/global/index.js',
        "vendor": './frontend/vendor/index.js',
        "sample-vue-app": './frontend/sample-vue-app/index.js',
...

index.js

import Vue from 'vue';
// import store from './helpers/store'
import App from './app.vue'

// create a constructor for your widget
let Widget = Vue.extend({
    render(h) {
        return h(App, {
            props: { // load stuff from data attributes in the django template
                attribute1: JSON.parse(this.$el.getAttribute('data-attribute1')),
                attribute2: this.$el.getAttribute('data-attribute2'),
                attribute3: parseFloat(this.$el.getAttribute('data-attribute3').replace(',', '.')),
            }
        })
    },
});

// mount the widget to any occurences
let nodes = document.querySelectorAll('.class-from-django-template'); // unique selector!
for (let i = 0; i < nodes.length; ++i) {
    new Widget({store, el: nodes[i]})
}


app.vue

<template src='./app.html'></template>
<script src="./app.js"></script>
<style lang="scss" scoped src="./app.scss"></style>

app.js

// ....

export default {
    props: [
        "attribute1",
        "attribute2", 
       "attribute3"
   ]
/// ...
}