Using Vue.js

How to write client applications using Vue.js

We will base this guide on Book Collection example - Vue.js, which shows an editable list of books, with their titles and authors. To try out the example, visit the link and follow the instructions.

Before we go into the code on how to use ResClient together with Vue.js, we need to have a look at how ResClient handles state.

State

ResClient stores and manages the state in model and collection objects, which represents the state as it is on the backend. The resource objects are to be considered immutable, and should only be managed and updated by ResClient itself, based on events coming from Resgate.

The basic idea of Resgate and ResClient is to subscribe to resources which is currently relevant to the user. Eg. if a user is browsing a list of company personel, it would be wasteful to store and update the state of the company’s inventory list when it is not in view. Since ResClient keeps track of which resources are being listened to, we can avoid redundant subscriptions by having components explicitly listen and unlisten to resource events, and not only rely on Vue.js’ reactivity system.

Initializing ResClient

To get data from Resgate, we need to create a ResClient instance. This can be done in many places, but in our example this is done inside the App component’s created hook:

created() {
	this.client = new ResClient('ws://localhost:8080');
	/* ... */
}

Getting a resource

We also want to load the resources needed for our components, in this case our list of books, which we store as data. With Vue.js, this can preferably be done in the created hook as well:

data: {
	this.books = null
},
created() {
	this.client = new ResClient('ws://localhost:8080');
	this.client.get('library.books').then(books => {
		this.books = books;
	});
},

The books collection is then passed to the BookList component as props:

<book-list v-if="books" :books="books"></book-list>

Tip

Instead of having the parent component (App) load the resources to pass them to the child components (BookList) one may pass the ResClient instance (client) to the child component instead.

This way, the child component will have the responsibility of loading the data.

Rendering the list

When rendering the list, we use the books collection object just like with any other Vue.js props:

<div class="book-list">
	<book v-for="book in books" :key="book.id" :book="book"></book>
</div>

Note

ResClient collections are iterables, but not arrays. Since the v-for directive supports iterables, Vue.js will have no trouble iterating over the collections.

Listen to events

Once the component is created, we also want to listen to add and remove events. This is preferably done in the created hook. While Vue.js may detect changes inside props, it will have problems reacting to items being added and removed by ResClient. Because of this, we will need to trigger a re-render ourself, using this.$forceUpdate:

created() {
	this.books.on('add', this.onUpdate);
	this.books.on('remove', this.onUpdate);
},
methods: {
	onUpdate() {
		this.$forceUpdate();
	}
}

Unlisten to events

Once the component is about to be destroyed, we need to unregister our callbacks so that ResClient may be free to unsubscribe. This is preferably done in the beforeDestroy hook:

beforeDestroy() {
	this.books.off('add', this.onUpdate);
	this.books.off('remove', this.onUpdate);
},

Tip

To make a component reusable with non-ResClient objects, the registering of listeners can be made conditional:

created() {
	if (typeof this.books.on == "function") {
		this.books.on('add', this.onUpdate);
		this.books.on('remove', this.onUpdate);
	}
},
beforeDestroy() {
	if (typeof this.books.off == "function") {
		this.books.off('add', this.onUpdate);
		this.books.off('remove', this.onUpdate);
	}
},

Listening to model events

The above examples shows how to listen/unlisten to collection events. As shown in the example’s Book component, listening to model events is close to identical:

created() {
	this.book.on('change', this.onUpdate);
},
beforeDestroy() {
	this.book.off('change', this.onUpdate);
},
methods: {
	onUpdate() {
		this.$forceUpdate();
	}
}

Tip

Instead of using forceUpdate, one could use data variables which are initialized from props, and updated in the event callback methods:

data() {
    return {
        title: this.book.title,
        author: this.book.author
    };
},
methods: {
    onUpdate() {
        this.title = this.book.title;
        this.author = this.book.author;
    }
}

Calling methods

Resources are not only data, but may also have methods that can be called. In the Book component, we can see how such a method is called when editing the book’s fields:

this.book.set({
	title: this.editTitle,
	author: this.editAuthor
});

Conclusion

That’s it! Communication and real-time synchronization with backend is solved for your Vue.js app, and everything updates as expected.

What’s left?

This guide doesn’t bring up transition animations between different states. But that subject has more to do with Vue.js than with ResClient, and is better discussed by other people.

Enjoy using ResClient!

Note

By using a res-vuejs library, the amount of boilerplate code can be reduced. Unfortunately, no such library exists yet. Care to create it?