Menu

Using a Vue.js app in a D8 Paragraph

I have had been given the task to pull data from a REST API into a Drupal 8 Paragraph, to be rendered on any specific page. The API would provide the data for a profile, showing in a listing, and a full bio. 

I wanted to keep this as simple as possible, and at the same time, make it cool. 

For this task, I decided to use Vue.js inside a template. I felt that this would give me a way to work with Vue in a way that is not as common - inside a Drupal 8 template. 

I created a Paragraph type called a Profile Listing. In that Paragraph, I added some fields that I would be using to control the API call. In this case, a related taxonomy, limit, and offset, and a few other fields.

The Template

Using a template I created for that Paragraph, I did some markup tricks to get twig to render correctly. Notice the {' '} wrapping the {{ profile.firstname }}.

{# string wrapper is to prevent Twig processing the Vue vars #}
<div id="App">
  <div v-for="profile in profiles" class="card">
    <h2 class="name">{{ '{{ profile.firstName }} {{ profile.lastName }}' }}</h2>
  </div>
</div>

This allows the HTML to not be filtered in the template. This does make it so that ticks ` have to be used instead of single quotes inside that template.

You can also write it this way:

<div id="App">
  <div v-for="profile in profiles" class="card">
    <h2 class="name"><span v-html="profile.firstName"></span> <span v-html="profile.lastName"></span></h2>
  </div>
</div>

Next I pulled the CDN versions of Vue into the template, as well as Axios, a data management plugin.

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

Then inside a following script tag, I write the app. Since the app is inside the Paragraph, all field values can be used in the markup, allowing you to filter based on the values. This is a very shortened and modified version, but you get the picture.

<script> const myApp = new Vue({
  el: '#App',
  data () {
    return {
      profiles: [],
      loaded: false
      }
    },
  methods: {
    async getProfiles(id) {
      const = 'https://example.com/api/';
      let endpoint = 'profiles' 

Notice the following API call, which includes the field values to filter the result.

  try {
    const resp = await axios.get(url + endpoint + '?args[0]=
      {% if content.field_setting|field_value %}
        {{ content.field_setting|field_value }}  
      {% else %}all{% endif %}
      &limit={{ content.field_limit|field_value }}  
      &offset={{ content.field_offset|field_value }}');

      this.profiles = resp.data;
      this.loaded = true;
     }
     catch (err) {
        console.log(err)
      }
     },
   },
   mounted() {
    profileParams = 'all';
    this.getProfiles(profileParams);
  }
});
</script>

The result is pretty awesome. Now anytime that paragraph is used, the app loads the Vue app, with the REST data.

 

portrait of