How to create a scheduled component using Vue.js?

Contents

With Vue.js it is unbelievably easy to render dynamic components, which we can utilize to render content with specific components and layouts by only using their name.

We’ve already covered the topic on how to render component dynamically from a JSON, now we will focus on how to only render components if in the correct time frame.

The JSON structure

The content JSON has been enhanced by a new component called schedule which consists of a field start, end and body field. The start and end property in our example are not required and can result in an empty string. Each DateTime string is in GMT+0 just so we get the question about timezones out of our mind for this.

...
  data() {
    return {
      content: {
        body: [
          {
+           _uid: "BU525sfe24",
+           component: "schedule",
+           start: "2019-09-02 01:59",
+           end: "2019-09-14 20:56",
+           body: [
              {
                _uid: "BUY6Drn9e1",
                component: "foo",
                headline: "Foo"
              }
+           ]
          },
          {
            _uid: "gJZoSLkfZV",
            component: "bar",
            title: "Bar"
          },
          {
            _uid: "X1JAfdsZxy",
            component: "foo",
            headline: "Another headline"
          }
        ]
      }
    }
  },
...

Filtering before or during looping through components?

We now have two possibilities on how to receive the components we want to actually how. In this small example filtering the components beforehand will be much easier by using a computed property, however with a nested and more complex content structure that might already be way harder as you no longer loop through one array of objects but a tree content structure instead.

Looping through the component tree

Filtering dates in JavaScript is always a bit tricky, packages like momentjs make things easier. If you’re performing more complex date operations it’s good to know they exist, for this example we’ll use the default Date of JavaScript and add the timezone +0000 before using the date strings. To use a method and the Vue.js template instead of using a computed property we extract the above filter method into a helper method, let us call it isComponentVisible.

methods: {
  isComponentVisible: comp => {
    if (comp.start && comp.end) {
      let start = new Date(`${comp.start}+0000`);
      let end = new Date(`${comp.end}+0000`);
      return start <= new Date() && new Date() <= end;
    }

    if (comp.start) {
      let start = new Date(`${comp.start}+0000`);
      return start <= new Date();
    }

    if (comp.end) {
      let end = new Date(`${comp.end}+0000`);
      return new Date() <= end;
    }
    return true;
  }
},

Since we’re not going to use the computed property we can return to our direct content structure using content.body.

<template v-for="block in content.body">
  <component :is="block.component" :block="block" :key="block._uid"></component>
</template>

The last step to filter our components is to use the isComponentVisible method with the current block object.

<template v-for="block in content.body">
-  <component :is="block.component" :block="block" :key="block._uid"></component>
+  <component v-if="isComponentVisible(block)" :is="block.component" :block="block" :key="block._uid"></component>
</template>

When should I use which approach?

We would recommend opting for the method approach as you can outsource that one method from your component and re-use it everywhere (where component might be used) in your Vue.js app. This way you no longer have the complexity of filtering out the components of the structure before rendering, but decide during rendering what to show.

Keep in mind that even tho the components are not displayed we would recommend to only use this approach with non-sensitive information. Your data is filtered on the client and the original information is still available through the browsers dev tools.

If you want to filter information before rendering on the client you would be able to use a server-side rendered website or a static site generator. Another option would be to go for a cloud function / custom API (Netlify Function / Vercel) to filter your component on a server.

Editing the JSON

Start End Schema in component schedule of Storyblok

Adding a new component called schedule to wrap around your other components will allow you to not have to add fields (start, end) on every component, but only one those that you need. Adding a start and end field to the schedule component can be done by pressing Define Schema in that component and adding two new fields. Select the type Date/Time and you’re ready to go. Next, to allow nesting of components, we will also create a field called body with the field type Blocks. This will allow you to nest your previous components (in our example: foo and bar) in it. Try it out by exchanging the static content with our demo API Call right here.

The Schedule Components code

The schedule component itself uses the same set-up as our page component. If you want to allow schedule components in scheduled components you can again use the same isComponentVisible set-up. Loop over your components in the “body” field and include the components as you go.

<template v-for="scheduledBlock in block.body">
  <component v-if="isComponentVisible(scheduledBlock)" :is="scheduledBlock.component" :block="scheduledBlock" :key="scheduledBlock._uid"></component>
</template>
Dominik Angerer

Author

Dominik Angerer

A web performance specialist and perfectionist. After working for big agencies as a full stack developer he founded Storyblok. He is also an active contributor to the open source community and one of the organizers of Stahlstadt.js.