Install axios
and vuex-loopback
.
yarn add axios vuex-loopback
or via npm
npm install axios vuex-loopback
import axios from 'axios';
import {createModule} from 'vuex-loopback';
const client = axios.create({
baseURL: 'https://my-domain.com/api/',
});
const model = {
id: '',
title: '',
body: '',
}
Before use built-in components ItemsLoader
and ItemEditor
you need to create the Vuex module of Loopback collection which you want to manage. For example we will create a named module vlArticles
of Articles
collection.
new Vuex.Store({
modules: {
// ...
vlArticles: {
namespaced: true,
...createModule({
client, // (required) Axios instance.
model, // (required) Collection model.
collection: 'Articles', // (required) Plural collection name.
state: {}, // Extend default state.
getters: {}, // Extend default getters.
actions: {}, // Extend default actions.
mutations: {}, // Extend default mutations.
}),
},
// ...
}
});
It's recommended to prefer vl
prefix of a module name to mark module is created by vuex-loopback
.
The following fields will contain data when you get, create or update a single item.
item: object = null
- Persisted item.tempItem: object = null
- New or modified item.
And a state of a multiple items:
items: object[] = []
- Fetched items.
It's a general part of module state.
In the previous step we provided the model with default fields to the module factory. An action CREATE_TEMP_ITEM
will create a new item by this model automatically (only tempItem
state, not in database).
store.dispatch(
'vlArticles/CREATE_TEMP_ITEM',
{title: 'My Article'},
);
The second argument is not required but you can patch data of new item.
State of tempItem
now is:
{
"id": "",
"title": "My Article",
"body": ""
}
By an action PUT_TEMP_ITEM
the data of tempItem
will be created or updated (if exists) in database.
await store.dispatch(
'vlArticles/PUT_TEMP_ITEM',
);
During request a state of loading
is true
.
State of item
and tempItem
now is:
{
"id": "5fd491fceea2be937cb838fc",
"title": "My Article",
"body": ""
}
After that, we have a new state of item
which contains persisted data, but the tempItem
has updated also (by a new id
value). So if you will change the state of tempItem
then you can check differences between persisted data item
and modified tempItem
, and discard changes by SET_TEMP_ITEM
mutation to a previous value from item
state.
Type of generated ID is depends to your database.
Create another one article.
store.dispatch(
'vlArticles/CREATE_TEMP_ITEM',
{title: 'Second Article'}
);
await store.dispatch(
'vlArticles/PUT_TEMP_ITEM'
);
Dispatch an action FETCH_ITEMS
to get an array in items
state.
await store.dispatch(
'vlArticles/FETCH_ITEMS',
);
Use a second argument to provide fetching options.
State of items
is:
[
{
"id": "5fd491fceea2be937cb838fc",
"title": "My Article",
"body": ""
},
{
"id": "5fd491fceea2be937cb838fb",
"title": "Second Article",
"body": ""
}
]
The FETCH_ITEMS
action will request an items by conditions defined in the state which you can set by mutations.
An action FETCH_ITEM
will update item
and tempItem
state by fetched data.
await store.dispatch(
'vlArticles/FETCH_ITEM',
{id: '5fd491fceea2be937cb838fc'},
);
State of item
and tempItem
now is:
{
"id": "5fd491fceea2be937cb838fc",
"title": "My Article",
"body": ""
}
Before update a database, we need to modify the data of tempItem
which was fetched in the previous step.
const {tempItem} = store
.state
.vlArticles;
store.commit('vlArticles/SET_TEMP_ITEM', {
...tempItem,
body: 'Article body',
});
State of tempItem
now has a new body
value:
{
"id": "5fd491fceea2be937cb838fc",
"title": "My Article",
"body": "Article body"
}
Commit changes by PUT_TEMP_ITEM
action.
await store.dispatch(
'vlArticles/PUT_TEMP_ITEM',
);
Now your database and item
state has updated by modified tempItem
.
An action REMOVE_ITEM
will remove an item from database.
await store.dispatch(
'vlArticles/REMOVE_ITEM',
'5fd491fceea2be937cb838fc',
);
The item will be removed from the state automatically.
Built-in component ItemsLoader
will help you to load collection items right in Vue template. A scope of default slot has some usefull methods and properties to create items list with lazy-load or pagination behaviours.
module: string
- Name of Vuex module.noAutoload: boolean
- Do not autoload items after mount.
items: object[]
- Loaded items.loading: boolean
- Loading state.page: number
- Current page.pages: number
- Total number of pages.hasMore: boolean
- Can we load more?load()
- Load items.loadPage(page: number)
- Load specific page.loadMore()
- Load more items.
* Component will load items automatically if prop noAutoload
has not specified.
* All properties and methods of slot scope are accessible by component reference (ref
attribute).
import {ItemsLoader} from 'vuex-loopback';
export default {
// ...
components: {
ItemsLoader,
},
// ...
}
<!-- Loader -->
<items-loader
module="vlArticles">
<template
slot-scope="{items, hasMore, loadMore}">
<!-- Item -->
<div
:key="item.id"
v-for="item in items">
{{ item.title }}
</div>
<!-- More Button -->
<button
v-if="hasMore"
@click="loadMore">
More
</button>
</template>
</items-loader>
You are able to create, update or remove collection item by built-in component ItemEditor
. Same as above ItemEditor
has a scope of default slot which provides specific methods and properties.
module: string
- Name of Vuex module.extend: object
- Extend an item fields.
item: object
- Selected item.loading: boolean
- Loading state.edit(item: object)
- Select or create item if no argument specified.set(item: object)
- Update selected or created item temporary.save()
- Commit temporary changes applied by methodset
.remove()
- Remove selected item from collection.
* All properties and methods of slot scope are accessible by component reference (ref
attribute).
import {ItemEditor} from 'vuex-loopback'; // new line
import {ItemsLoader} from 'vuex-loopback';
export default {
// ...
components: {
ItemEditor, // new line
ItemsLoader,
},
// ...
}
<!-- Editor -->
<item-editor
ref="editor"
module="vlArticles">
<template
slot-scope="{item, set, save, remove}">
<form
@submit.prevent="save">
<!-- Title Field -->
<input
:value="item.title"
@input="set({...item, title: $event})"/>
<!-- Save Button -->
<button
type="submit">
Save
</button>
<!-- Remove Button -->
<button
v-if="item.id"
@click="remove">
Remove
</button>
</form>
</template>
</item-editor>
<!-- Loader -->
<items-loader
module="vlArticles">
<template
slot-scope="{items, hasMore, loadMore}">
<!-- Item -->
<div
:key="item.id"
v-for="item in items">
{{ item.title }}
<!-- Edit Button -->
<button
@click="() => $refs.editor.edit(item)">
Edit
</button>
</div>
<!-- More Button -->
<button
v-if="hasMore"
@click="loadMore">
More
</button>
<!-- Create Button -->
<button
@click="() => $refs.editor.edit()">
Create
</button>
</template>
</items-loader>
You may want to use Vuex module directly.
Let's see what it has.
item: object = null
- Loaded item.tempItem: object = null
- Clone ofitem
.items: object[] = []
- Loaded items.skip: number = 0
- Items offset.limit: number = 20
- Items limit.total: number = 0
- Total items.orderBy: string = ''
- Sort by field.orderDesc: boolean = ''
- Sort descending.searchBy: string[] = ['name']
- Search by fields.searchQuery: string = ''
- Searching query.where: object = {}
- Fetching condition.loading: boolean = false
- Loading state.include: string[] = []
- Fetching relations.fields: string[] = []
- Fetching fields.
page: number
- Number of current page.totalPages: number
- Number of total pages.hasMore: boolean
- Can we load more? (lazy loading)itemChanged: boolean
- State ofitem
andtempItem
is not the same.
RESET
SET_ITEM(value: object)
RESET_ITEM
SET_TEMP_ITEM(value: object)
RESET_TEMP_ITEM
SET_ITEMS(value: object[])
RESET_ITEMS
SET_SKIP(value: number)
RESET_SKIP
SET_LIMIT(value: number)
RESET_LIMIT
SET_TOTAL(value: number)
RESET_TOTAL
SET_ORDER_BY(value: string)
RESET_ORDER_BY
SET_ORDER_DESC(value: boolean)
RESET_ORDER_DESC
SET_SEARCH_BY(value: string[])
RESET_SEARCH_BY
SET_SEARCH_QUERY(value: string)
RESET_SEARCH_QUERY
SET_WHERE(value: object)
RESET_WHERE
SET_LOADING(value: boolean)
RESET_LOADING
SET_INCLUDE(value: string[])
RESET_INCLUDE
SET_FIELDS(value: string[])
RESET_FIELDS
UPDATE_ITEM(item: object)
REMOVE_ITEM(id: number|string)
-
FETCH_ITEM(payload)
id: number|string
filter: object = {}
noTempItem: boolean = false
-
FETCH_ITEMS(payload)
filter: object = {}
noGlobals: boolean = false
append: boolean = false
-
CREATE_ITEM(payload)
data: object
filter: object = {}
-
PATCH_ITEM(payload)
id: number|string
data: object
filter: object = {}
-
REMOVE_ITEM(id: number|string)
-
CREATE_TEMP_ITEM(item: object = null)
-
PUT_TEMP_ITEM(payload)
filter: object = {}
noPatch: boolean = false
reset: boolean = false
-
SEARCH_ITEMS(payload)
query: string = ''
searchBy: string[] = null
-
FETCH_PAGE(payload)
page: number = 1
-
FETCH_MORE()
git clone https://github.com/strongloop/loopback-example-relations.git
cd loopback-example-relations
yarn
yarn start
git clone https://github.com/mikeevstropov/vuex-loopback.git
cd vuex-loopback
yarn
yarn test
Vue CLI project vuex-loopback-example
- State factory.
- Mutations factory.
- Actions factory.
- Getters factory.
- Loader component.
- Editor component.
- Documentation.
- Examples.