Skip to content

Commit f7bcb6b

Browse files
committed
Merge branch 'master' into 3.0
# Conflicts: # package.json
2 parents 8bbc38f + a2f2a51 commit f7bcb6b

File tree

6 files changed

+188
-7
lines changed

6 files changed

+188
-7
lines changed

docs/.vuepress/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module.exports = {
22
title: 'FeathersVuex',
33
description: 'Integration of FeathersJS, Vue, and Nuxt for the artisan developer',
4+
head: [['link', { rel: 'icon', href: '/favicon.ico' }]],
45
theme: 'default-prefers-color-scheme',
56
themeConfig: {
67
repo: 'feathersjs-ecosystem/feathers-vuex',
@@ -24,4 +25,4 @@ module.exports = {
2425
updatePopup: true
2526
}
2627
}
27-
}
28+
}

docs/.vuepress/public/favicon.ico

11.8 KB
Binary file not shown.
87.5 KB
Loading

docs/api-overview.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,17 @@ build: {
112112
}
113113
```
114114

115+
## Vue DevTools
116+
117+
Since Feathers-Vuex extensively uses Vuex under the hood, you'll want to make sure your VueJS developer tools are up to date AND setup properly. Specifically, the "New Vuex Backend" needs to be enabled. To setup the devtools
118+
119+
1. Open the Vue tab of the developer tools while viewing your Vue project in the browser.
120+
1. Go to the Settings panel.
121+
1. Enable the new Vuex backend:
122+
123+
![New Vuex Backend in Vue DevTools](/img/devtools.jpg)
124+
125+
When the above setting is not enabled, the Vue Devtools will likely hang when you start working on a large project.
115126

116127
## Setup
117128

docs/feathers-vuex-form-wrapper.md

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,12 @@ The `FeathersVuexFormWrapper` component uses the "clone and commit" pattern to c
4646
</template>
4747
4848
<script>
49+
50+
import { FeathersVuexFormWrapper } from 'feathers-vuex'
51+
4952
export default {
5053
name: 'MyComponent',
54+
components: { FeathersVuexFormWrapper },
5155
props: {
5256
currentItem: {
5357
type: Object,
@@ -85,8 +89,12 @@ Here's another example of how you could use the form wrapper to both save the fo
8589
</template>
8690
8791
<script>
92+
93+
import { FeathersVuexFormWrapper } from 'feathers-vuex'
94+
8895
export default {
8996
name: 'MyComponent',
97+
components: { FeathersVuexFormWrapper },
9098
props: {
9199
currentItem: {
92100
type: Object,
@@ -117,4 +125,128 @@ The default slot contains only four attributes. The `clone` data can be passed
117125
- `clone`: {Object} The cloned record. Each record in the store can have a single clone. The clones are stored on the service's model class, by default.
118126
- `save`: {Function} When called, it commits the data and saves the record (with eager updating, by default. See the `eager` prop.) The save method calls `instance.save()`, internally, so you can pass a params object, if needed.
119127
- `reset`: {Function} When called, the clone data will be reset back to the data that is currently found in the store for the same record.
120-
- `remove`: {Function} When called, it removes the record from the API server and the Vuex store.
128+
- `remove`: {Function} When called, it removes the record from the API server and the Vuex store.
129+
130+
## Example Usage: CRUD Form
131+
132+
### TodoView
133+
134+
It's a pretty common scenario to have the same form handle editing and creating data. Below is a basic example of how you could use the FeathersVuexFormWrapper for this. A few things to notice about the example:
135+
136+
1. It uses a `Todo` Model class to create and edit todos. The `$FeathersVuex` object is available on `this` only when the [Feathers-Vuex Vue plugin](./vue-plugin.md) is used.
137+
2. It assumes that you have a route setup with an `:id` parameter.
138+
3. It assumes that the data has a MongoDB-style `_id` property, where an SQL-based service would probably use `id`.
139+
140+
```vue
141+
<template>
142+
<FeathersVuexFormWrapper :item="item" watch>
143+
<template v-slot="{ clone, save, reset, remove }">
144+
<TodoEditor
145+
:item="clone"
146+
@save="save().then(handleSaveResponse)"
147+
@reset="reset"
148+
@remove="remove"
149+
></TodoEditor>
150+
</template>
151+
</FeathersVuexFormWrapper>
152+
</template>
153+
154+
<script>
155+
import { FeathersVuexFormWrapper } from 'feathers-vuex'
156+
import TodoEditor from './TodoEditor.vue'
157+
158+
export default {
159+
name: 'TodoView',
160+
components: {
161+
FeathersVuexFormWrapper,
162+
TodoEditor
163+
},
164+
props: {
165+
currentItem: {
166+
type: Object,
167+
required: true
168+
}
169+
},
170+
computed: {
171+
id() {
172+
return this.$route.params.id
173+
},
174+
// Returns a new Todo if the route `id` is 'new', or returns an existing Todo.
175+
item() {
176+
const { Todo } = this.$FeathersVuex.api
177+
178+
return this.id === 'new' ? new Todo() : Todo.getFromStore(this.id)
179+
},
180+
},
181+
watch: {
182+
id: {
183+
handler(val) {
184+
// Early return if the route `:id` is 'new'
185+
if (val === 'new') {
186+
return
187+
}
188+
const { Todo } = this.$FeathersVuex.api
189+
const existingRecord = Todo.getFromStore(val)
190+
191+
// If the record doesn't exist, fetch it from the API server
192+
// The `item` getter will automatically update after the data arrives.
193+
if (!existingRecord) {
194+
Todo.get(val)
195+
}
196+
},
197+
// We want the above handler handler to run immediately when the component is created.
198+
immediate: true
199+
}
200+
},
201+
methods: {
202+
handleSaveReponse(savedTodo) {
203+
// Redirect to the newly-saved item
204+
if (this.id === 'new') {
205+
this.$router.push({ params: { id: savedTodo._id } })
206+
}
207+
}
208+
}
209+
}
210+
</script>
211+
```
212+
213+
214+
### TodoEditor
215+
216+
Next let's look at a minimal example of a 'TodoEditor' component which is a child of the `FeathersVuexFormWrapper` in the above example. A few things to notice about the below `TodoEditor` component:
217+
218+
1. It's minimal on purpose to show you the important parts of working with the `FeathersVuexFormWrapper`.
219+
1. It emits the `save`, `reset`, and `remove` events, which are connected to the `FeathersVuexFormWrapper` in the above code snippet.
220+
1. It's not styled to keep it simple. You'll probably want to add some styles. ;)
221+
1. The Delete button immediately emits remove, so the instance will be deleted immediately. You probably want, instead, to show a prompt or confirmation dialog to ask the user to confirm deletion.
222+
1. This is HTML, so the button `type` is important. If you forget to add `type="button"` to a button, it will default to `type="submit"`. Clicking the button would submit the form and call the `@submit.prevent` handler on the `<form>` element. This even applies to buttons inside child components of the form. You definitely want to remember to put `type` attributes on all of your buttons.
223+
224+
```vue
225+
<template>
226+
<form @submit.prevent="$emit('save')">
227+
<input type="checkbox" v-model="item.isComplete" />
228+
<input type="text" v-model="item.description" />
229+
230+
<!-- Submits the form, see the @submit handler, above -->
231+
<button type="submit">Save</button>
232+
233+
<!-- Emitting reset will restore the item back to the stored version. -->
234+
<button type="button" @click="$emit('reset')>Reset</button>
235+
236+
<!-- Delete's the instance -->
237+
<button type="button" @click="$emit('remove')>Delete</button>
238+
</form>
239+
</template>
240+
241+
<script>
242+
export default {
243+
name: 'TodoEditor',
244+
props: {
245+
item: {
246+
type: Object,
247+
required: true
248+
}
249+
}
250+
}
251+
</script>
252+
```

docs/model-classes.md

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,23 +113,60 @@ created () {
113113

114114
`instanceDefaults(data, { store, Models })`
115115

116+
The `instanceDefaults` API was created in version 1.7 to prevent requiring to specify data for new instances created throughout the app. Depending on the complexity of the service's "business logic", it can save a lot of boilerplate. Notice that it is similar to the `setupInstance` method added in 2.0. The instanceDefaults method should ONLY be used to return default values for a new instance. Use `setupInstance` to handle other transformations on the data.
117+
116118
Starting with version 2.0, `instanceDefaults` must be provided as a function. The function will be called with the following arguments and should return an object of default properties for new instances.
117119

118120
- `data {Object}` - The instance data
119121
- An `utils` object containing these props:
120122
- `store` - The vuex store
121-
- `Models {Object}` The `globalModels` object, which is the same as you'll find inside a component at `this.$FeathersVuex`.
123+
- `models {Object}` The `globalModels` object, which is the same as you'll find inside a component at `this.$FeathersVuex`.
124+
125+
As an example, a User model class might look like this:
126+
127+
```js
128+
instanceDefaults(data, { store, models }) {
129+
return {
130+
firstName: '',
131+
lastName: '',
132+
email: '',
133+
password: '',
134+
isAdmin: false
135+
}
136+
}
137+
```
138+
139+
With the above attributes in place, you no longer have to manually specify any of the listed attributes. You can, however, provided data to replace them. Calling `new User({ firstName: 'Marshall' })` will create the instance with the `firstName` filled in, already.
140+
141+
One important note, the `isAdmin` attribute is specified in the above example in order to allow immediate binding in a form. You would pretty much NEVER allow specifying `isAdmin` from the client and storing it on the server. Attributes related to roles and app security should pretty much ALWAYS be written in hooks on the API server.
122142

123143
### setupInstance <Badge text="2.0.0+" />
124144

125145
`setupInstance(data, { store, Models })`
126146

127-
A new `setupinstance` class method is now available in version 2.0. The function will be called with the following arguments and should return an object of default properties for new instances.
147+
A new `setupinstance` class method is now available in version 2.0. This method allows you to transform the data and setup the final instance based on incoming data. For example, you can access the `models` object to reference other service Model classes and create data associations.
148+
149+
The function will be called with the following arguments and should return an object of default properties for new instances.
128150

129151
- `data {Object}` - The instance data
130-
- An `utils` object containing these props:
152+
- A `utils` object containing these props:
131153
- `store` - The vuex store
132-
- `Models {Object}` The `globalModels` object, which is the same as you'll find inside a component at `this.$FeathersVuex`.
154+
- `models {Object}` The `globalModels` object, which is the same as you'll find inside a component at `this.$FeathersVuex`.
155+
156+
For an example of how you might use `setupInstance`, suppose we have two services: Users and Posts. Assume that the API request to get a user includes their `posts`, already populated on the data. The `instanceDefaults` allows us to convert the array of `posts` into an array of `Post` instances.
157+
158+
```js
159+
// The setupInstance method on an imaginary User model.
160+
setupInstance(data, { store, models }) {
161+
if (data.posts) {
162+
// Turn posts into an array of Post instances
163+
data.posts = data.posts.map(p => new models.Todo(p))
164+
}
165+
return data
166+
}
167+
```
168+
169+
With the above `setupInstance` method in place, each `User` instance now stores a direct reference to the `Post` records in the store.
133170

134171
### on <Badge text="2.3.0+" />
135172

@@ -165,7 +202,7 @@ export default {
165202
}
166203
```
167204

168-
Since they have all of the EventEmitter methods, Model classes can be used as a data-layer Event Bus. You can even use custom methods:
205+
Since they have all of the EventEmitter methods, Model classes can be used as a data-layer Event Bus. You can even use custom event names:
169206

170207
```js
171208
const { Todo } = this.$FeathersVuex.api

0 commit comments

Comments
 (0)