JSPM

  • Created
  • Published
  • Downloads 8
  • Score
    100M100P100Q52576F
  • License Apache-2.0

Vuex module factory for Loopback

Package Exports

  • vuex-loopback
  • vuex-loopback/dist/main.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (vuex-loopback) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

npm version license



logo

Installing

Install axios and vuex-loopback.

yarn add axios vuex-loopback

or via npm

npm install axios vuex-loopback

Create 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 method set.
  • 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 of item.
  • 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 of item and tempItem 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

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 start

2. 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 test

Examples

Vue CLI project vuex-loopback-example

Todo

  • State factory.
  • Mutations factory.
  • Actions factory.
  • Getters factory.
  • Loader component.
  • Editor component.
  • Documentation.
  • Examples.