Open tylermmorton opened 4 years ago
Thanks for the well-documented issue. We will probably want to include a utility like the one you made in a future release.
Maybe we can make FeathersVuexFormWrapper
check for instances in the store.
Thanks Marshall! I've glanced at FeathersVuexFormWrapper
to see if I could maybe submit a PR. I was thinking right in setup()
before the clone operation we could add a check for the clone function.
if(typeof this.item.clone !== undefined)
but I'm not sure how to determine the model type without passing it as a prop. Maybe you'd have a better idea on how to do this
@tylermmorton good point, we will definitely need to pass the model as a prop.
I haven't forgotten about this issue. I'm still playing around in my personal project and trying different solutions. If anyone has any ideas on this I'd be happy to try them out.
I haven't figured out yet how to automatically hydrate the objects when Nuxt serves the page on a reload. There just isn't a good hook to use for this. If Nuxt had a fetched()
hook that gets called after a fetch()
query completes, that would be a great place for this. I've tried adding a watcher on the Nuxt $fetchState
but that wasn't working for me.
I've instead adapted the clone/commit strategy with an "edit" toggle for the form or editor you're trying to use this clone/commit pattern with. I've taken the FeathersVuexFormWrapper and modified it. I apologize in advance if some of this is unclear. Its rough ideas for now
The utility function I wrote earlier:
import { hydrateObject } from "~/plugins/feathers";
// equates to
export const hydrateObject = function hydrateObject(obj, model) {
if (typeof model == 'string') {
model = models.api[model]
}
// return the feathers-vuex record from the obj's id.
if (obj.id != undefined && model != undefined) {
return model.getFromStore(obj.id)
}
}
additional props:
item: {
type: Object,
required: true
},
model: {
type: String || Object,
required: false
},
additional data:
data: () => ({
clone: null,
isEditing: false,
isHydrated: false
}),
additional computed props:
form() {
if (this.isHydrated) {
return this.clone;
} else {
return this.item;
}
},
and finally a new method to be exported. This is the most important because it turns the normal JS object into a model reference whenever the "edit" button is clicked.
edit() {
if (process.client) {
let modelInstance = hydrateObject(this.item, this.model);
if (modelInstance != undefined) {
this.clone = modelInstance.clone();
this.isHydrated = true;
this.isEditing = true;
}
}
},
and we export everything
render() {
const { form, edit, save, reset, remove, isDirty, isNew, isEditing } = this;
return this.$scopedSlots.default({
form,
edit,
save,
reset,
remove,
isDirty,
isNew,
isEditing
});
}
and usage would look like this:
<SSRFormWrapper :item="post" model="Post">
<template v-slot="{ form, save, reset, edit, isEditing }">
<div>
<v-editor v-model="form.content" :editable="isEditing"/>
<div v-if="isEditing">
<v-btn @click="save">Save</v-btn>
<v-btn @click="reset">Cancel</v-btn>
</div>
<div v-else>
<v-btn @click="edit">Edit</v-btn>
</div>
</div>
</template>
</SSRFormWrapper>
Hi all, I have expanded upon @tylermmorton 's workaround function a little with the following: 1) Handles already hydrated objects 2) Errors if an invalid modelName is passed. (Assumes always passing a string modelName)
export const hydrateObject = (obj, modelName) => {
// Already hydrated.
if (obj && obj.constructor && obj.constructor.name === modelName) {
return obj;
}
if (typeof modelName !== 'string' || !models.api[modelName]) {
throw new Error('Please pass a valid model name to hydrateObject');
}
const Model = models.api[modelName];
// Return the feathers-vuex record from the obj's id.
if (typeof obj.id !== 'undefined' && typeof Model !== 'undefined') {
return Model.getFromStore(obj.id);
}
return obj;
};
Steps to reproduce
/pages/post/_id.vue
Expected behavior
When Nuxt serves a page to the client on first load/page reload, objects received from FeathersVuex in asyncData should be model instances. Note that any subsequent loads through the Vue/Nuxt router in Universal or SPA mode are working as intended.
Actual behavior
The data loaded during asyncData is merged with the component's data field but loses its ties to the FeathersVuex store. Without those ties, functions like clone() or save() cannot be called. This also breaks components like FeathersVuexFormWrapper.
My findings & workaround
I've made a temporary solution to get me moving forward again with my project. On the client side only, whenever the component is created, I will individually hydrate each object using a function I exported from my feathers plugin.
The
hydrateObject
function will just return the corresponding record in the Vuex store based on the object's id and api passed in. I assume the object is already in the store (thanks to calls to hydrateApi on nuxtClientInit), so no redundant calls to the server will happen here. This seems to work OK at replacing the normal Object with a model instance but can be cumbersome when there's a lot of objects to hydrate:/plugins/feathers.js
/pages/post/_id.vue
Theres a couple of caveats I've run into with this solution, the main one being that if you are following the form binding "clone/commit" pattern or using the FeathersVuexFormWrapper, your data is not turned into a model instance until
created()
is called, causing any components that rely on the model instance functions to fail.Right now the only solution I can think of to this is to roll my own version of FeathersVuexFormWrapper that can take either a normal object or a model instance on page load, and hydrates the object if needed when created() on the client is called.
System configuration
I've set up my project as close to the FeathersVuex + Nuxt documentation as I can. When setting up the store, I've made sure to include the call to hydrateApi() provided by FeathersVuex, which seems to be hydrating the store data properly.
Module versions
NodeJS version: v10.7.0
Operating System: MacOS Mojave Version 10.14.5 (18F132)
Browser Version: Chrome Version 81.0.4044.129 (Official Build) (64-bit)
-- This may just be an issue with Nuxt, but I'm interested to see what others think of this.
Thanks in advance Tyler