vuex-loopback v1.5.4
Installing
Install axios and vuex-loopback.
yarn add axios vuex-loopbackor via npm
npm install axios vuex-loopbackCreate Vuex module
1. Import axios and module factory.
import axios from 'axios';
import {createModule} from 'vuex-loopback';2. Create axios instance with baseURL option.
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
1. Import ItemsLoader from vuex-loopback.
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
1. Import ItemEditor from vuex-loopback.
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 ofitemandtempItemis not the same.
Mutations
RESETSET_ITEM(value: object)RESET_ITEMSET_TEMP_ITEM(value: object)RESET_TEMP_ITEMSET_ITEMS(value: object[])RESET_ITEMSSET_SKIP(value: number)RESET_SKIPSET_LIMIT(value: number)RESET_LIMITSET_TOTAL(value: number)RESET_TOTALSET_ORDER_BY(value: string)RESET_ORDER_BYSET_ORDER_DESC(value: boolean)RESET_ORDER_DESCSET_SEARCH_BY(value: string[])RESET_SEARCH_BYSET_SEARCH_QUERY(value: string)RESET_SEARCH_QUERYSET_WHERE(value: object)RESET_WHERESET_LOADING(value: boolean)RESET_LOADINGSET_INCLUDE(value: string[])RESET_INCLUDESET_FIELDS(value: string[])RESET_FIELDSUPDATE_ITEM(item: object)REMOVE_ITEM(id: number|string)
Actions:
FETCH_ITEM(payload)id: number|stringfilter: object = {}noTempItem: boolean = false
FETCH_ITEMS(payload)filter: object = {}noGlobals: boolean = falseappend: boolean = false
CREATE_ITEM(payload)data: objectfilter: object = {}
PATCH_ITEM(payload)id: number|stringdata: objectfilter: object = {}
REMOVE_ITEM(id: number|string)CREATE_TEMP_ITEM(item: object = null)PUT_TEMP_ITEM(payload)filter: object = {}noPatch: boolean = falsereset: boolean = false
SEARCH_ITEMS(payload)query: string = ''searchBy: string[] = null
FETCH_PAGE(payload)page: number = 1
FETCH_MORE()
Tests
1. Clone loopback-example-relations and start web-server.
git clone https://github.com/strongloop/loopback-example-relations.git
cd loopback-example-relations
yarn
yarn start2. Clone vuex-loopback in a new terminal session and run the tests.
git clone https://github.com/mikeevstropov/vuex-loopback.git
cd vuex-loopback
yarn
yarn testExamples
Vue CLI project vuex-loopback-example
Todo
- State factory.
- Mutations factory.
- Actions factory.
- Getters factory.
- Loader component.
- Editor component.
- Documentation.
- Examples.
3 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago