Essential Guide 9: Custom Vue.js components

Demo: Matestack Demo Github Repo: Matestack Demo Application

Welcome to the ninth part of our tutorial about building a web application with matestack.

Introduction

In a previous guide, we introduced custom components. In this one, we're bringing it to the next level with custom Vue.js components!

In this guide, we will

  • add a custom Vue.js component that fetches and displays data from a third-party API

Prerequisites

We expect you to have successfully finished the previous guide.

Fetching and displaying data from a third party API

We want to display an option for what you could do with the person at its show page. Therefore we want to fetch a json api and display content of the responses on the page.

We can achieve this by creating a custom Vue.js component. A custom Vue.js component is different from a custom component in the way that it needs a corresponding JavaScript file, which implements the frontend counterpart of our custom Vue.js component.

Okay, let's create our custom Vue.js component. First we create a component in app/matestack/components/persons/activity.rb.

class Components::Persons::Activity < Matestack::Ui::VueJsComponent
  vue_js_component_name 'person-activity'

  def response
    div do
      paragraph do
        plain 'Need ideas on what to do with this person?'
        button text: 'Click here', attributes: {"@click": "addActivity()"}
      end
      ul attributes: {"v-if": "activities.length"} do
        li attributes: {"v-for": "activity,index in activities"} do
          plain "{{activity}}"
          button attributes: {"@click": "deleteActivity(index)"}, text: 'Remove'
        end
      end
    end
  end

end

Our Vue.js component renders a div containing a paragraph and a list. The paragraph contains some text and a button. We set a v-on:click event handler like you would normally do in Vue.js with its shorter version @click. This means the button will call the addActivity method from its corresponding Vue.js component. In the list below we assume that our JavaScript Vue.js component has an activities array. We loop over it, again using Vue.js directives and display each activity with a button labelled 'Remove' which calls deleteActivity(index) if clicked.

Like stated above, a Matestack::Ui::VueJsComponent requires a JavaScript counterpart. Inside we define a Vue.js component and give it a name. The name needs to equal the name we defined in our ruby component at the top with vue_js_component_name. This settings is necessary in order to connect the JavaScript component with our ruby component. Let's create it aside our ruby component in app/matestack/components/persons/activity.js

MatestackUiCore.Vue.component('person-activity', {
  mixins: [MatestackUiCore.componentMixin],
  data() {
    return {
      url: 'https://www.boredapi.com/api/activity/?participants=2',
      activities: []
    };
  },
  methods: {
    addActivity: function(){
      fetch(this.url, {
        headers: [
          ["Content-Type", "application/json"],
          ["Content-Type", "text/plain"]
        ]
      })
      .then(response => response.json())
      .then(data => this.activities.push(data.activity));
    },
    deleteActivity: function(position){
      this.activities.splice(position, 1);
    }
  }
});

As you can see, we're making use of The Bored API to fill an (initially empty) array with strings. The addActivity method calls the api and pushes the activity from the response into the activities array. Because Vue.js is reactive, our list specified in the ruby component will be updated and contain the added activity. Clicking the delete button will remove the activity and therefore remove the entry from the list.

After creating the new component, we still need to register it, both in the /app/matestack/components/registry.rb

module Components::Registry

  Matestack::Ui::Core::Component::Registry.register_components(
    person_teaser: Components::Persons::Teaser,
    person_disclaimer: Components::Persons::Disclaimer,
    person_activity: Components::Persons::Activity
  )

end

and, since it's a JavaScript driven component, we also need to add the JavaScript file to our app/javascript/packs/application.js like this

// ...
import '../../matestack/components/person/activity'

Finally, we can use it in our show page!

class Demo::Pages::Persons::Show < Matestack::Ui::Page

  def response
    transition path: persons_path, text: 'All Persons'
    heading size: 2, text: "Name: #{@person.first_name} #{@person.last_name}"
    paragraph text: "Role: #{@person.role}"

    onclick emit: 'show_more' do
      button text: 'Show more'
    end

    toggle show_on: 'show_more' do
      paragraph text: "Created at: #{I18n.l(@person.created_at)}"
      paragraph text: "Created at: #{I18n.l(@person.updated_at)}"
    end

    person_activity

    transition path: :edit_person_path, params: { id: @person.id }, text: 'Edit'
    action delete_person_config do
      button text: 'Delete person'
    end
  end

  #...

end

Spin up your app and head to the Show page to play around - fetching and deleting activities should work flawlessly! Hint: The way we have set it up for now, everything in this component happens on the client side and refreshing the page resets your activities to an empty array. If you use page transitions to swap to the index page and then back to the user, the data should still be there.

Again, don't forget to save your progress to Git. In the repo root, run

git add . && git commit -m "Add activity dynamic component to display activities from The Bored API"

More information on custom Vue.js components

The existing components inside matestack aim to cover as many use cases as possible, abstracting away most of the JavaScript for generic, recurring use cases. This way, you can stick to Ruby logic and focus on building business logic instead of re-implementing basic JavaScript functionality. There sometimes is, however, a valid case for specific, handcrafted client side functionality. Therefore, matestack offers the option of creating custom Vue.js components. Go ahead and check the Vue.js guides if you have never used it before.

As with custom components, there are hardly any limits for your creativity. You can also

  • use haml (and in the future slim and erb) for templating

  • use existing Vue.js mixins and JavaScript libraries (guides coming soon)

  • use custom dynamic components to fetch, display, modify and update records from your database

  • tweak and extend core components according to your situational, specific needs

To learn more, check out the basic building blocks for custom custom components.

Recap & outlook

In this guide, we introduced custom Vue.js components - an option for you to bring more complex JavaScript functionality to your application by extending matestacks components with your own Vue.js components.

Let's go ahead and learn how to best add styling and notifications to our application in the next part of the series!

Last updated