Installing
Install axios
and vuex-loopback
.
yarn add axios vuex-loopback
or via npm
npm install axios vuex-loopback
Create Vuex module
axios
and module factory.
1. Import import axios from 'axios';
import {createModule} from 'vuex-loopback';
axios
instance with baseURL
option.
2. Create const client = axios.create({
baseURL: 'https://my-domain.com/api/',
});
3. Define collection model with default fields.
const model = {
id: '',
title: '',
body: '',
}
4. Create Vuex module by the module factory.
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
.
Vuex module usage
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.
Create item
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.
Fetch items
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.
Fetch item by ID
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": ""
}
Update item
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
.
Remove item
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.
Load items by Vue Component
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.
Props
-
module: string
- Name of Vuex module. -
noAutoload: boolean
- Do not autoload items after mount.
Scope of default slot
-
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).
Basic example
ItemsLoader
from vuex-loopback
.
1. Import import {ItemsLoader} from 'vuex-loopback';
2. Define local component.
export default {
// ...
components: {
ItemsLoader,
},
// ...
}
3. Use it to load collection items.
<!-- 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>
Manage an item by Vue Component
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.
Props
-
module: string
- Name of Vuex module. -
extend: object
- Extend an item fields.
Scope of default slot
-
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).
Basic example
ItemEditor
from vuex-loopback
.
1. Import import {ItemEditor} from 'vuex-loopback'; // new line
import {ItemsLoader} from 'vuex-loopback';
2. Define local component.
export default {
// ...
components: {
ItemEditor, // new line
ItemsLoader,
},
// ...
}
3. Use it to create editor form.
<!-- 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>
4. Update items loader template.
<!-- 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>
Module structure
You may want to use Vuex module directly.
Let's see what it has.
State
-
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.
Getters
-
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.
Mutations
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)
Actions:
-
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()
Tests
loopback-example-relations
and start web-server.
1. Clone git clone https://github.com/strongloop/loopback-example-relations.git
cd loopback-example-relations
yarn
yarn start
vuex-loopback
in a new terminal session and run the tests.
2. Clone git clone https://github.com/mikeevstropov/vuex-loopback.git
cd vuex-loopback
yarn
yarn test
Examples
Vue CLI project vuex-loopback-example
Todo
- [x] State factory.
- [x] Mutations factory.
- [x] Actions factory.
- [x] Getters factory.
- [x] Loader component.
- [x] Editor component.
- [ ] Documentation.
- [x] Examples.