Skip to content

Commit 40f0cf1

Browse files
committed
Add custom actions and so much more
1 parent 80e238c commit 40f0cf1

19 files changed

+459
-158
lines changed

docs/.vuepress/config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ const sidebars = {
55
collapsable: false,
66
children: [
77
'/guide/installation',
8-
'/guide/getting-started',
9-
'/guide/basic-usage',
10-
'/guide/advanced-usage',
8+
'/guide/setup',
9+
'/guide/usage',
1110
'/guide/configurations',
11+
'/guide/custom-actions',
1212
'/guide/sponsors'
1313
]
1414
},

docs/api/request.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,3 @@ User.api().get()
7575
- **`delete(url: string, config: Config = {}): Promise<Response>`**
7676

7777
Performs a `DELETE` request. It takes the same argument as Axios's `delete` method.
78-
79-
### fetch
80-
81-
- **`fetch(url: string, params: any, config: Config = {}): Promise<Response>`**
82-
83-
The `fetch` method works almost as same as `get` method, but it will take URL query parameters as the 2nd argument. Please [refer here](../guide/advanced-usage.html#fetch) for more details.

docs/guide/advanced-usage.md

Lines changed: 0 additions & 31 deletions
This file was deleted.

docs/guide/configurations.md

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import VuexORMAxios from '@vuex-orm/plugin-axios'
1515

1616
VuexORM.use(VuexORMAxios, {
1717
axios,
18-
headers: {'X-Requested-With': 'XMLHttpRequest'},
18+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
1919
baseURL: 'https://example.com/api/'
2020
})
2121
```
@@ -36,7 +36,7 @@ class User extends Model {
3636
}
3737

3838
static apiConfig = {
39-
headers: {'X-Requested-With': 'XMLHttpRequest'},
39+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
4040
baseURL: 'https://example.com/api/'
4141
}
4242
}
@@ -46,7 +46,7 @@ Finally, you can pass configuration when making the api call.
4646

4747
```js
4848
User.api().get('/api/users', {
49-
headers: {'X-Requested-With': 'XMLHttpRequest'},
49+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
5050
baseURL: 'https://example.com/api/'
5151
})
5252
```
@@ -59,7 +59,9 @@ All Axios configurations are available. For those, please refer to [the Axios do
5959

6060
### dataKey
6161

62-
- **`dataKey: string | null`**
62+
- **`dataKey?: string | null`**
63+
64+
This option will define which key to look for when persisting response data. Let's say your response from the server looks like below.
6365

6466
```js
6567
{
@@ -82,3 +84,37 @@ All Axios configurations are available. For those, please refer to [the Axios do
8284
```
8385

8486
With the above config, the data inside `data` key will be inserted to the store.
87+
88+
### save
89+
90+
- **`save: boolean = true`**
91+
92+
This option will determine whether to store the response data to the store or not. If you set this value to `false`, the response data will not be persisted to the store. In that case, the `entities` property at the Response object will become null.
93+
94+
```js
95+
User.api().get('/api/users', {
96+
save: false
97+
})
98+
```
99+
100+
### delete
101+
102+
- **`delete?: string | number | (model: Model) => boolean`**
103+
104+
When this option is defined, the matching record will be deleted from the store after the api call. Usually, you need to set this when calling api to delete a record. When this option is set, the response data will not be persisted even if the `save` option is set to true.
105+
106+
```js
107+
User.api().delete('/api/users', {
108+
delete: 1
109+
})
110+
```
111+
112+
Well, you may wonder having to manually specify what record to be deleted is a bit redundant. However, without this option, Vuex ORM Axios wouldn't know what records should be deleted because it can't rely on the response data.
113+
114+
We're open to any suggestions and recommendations to improve the "delete" functionality. Please feel free to open an issue on GitHub!
115+
116+
### actions
117+
118+
- **`actions?: { [name: string]: ActionObject | ActionFunction }`**
119+
120+
You can define custom actions in here. Please refer to the [Custom Actions](custom-actions) page to learn more about it.

docs/guide/custom-actions.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Custom Actions
2+
3+
The Custom Actions lets you define your own predefined api methods. You can define any number of custom actions through your Model configurations through `actions` option.
4+
5+
```js
6+
class User extends Model {
7+
static entity = 'users'
8+
9+
static fields () {
10+
return {
11+
id: this.attr(null),
12+
name: this.attr('')
13+
}
14+
}
15+
16+
static apiConfig = {
17+
actions: {
18+
fetch: {
19+
method: 'get',
20+
url: '/api/users'
21+
}
22+
}
23+
}
24+
}
25+
```
26+
27+
You can see that in the above example, we have defined `fetch` action with the option that defines the `method` and `url`.
28+
29+
Now you may call this action as if it was a predefined method.
30+
31+
```js
32+
User.api().fetch()
33+
```
34+
35+
The above method will perform api call to `/api/users` with `GET` method. Now the value for the action (in the example, which is the object that defines `method` and `url`) is the request configuration. Now you see that the above example is equivalent to calling:
36+
37+
```js
38+
User.api().request({
39+
method: 'get',
40+
url: '/api/users'
41+
})
42+
```
43+
44+
Actions can also be defined as a function. In this case, just call the desired method with in action. With this approach, you can configure the convenience argument to the action, and gives you more powerful control.
45+
46+
Remember that inside the function, `this` is bind to the Request object, not the Model where the actions are defined.
47+
48+
```js
49+
class User extends Model {
50+
static apiConfig = {
51+
actions: {
52+
fetchById (id) {
53+
return this.get(`/api/users/${id}`)
54+
}
55+
}
56+
}
57+
}
58+
```
59+
60+
And you can call that action like so.
61+
62+
```js
63+
User.api().fetchById(1)
64+
```
65+
66+
## When to Use Custom Actions?
67+
68+
While the custom actions are convenient and easy to set up, you can always define methods to the Model directly to get pretty much the same result.
69+
70+
```js
71+
class User extends Model {
72+
static fetchById (id) {
73+
return this.api().get(`/api/users/${id}`)
74+
}
75+
}
76+
```
77+
78+
Well of crouse in this case, you must call that method from Model and not from `api()` method.
79+
80+
```js
81+
User.fetchById(1)
82+
```
83+
84+
To be honest, this is a much better way to define custom methods in terms of simplicity and also better with type safety when using TypeScript.
85+
86+
The benefits of defining custom actions inside the configuration are that you can put those methods under Request object, so it becomes more consistent when calling it from the Model. Also, it could be easier to share custom actions between different Models.
87+
88+
It's up to you how to define custom actions. Though if you have any ideas or feedback, we're more than happy to hear it from you!

docs/guide/getting-started.md renamed to docs/guide/setup.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# Setup
22

3-
This page is a quick start guide to begin using Vuex ORM. It assumes you have a basic understanding of Vuex ORM. If you are not familiar with Vuex ORM, please check out the [Vuex ORM Documentation](https://vuex-orm.github.io/vuex-orm/).
4-
5-
At first, Vuex ORM Axios requires manually passing Axios instance during the installation process. Please make sure you have axios installed to your app.
3+
At first, Vuex ORM Axios requires manually passing Axios instance during the setup process. Please make sure you have axios installed to your app.
64

75
To install Vuex ORM Axios to Vuex ORM, pass Vuex ORM Axios to the `VuexORM.use` method. Here, you should pass your axios instance as an option.
86

docs/guide/basic-usage.md renamed to docs/guide/usage.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Basic Usage
1+
# Usage
22

33
After setting up Vuex ORM Axios, you may use `Model.api` method to perform api call.
44

@@ -61,6 +61,18 @@ User.api().get('users', {
6161

6262
There're additional configuration specific to Vuex ORM Axios as well. Please check out [Configurations page](configurations) for more.
6363

64+
### Note on Delete Method
65+
66+
Even when you call `delete` method, it will not delete records from the store. It just means that it will perform HTTP DELETE request. If you want to delete the record after calling the API, you must define `delete` option.
67+
68+
```js
69+
User.api().delete('/api/users/1', {
70+
delete: 1
71+
})
72+
```
73+
74+
The above example will delete the user record with an id of 1. Please check out [Configurations page](configurations) for more.
75+
6476
## Response
6577

6678
The response object is a bit different from Axios response and contains 2 properties. One is `response`, which is the pure Axios response object, and the second one is the `entities`, which holds Vuex ORM persistent result.

src/api/Request.ts

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { AxiosInstance, AxiosResponse } from 'axios'
22
import { Model } from '@vuex-orm/core'
3-
import GlobalConfig from '../contracts/GlobalConfig'
43
import Config from '../contracts/Config'
54
import Response from './Response'
65

@@ -10,13 +9,27 @@ export default class Request {
109
*/
1110
model: typeof Model
1211

12+
/**
13+
* The default config.
14+
*/
15+
config: Config = {
16+
save: true
17+
}
18+
1319
/**
1420
* Create a new api instance.
1521
*/
1622
constructor (model: typeof Model) {
1723
this.model = model
24+
25+
this.registerActions()
1826
}
1927

28+
/**
29+
* Index key for the user defined actions.
30+
*/
31+
[action: string]: any
32+
2033
/**
2134
* Get the axios client.
2235
*/
@@ -28,6 +41,41 @@ export default class Request {
2841
return this.model.axios
2942
}
3043

44+
/**
45+
* Register actions from the model config.
46+
*/
47+
private registerActions (): void {
48+
const actions = this.model.apiConfig.actions
49+
50+
if (!actions) {
51+
return
52+
}
53+
54+
for (const name in actions) {
55+
const action = actions[name]
56+
57+
typeof action === 'function'
58+
? this.registerFunctionAction(name, action)
59+
: this.registerObjectAction(name, action)
60+
}
61+
}
62+
63+
/**
64+
* Register the given object action.
65+
*/
66+
private registerObjectAction (name: string, action: any): void {
67+
this[name] = (config: Config) => {
68+
return this.request({ ...action, ...config })
69+
}
70+
}
71+
72+
/**
73+
* Register the given function action.
74+
*/
75+
private registerFunctionAction (name: string, action: any): void {
76+
this[name] = action.bind(this)
77+
}
78+
3179
/**
3280
* Perform a get request.
3381
*/
@@ -63,13 +111,6 @@ export default class Request {
63111
return this.request({ method: 'delete', url, ...config })
64112
}
65113

66-
/**
67-
* Fetch data from the api.
68-
*/
69-
fetch (url: string, params: any = {}, config: Config = {}): Promise<Response> {
70-
return this.get(url, { params, ...config })
71-
}
72-
73114
/**
74115
* Perform an api request.
75116
*/
@@ -78,22 +119,39 @@ export default class Request {
78119

79120
const response = await this.axios.request(requestConfig)
80121

81-
const entities = await this.model.insertOrUpdate({
82-
data: this.getDataFromResponse(response, requestConfig)
83-
})
122+
const entities = await this.persistResponseData(response, requestConfig)
84123

85-
return new Response(response, entities)
124+
return new Response(this.model, requestConfig, response, entities)
86125
}
87126

88127
/**
89128
* Create a new config by merging the global config, the model config,
90129
* and the given config.
91130
*/
92131
private createConfig (config: Config): Config {
93-
const globalConfig: GlobalConfig = this.model.globalApiConfig
94-
const modelConfig: Config = this.model.apiConfig || {}
132+
return {
133+
...this.config,
134+
...this.model.globalApiConfig,
135+
...this.model.apiConfig,
136+
...config
137+
}
138+
}
139+
140+
/**
141+
* Persist the response data to the vuex store.
142+
*/
143+
private async persistResponseData (response: AxiosResponse, config: Config): Promise<any> {
144+
if (!config.save) {
145+
return null
146+
}
95147

96-
return { ...globalConfig, ...modelConfig, ...config }
148+
if (config.delete !== undefined) {
149+
return this.model.delete(config.delete as any)
150+
}
151+
152+
return this.model.insertOrUpdate({
153+
data: this.getDataFromResponse(response, config)
154+
})
97155
}
98156

99157
/**

0 commit comments

Comments
 (0)