Utility Belt App Extension

Speeding up the development of SSR and offline-first apps

Tobias Mesquita
Oct 5 · 5 min read

Note: This article was originally released on Dev.to.

Table Of Contents

1 Introduction

I developed a small app extension with the goal to speed up the development/prototyping of the SSR and offline-first apps. I’ll try to explain all the helpers, how to use them and what problem they try to solve.

2 Installing

quasar ext add "@toby.mosque/utils"

Remember, the full package name is @toby.mosque/quasar-app-extension-utils, but for convenience, the extension will register @toby.mosque/utils as an alias to @toby.mosque/quasar-app-extension-utils/src/utils.

3 Timer

3.1 Sleep

time.sleep is as simple as:

the goal is to enable the dev to suspend an async method for sometime.

outputs:

start:  {"time":1567290656567,"iso":"2019-08-31T22:30:56.567Z"}
end: {"time":1567290661570,"iso":"2019-08-31T22:31:01.570Z"}

4 UUID

A common issue that happens when you use UUID/GUID as primary key, is the order of insertion of your records/docs becomes unpredictable, which can potentially slow down the insertion of new records/docs, or even make the order by id a problem with the UX, since the records/docs will look like it's been shuffled around.

4.1 comb

The comb method is inspired by the C# lib RT.Comb, but I implemented only the Postgre provider, that replaces the first 6 bytes of the UUID with the current date.

4.1.1 parameters

4.2 extractDate

The extractDate method allows the dev to retrieve the date used to create the COMB.

4.2.1 parameters

4.3 Demos

4.3.1 Basic Usage

outputs

comb a:  016ce9d4-cb8f-4b88-b049-8569e9bec009
comb b: 016cee81-321b-f5ee-92ab-fdafda9612fe
date a: {"time":1567291132815,"iso":"2019-08-31T22:38:52.815Z"}
date b: {"time":1567369540123,"iso":"2019-09-01T20:25:40.123Z"}

4.3.2 Sorting

outputs

ascending:
{"index":1,"value":"016ce9dc-9422-be1b-d70f-8769fa62380c"}
{"index":2,"value":"016ce9dc-94e1-40e4-5a16-7db2c0fb109d"}
{"index":3,"value":"016ce9dc-94e3-1316-8633-23694b929895"}
{"index":4,"value":"016ce9dc-94e5-04e3-d043-2b147af04f19"}
{"index":5,"value":"016ce9dc-98ca-8307-1ea1-54a488a1195c"}
{"index":6,"value":"016ce9dc-9cb2-468b-23d6-3bbe47c70539"}
{"index":7,"value":"016ce9dc-a099-fc93-8edf-68f3c00b4fcc"}
{"index":8,"value":"016ce9dc-a482-5f80-b40d-a64b0e8a23a9"}
{"index":9,"value":"016ce9dc-a86a-d6c5-1359-ce0c7b9d053e"}
{"index":10,"value":"016ce9dc-abc4-7d81-e27b-9f1868a48be8"}
descending:
{"index":10,"value":"016ce9dc-abc4-7d81-e27b-9f1868a48be8"}
{"index":9,"value":"016ce9dc-a86a-d6c5-1359-ce0c7b9d053e"}
{"index":8,"value":"016ce9dc-a482-5f80-b40d-a64b0e8a23a9"}
{"index":7,"value":"016ce9dc-a099-fc93-8edf-68f3c00b4fcc"}
{"index":6,"value":"016ce9dc-9cb2-468b-23d6-3bbe47c70539"}
{"index":5,"value":"016ce9dc-98ca-8307-1ea1-54a488a1195c"}
{"index":4,"value":"016ce9dc-94e5-04e3-d043-2b147af04f19"}
{"index":3,"value":"016ce9dc-94e3-1316-8633-23694b929895"}
{"index":2,"value":"016ce9dc-94e1-40e4-5a16-7db2c0fb109d"}
{"index":1,"value":"016ce9dc-9422-be1b-d70f-8769fa62380c"}

5 Store

If you’re developing an SSR or offline-first app, you’re probably overusing Vuex modules; and I know, you don’t have much choice here. So let’s try to make parts of the module definition more reusable.

5.1 mapStoreMutations

mapStoreMutations maps all classes fields to a mutations-like object.

5.1.1 parameters

5.1.2 Demos

5.1.2.1 Basic Usage

Expected Output

5.1.3 Scenario without the helper

This is what you would need to write to create the above module without using the mapStoreMutations helper

5.2 mapStoreCollections

Here the goal is to create mutations (create, update, delete), actions (upsert, delete) and getters (index, getById) related to array fields.

5.2.1 parameters

5.2.1.1 Collection info object

5.2.2 Demos

5.2.2.1 Basic Usage

Expected Output

5.2.3 Scenario without the helper

This is what you would need to write in order to create the above module without the help of the mapStoreMutations and mapStoreCollections helpers

5.3 mapState

I don’t know if you noticed, but our store now has a pattern, where the state fields and mutations names have the same name and that has a reason.

We need both to have the same name, in order to map them to a computed property, to be exact, the state will be mapped to a get, and the mutation to a set.

5.3.1 Parameters

The parameters will be very much like the Vuex mapState’s parameters.

5.3.2 Demos

5.3.2.1 Basic Usage

Expected Output

console

mounted page

getting record/doc by your id

item removed

6 Factories

the primary goal here is to wrap components or generate the whole store and/or pages.

6.1 component

the factory.component objective is to wrap another component and allow the dev to modify them at the render time, or even set up your properties, slots, etc.

6.1.1 Parameters

6.1.1.1 Options

6.1.2 Demos

6.1.2.1 Basic Usage

We’ll inject the reverse property in the QInput and when that property is set as true the label will be inverted. Besides that, we'll set the dark and outlined property to true. The QMarkupTable will also have the dark property changed to true

Expected Output

6.1.2.2 More Reusable

Ok, we had only 2 components and we already had a lot of boilerplate. We can improve that:

Now, we’re rebranding QInput, QSelect, QField, QMarkupTable, QList and QItem

Expected Output

6.2 Store

factory.store combines store.mapStoreMutations and store.mapStoreCollections.

6.2.1 Parameters

6.2.1.1 options

6.2.2 Demos

6.2.2.1 Basic Usage

Expected Output

6.3 Page

factory.page will expect the same options as factory.store and will map the state, mutations, actions and getters generated by factory.store to the page.

6.3.1 Parameters

6.3.1.1 options

6.3.2 Demos

6.3.2.1 Basic Usage

Expected Output

6.4 Page and Store

Let’s combine factory.page with factory.store and see what happens.

6.4.1 Demos

6.4.1.1 Basic Usage

Pay particular attention to the initialize action, it receives the route as an argument and is called as soon as the store is registered (perfect place to load data intro the state).

Expected Output

console — without initialize action

console — with initialize action

rendered page

Interested in Quasar? Here are some more tips and information:

More info: https://quasar.dev
GitHub: https://github.com/quasarframework/quasar
Newsletter: https://quasar.dev/newsletter
Getting Started: https://quasar.dev/start
Chat Server: https://chat.quasar.dev/
Forum: https://forum.quasar.dev/
Twitter: https://twitter.com/quasarframework
Donate: https://donate.quasar.dev

Tobias Mesquita

Written by

Quasar Framework

Build high-performance cross-device VueJS user interfaces in record time

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade