plugin-graphql icon indicating copy to clipboard operation
plugin-graphql copied to clipboard

Problem to $persist

Open phiny1 opened this issue 4 years ago • 7 comments

I dont know why I cant persist and object... I got: fetch, push and destroy works well.

vuex-orm-graphql.es5.js?8eb2:1 Uncaught (in promise) TypeError: Cannot convert undefined or null to object
    at Function.keys (<anonymous>)
    at Function.e.transformOutgoingData (vuex-orm-graphql.es5.js?8eb2:1)
    at Function.e.addRecordToArgs (vuex-orm-graphql.es5.js?8eb2:1)
    at Function.eval (vuex-orm-graphql.es5.js?8eb2:1)
    at eval (vuex-orm-graphql.es5.js?8eb2:1)
    at Object.eval [as next] (vuex-orm-graphql.es5.js?8eb2:1)
    at eval (vuex-orm-graphql.es5.js?8eb2:1)
    at new Promise (<anonymous>)
    at __awaiter (vuex-orm-graphql.es5.js?8eb2:1)
    at Function.t.call (vuex-orm-graphql.es5.js?8eb2:1)

Sector,js

import { Model } from '@vuex-orm/core'

export default class Sector extends Model {
  static entity = 'sectors'

  static fields () {
    return {
      id: this.number(null),
      name: this.string(''),
    }
  }
}

store/index.js

import Vue from 'vue';
import Vuex from 'vuex';
import VuexORM from '@vuex-orm/core';
import VuexORMGraphQL from '@vuex-orm/plugin-graphql';
import { AuthModule } from './auth-module';
import { HttpModule } from './http-module';
import { SettingsModule } from './settings-module';
import { CustomAdapter } from '@/plugins/graphql-adapter';
import ApolloClient from '@/plugins/apollo-client';

// Create a new instance of Database.
const database = new VuexORM.Database();

// Models
import User from '@/models/User';
import Sector from '@/models/Sector';
import Subsector from '@/models/Subsector';
import Segment from '@/models/Segment';

// Register Models to Database.
database.register(User);
database.register(Sector);
database.register(Subsector);
database.register(Segment);

VuexORM.use(VuexORMGraphQL, { 
	database,
	adapter: new CustomAdapter(),
	apolloClient: ApolloClient
});
Vue.use(Vuex);

export default new Vuex.Store({
  plugins: [VuexORM.install(database)],
  modules: {
    auth: AuthModule,
    http: HttpModule,
    settings: SettingsModule
  },
});

Sector.vue

<script>
import Sector from '@/models/Sector';

export default {
	data() {
		return {
			sector: {},
		}
	},
	async mounted() {
		await Sector.fetch();
	},
	computed: {
		sectors: () => Sector.all(),
	},
	methods: {
		newf() {
			this.sector = {};
		},
		edit(sector) {
			this.sector = sector;
		},
		async create() {
			await Sector.insert({data: this.sector});
			const sector = Sector.query().last();
			await sector.$persist();
		},
		async update() {
			await this.sector.$push();
		},
		async destroy(sector) {
			await sector.$deleteAndDestroy();
		}
	}
};
</script>

phiny1 avatar Nov 19 '19 14:11 phiny1

For Vuex ORM the key of model is attribute "$id" as we can see in:

vuex-orm/query/Query.ts

private deleteById (id: string | number | (number | string)[]): Data.Item {
    const item = this.find(id)

    if (!item) {
      return null
    }

    return this.deleteByCondition(model => model.$id === item.$id)[0]
  }

When the model has increment id, its set to "$id" and "id" attributes the key value, but when the model doesnt have increment id , its set the fictitious key only to "$id" attribute.

Then, the plugin cant get data by "id" attribute on:

plugin-graphql/orm/model.ts

public getRecordWithId(id: number) {
    return this.baseModel
      .query()
      .withAllRecursive()
      .where("id", id)
      .first();
  }

In where should be "$id".

Another problem, its seems possible in Vuex ORM have more then one attribute as model key and plugin graphql is not prepered for this situation, as we can see in:

Vuex ORM/model/Model.ts

async $delete (): Promise<Item<this>> {
    const primaryKey = this.$primaryKey()

    if (!Array.isArray(primaryKey)) {
      return this.$dispatch('delete', this[primaryKey])
    }

    return this.$dispatch('delete', (model: this): boolean => {
      return primaryKey.every(id => model[id] === this[id])
    })
  }

phiny1 avatar Nov 20 '19 02:11 phiny1

@vuex-orm/core 0.34 solves the problem of null id, because Uid generate to $id and id attributes. But the plugin still dont work with composite keys

phiny1 avatar Nov 26 '19 12:11 phiny1

With the latest release, the plugin works with Vuex-ORM 0.36. Feel free to reopen the issue when the problem still exists :)

phortx avatar May 02 '20 20:05 phortx

Thank you very much for the update =]

phiny1 avatar May 03 '20 15:05 phiny1

This is still an issue as it tries to convert a string id into a number before performing the search query. https://github.com/vuex-orm/plugin-graphql/blob/50d0bb689bcc8dd7b0db4692ca51f2596621db6c/src/orm/model.ts#L267

M1chaelTran avatar May 20 '20 10:05 M1chaelTran

I am using string ids in many of my models. Hat to fork the Repo and remove all "toNumber" calls in the code. Hope it will be fixed soon

sysrun avatar Oct 27 '20 08:10 sysrun

With generated $id: "tyr13zqj" I have the same error Uncaught (in promise) TypeError: Cannot convert undefined or null to object.

Seems that now (vuex-orm/core 0.36.3) it not prefixed with $uid in models: id: this.uid(() => tempId()) // tempId returns 'tyr13zqj' value

But in plugin code we have

getRecordWithId(id) {
    return this.baseModel
        .query()
        .withAllRecursive()
        .where("id", toPrimaryKey(id))
        .first();
}

where toPrimaryKey is

function toPrimaryKey(input) {
    if (input === null)
        return 0;
    if (typeof input === "string" && (input.startsWith("$uid") || isGuid(input))) {
        return input;
    }
    return parseInt(input.toString(), 10);
}

Why you need that conversion in plugin? Just return string if that is string!

Arsync avatar Mar 21 '21 11:03 Arsync