Getting started with Vue.js development

Make sure you have the latest version of Node.js LTS installed.

Start the Django portal (python manage.py runserver). Navigate to the Django app directory and run npm install and then npm run serve to start up the dev server. Now you can load the Django app in your browser and as you make code changes they should automatically be hot-reloaded in the browser. For example, if you wanted to work on the workspace app's frontend code, you could do

cd django_airavata/apps/workspace
npm install
npm run serve

Then in your browser go to http://localhost:8000/workspace/dashboard.

Note: after stopping the dev server the portal will still keep trying to load the app's JS and CSS from the dev server URLs, which it will fail to do. To go back to a pre dev server state run:

npm run build

Development

Adding a dependency

If you need to add a JavaScript dependency, run the following:

npm install --save <name of dependency>

This automatically updates the package.json file with the added dependency.

Adding an entry point

Create an entry point for the Vue app in a new javascript file. The naming convention is to name the entry point entry-<name of entry point>.js. For example, entry-something-list.js. The entry point shouldn't require compiling a Vue template since we don't include the template compiler in the runtime. The entry point will generally have the following structure:

import { components, entry } from "django-airavata-common-ui";
import SomethingListContainer from "./containers/SomethingListContainer.vue";

entry(Vue => {
  new Vue({
    render: h =>
      h(components.MainLayout, [h(SomethingListContainer)])
  }).$mount("#something-list");
});

If you need to pass data into the Vue app, see below.

vue-cli calls entry points "pages". Edit vue.config.js and add an entry to the "pages" config. For example, to add an entry point with the key "something-list" and that is defined in the file "static/django_airavata_myapp/js/entry-something-list.js", you would add:

pages: {
  // ...
  "something-list": "static/django_airavata_myapp/js/entry-something-list.js"
}

Now you need a template that will load the entry point. For the simple case you can just use the base.html template and pass in the bundle_name which should equal the page key that you entered in vue.config.js. So in views.py, add the following view function:

@login_required
def something_list(request):
    # request.active_nav_item = ... # update this as appropriate
    return render(request, 'django_airavata_myapp/base.html', {
        'bundle_name': 'something-list'
    })

Passing data through template to the Vue.js app

If you need to pass data from the backend to the frontend Vue.js app, you need to make that data available to the Django template and then pass it to the Vue.js app via a data attribute. For example, let's say we have a something-view and we need to pass something-id to the Vue.js app, we could do the following:

First, define a URL pattern that allows passing the id in urls.py:

url(r'^something/(?P<something_id>\w+)/$', views.view_something,
    name='view_something'),

Then define the view function in views.py:

@login_required
def view_something(request, something_id):
    # request.active_nav_item = ... # update this as appropriate
    return render(request, 'django_airavata_myapp/view_something.html', {
        'bundle_name': 'view-something',
        'something_id': something_id
    })

Then create a template that passes the something_id as a data attribute. We'll name the template view_something.html which will extend the local base.html template:

{% extends './base.html' %}
{% block content %}
<div id="{{ bundle_name }}" data-something-id="{{ something_id }}"></div>
{% endblock content %}

In the entry point, load the data attribute in the mounted() hook and pass to the Vue.js app container via a property:

import { components, entry } from "django-airavata-common-ui";
import ViewSomethingContainer from "./containers/ViewSomethingContainer.vue";

entry(Vue => {
  new Vue({
    render(h) {
      return h(components.MainLayout, [
        h(ViewSomethingContainer, {
          props: {
            somethingId: this.somethingId
          }
        })
      ]);
    },
    data() {
      return {
        somethingId: null
      };
    },
    beforeMount() {
      this.somethingId = this.$el.dataset.somethingId;
    }
  }).$mount("#view-something");
});