feathersjs-ecosystem / feathers-vuex

Integration of FeathersJS, Vue, and Nuxt for the artisan developer
https://vuex.feathersjs.com
MIT License
445 stars 110 forks source link

Cloned item is empty #542

Open Thomas-git opened 3 years ago

Thomas-git commented 3 years ago

Steps to reproduce

I use FeathersVuexFind to query database. Everything is fine.

         <FeathersVuexFind
            v-slot="{ items: languages }"
            service="languages"
            :query="{ $eager:'countries' }"
            watch="query"
          >
            <q-list>
              <q-item-label header>Langues</q-item-label>

              <q-item clickable v-ripple v-for="lang in languages" :key="lang.language_code">

                <q-item-section>
                  {{ lang.french_name }}
                </q-item-section>

                <q-item-section side>
                  <q-btn @click.stop="removeLanguage(lang)"/>
                </q-item-section>
              </q-item>

            </q-list>
          </FeathersVuexFind>

Problem arise in removeLanguage(lang) When I do

const clone = lang.clone()

clone is empty

I also tried to get a fresh copy from the store with const clone = Languages.getFromStore(lang.language_code).clone(). But got the same empty clone.

The Languages model idField is set to language_code in the service :

const servicePath = 'languages'
const servicePlugin = makeServicePlugin({
  Model: Languages,
  service: feathersClient.service(servicePath),
  paramsForServer: ['$eager', '$joinRelation', '$joinEager'],
  pagination: {
    limit: 50
  },
  idField: 'language_code',
  debug: true,
  servicePath
})

Expected behavior

I expected the clone to be... a clone. With same properties. Goal here is to allow user to cancel his action. So I would like to keep a copy of the deleted object. Obviously, it fails because my clone is empty. That's not my first time using feathersVuex. But the first time I use a custom idField. Maybe it's related ?

Actual behavior

The clone as none of the properties of the cloned Languages model. At first I thought it was related to #226 but I'm not using a select as you can see. Source object

Languages {__ob__: Observer}
countries: Array(0)
french_name: "Test"
language_code: "xx"
name: (...)
__ob__: Observer {value: Languages, dep: Dep, vmCount: 0}
get countries: ƒ reactiveGetter()
set countries: ƒ reactiveSetter(newVal)
get french_name: ƒ reactiveGetter()
set french_name: ƒ reactiveSetter(newVal)
get language_code: ƒ reactiveGetter()
set language_code: ƒ reactiveSetter(newVal)
get name: ƒ reactiveGetter()
set name: ƒ reactiveSetter(newVal)
__proto__: BaseModel

Cloned object

Languages {__isClone: true, __ob__: Observer}
__isClone: true
__ob__: Observer {value: Languages, dep: Dep, vmCount: 0}
__proto__: BaseModel

System configuration

Module versions (especially the part that's not working):

NodeJS version: Electron 9.3

Operating System: Archlinux

Browser Version: Electron 9.3

React Native Version: No. Using Quasar 1.14

Module Loader: Webpack 4.44

marshallswain commented 3 years ago

This is likely related to the custom idField. Can you set a break point in the clone mutation and get a better idea of what's going on?

Thomas-git commented 3 years ago

Ok, After first line in .clone() const { idField, tempIdField } = this.constructor idField === 'id' so it does not start good. and on const id = ... id is undefined.

Where should the idField be extracted in the first place ?

marshallswain commented 3 years ago

The idField gets placed on the Model class when passed in the options: https://github.com/feathersjs-ecosystem/feathers-vuex/blob/master/src/service-module/make-base-model.ts#L77.

You might need to verify that none of your service/model files has a duplicate service name or duplicate modelName attribute set. What does your Language class look like?

Thomas-git commented 3 years ago

So, there is no duplicate service name nor modelName If I add a static idField = 'language_code' in Languages model class, it works as expected. But if I understand the docs well, idField should be set in the service options when using makeServicePlugin. Again in the docs, I understand that modelName is also an option of makeServicePlugin. But if I do that, I have an error: Error: The modelName property is required for Feathers-Vuex Models

Note that it feels more natural to me that idField be specified in the Model class and not in service definition. That's what is done when defining models server side using feathers-objection db adapter using idColumn static property.

To answer your question, here is my Languages class

class Languages extends BaseModel {
  static modelName = 'languages'

  /*  static idField = 'language_code' */

  // Define default properties here
  static instanceDefaults () {
    return {
    }
  }

  static setupInstance (data, { store, models }) {
    if (data.countries) {
      // Turn countries into an array of Countries instances
      data.countries = data.countries.map(data => new models.health.countries(data)) // eslint-disable-line new-cap
    }
    return data
  }
}

export { Languages }

So am I faulty here, or there is something wrong in docs or in feathers-vuex code ?

marshallswain commented 3 years ago

Hmm... This appears to be a bug. Since we have a workaround I won't focus on fixing this for a little while. I'll leave this open to make sure we get to it. Thanks for reporting!