davestewart / vuex-pathify

Vue / Vuex plugin providing a unified path syntax to Vuex stores
https://davestewart.github.io/vuex-pathify
MIT License
1.37k stars 57 forks source link

Having problems with sync() and object syntax #103

Open Peppe87 opened 4 years ago

Peppe87 commented 4 years ago

Hi,

I cannot make sync work with a nested object.

In my store, inside order.js I have:

import { make } from "vuex-pathify";

const state = {
  order: {},
};

const mutations = make.mutations(state);
const defaultActions = make.actions(state);
const actions = {
  ...defaultActions,
  setOrderItemQuantity: async (foo, payload) => {
    //todo
    console.log(foo);
    console.log(payload);
  },
};
const getters = make.getters(state);

export default {
  state,
  mutations,
  actions,
  getters,
};

inside the index.js

import Vue from "vue";
import Vuex from "vuex";
import order from "./Modules/order";
import pathify from "@/plugins/pathify";
import * as restActions from "./Modules/restActions";

const store = {
  state: {},
  mutations: {},
  actions: { ...restActions },
  modules: {
    order: {
      namespaced: true,
      modules: {
        order,
      },
    },
  },
};

Vue.use(Vuex);
export default new Vuex.Store({
  plugins: [pathify.plugin],
  ...store,
});

The order object loaded inside the state (via a rest action) is like this:

order = {
  uuid: 1,
  id: 1,
 warranty: true,
  orderItems: [
    {
      id: 1,
      title: 'myTitle',
      quantity: 10,
      totalPrice: 100,
      unitPrice: 10,
    },
    {
      // other orderItem
    },
    {
      // another one
    }
  ]
};

Then in my component I'm trying to set a computed property for the v-model as seen in the doc\examples:

[...]
export default {
[...]
  data() {
    return {
      index: 0 //this will be dynamic
    };
  },
  computed: {
    quantitySelected: sync("order/order@orderItems[:index].quantity|setOrderItemQuantity")
  }
}

But that is not working.

What I've discovered so far:

1) quantitySelected: get("order/order@orderItems[0].quantity does work (I can print 10) 2) quantitySelected: get("order/order@orderItems[:index].quantity|setOrderItemQuantity") does not work

Any idea what I'm doing wrong? Thank you

Peppe87 commented 4 years ago

I've another unrelated problem with sync(): I've added another custom action to my order.js store, so now it is like:

import { make } from "vuex-pathify";

const state = {
  order: {},
};

const mutations = make.mutations(state);
const defaultActions = make.actions(state);
const actions = {
  ...defaultActions,
  setOrderShippingWarranty: async () => {
   //custom action
  },
  setOrderItemQuantity: async (foo, payload) => {
  //custom action
  },
};
const getters = make.getters(state);

export default {
  state,
  mutations,
  actions,
  getters,
};

Now I would like to use sync() in a component to set up the 2 way data binding calling the custom action but I can't understand how should do it. I thought it would be:

  computed: {
    shippingWarranty: sync(
      "order/order@shippingWarranty|order/setOrderShippingWarranty"
    )
  }

But that gives the error :

vuex-pathify.esm.js?7ffd:417 [Vuex Pathify] Unable to create sub-property for path 'order/order@order/setOrderShippingWarranty!':

  • Set option 'deep' to 2 to allow it

(after I set up the option I've seen that doesn't lead to the correct result anyway)

  computed: {
    shippingWarranty: sync(
      "order/order@shippingWarranty|setOrderShippingWarranty"
    )
  }

Just add setOrderShippingWarranty = true\false to the order object.

Therefore...What is the correct syntax to make sync() works in this case? THank you

davestewart commented 4 years ago

Hey,

Sorry I missed this!

In the first example, you seem to have two layers of modules:

modules: {
    order: {
      namespaced: true,
      modules: {
        order,
      },
    },
  },

This should just be:

const order = {
  namespaced: true,
  ... // other properties
}

modules: {
    order
  },

Additionally, you seem to have order inside state:

const state = {
  order: {},
};

So I would get your module nesting correct using Vuex, before even looking at Pathify.

I'll leave this open in case you need an extra pair of eyes, but this doesn't look like a Pathify issue... yet.

davestewart commented 4 years ago

Also:

quantitySelected: get("order/order@orderItems[:index].quantity|setOrderItemQuantity")

You have to think through in your head what the syntax resolves to:

order/order@orderItems[:index].quantity

This is going to be getting an array item from within a sub-property of the order state of the order module.

I'm not sure why setOrderItemQuantity is here. The pipe character is designed to give you an alternate method when using sync, but you are using get:

https://davestewart.github.io/vuex-pathify/#/api/component?id=sync

If you need to sync, use sync. If this is a mistake, fix the syntax.

Pathify is designed mainly to simplify the wiring for 1:1 relationships with the. I'd suggest if you have something a little more complex either:

By the way, I appreciate (in hindsight) the docs are not the easiest to navigate, there's a lot of reading, and it's not easy to just look things up via the API. This may change at some point in the future!

Peppe87 commented 4 years ago

Thank you for you answer,

In the first example, you seem to have two layers of modules:

modules: {
    order: {
      namespaced: true,
      modules: {
        order,
      },
    },
  },

This should just be:

const order = {
  namespaced: true,
  ... // other properties
}

modules: {
    order
  },

Yeah, you're totally right, that was a mistake, I've corrected that.


Additionally, you seem to have order inside state:

const state = {
  order: {},
};

Perhaps you've been confused by the nomenclature of my variables? I've named the "object" inside the state "order" as the module, but just for coherence.

I mean, I could just as well call it:

 const state = {
   myCustomObject: {},
 };

I'm not sure why setOrderItemQuantity is here. The pipe character is designed to give you an alternate method when using sync, but you are using get:

A typo: I tried

quantitySelected: get("order/order@orderItems[:index])

insted of using sync(getter/setAction) as a test to check if a getter with a variable :index worked, and it did not work for me (because I was combining with sync and had not idea what was working and what not with that combination).

However, it does work now that I've corrected the wrongly nested module, as you pointed out, thank you.


What I can't still get to work it is calling a sync(get|setAction), I'm writing in a separate following comment for clarity's sake.

Peppe87 commented 4 years ago

Current situation:

I've updated my vuex index.js to fix the nested module mistake:

import Vue from "vue";
import Vuex from "vuex";
import order from "./Modules/order";
import pathify from "@/plugins/pathify";
import * as restActions from "./Modules/restActions";

const store = {
  state: {},
  mutations: {},
  actions: { ...restActions },
  modules: {
    order: {
      namespaced: true,
      ...order
    }
  }
};

Vue.use(Vuex);
export default new Vuex.Store({
  plugins: [pathify.plugin],
  ...store
});

My order.js is still:

import { make } from "vuex-pathify";

const state = {
  order: null
};

const mutations = make.mutations(state);
const defaultActions = make.actions(state);
const actions = {
  ...defaultActions,
  setOrderShippingWarranty: async ({ dispatch }, payload) => {
    //custom action
  },
  setOrderItemQuantity: async ({ dispatch }, payload) => {
    //custom action
  }
};
const getters = make.getters(state);

export default {
  state,
  mutations,
  actions,
  getters
};

(the order object in the state is set up via a custom action when the App is mounted).

In a component, I'm trying to set up a 2 way data binding with

computed: {
    shippingWarranty: sync(
      "order/order@shippingWarranty|order/setOrderShippingWarranty"
    )
  }

The "get" works as I get the correct value, while the action doesn't, as I get the error describe in previous comment:

vuex-pathify.esm.js?7ffd:417 [Vuex Pathify] Unable to create sub-property for path 'order/order@order/setOrderShippingWarranty!':

  • Set option 'deep' to 2 to allow it

Could you see what I am doing wrongly? Thank you.

davestewart commented 4 years ago

A couple of code comments before I get started:

So I'm not 100% sure that pathify supports sync with actions (it was designed for commits), even though Pathify has "accessor priority".

If you can set up a simple Code Sandbox (creating a new issues should provide the links) I will be happy to take a look.

If it does, I suspect the code will be:

module  
  |  property                   
  |     |     sub-property  
  |     |           |                 action
  |     |           |                   |
  v     v           v                   v
order/order@shippingWarranty|setOrderShippingWarranty"

Sorry I don't sound so certain - this lib just runs itself these days and I rarely look at the code!

EDIT: actually, I've just seen what might be the problem. The error message actually tells you.

Did you configure the library before using it?

See: https://davestewart.github.io/vuex-pathify/#/setup/config

Set deep to 2 and it should work (also, it looks like the docs are inconsistent; the default for deep is 1 not true. I will raise a ticket).

davestewart commented 4 years ago

Any luck @Peppe87 ?

Peppe87 commented 4 years ago

Hi,

Sorry for the delay but In the last working days I had to focus on another project unrelated on Vue. Next week I'll check again the deep config to be sure (but previously I tried it and it did not work) and eventually set up a code sandbox to example my situation.

I'll notify you as soon I'll do that, thank you again for the support

Peppe87 commented 4 years ago

@davestewart

I've tried the set deep options = 2 but it does not solve my problem. As far as I can understand, is how you said: the sync doesn't "recognize" the custom action I've created.

Anyway I've created a mockup of what I'm trying to do in my project: https://codesandbox.io/s/pathify-sync-action-test-tw4h9

I'd be grateful if you could check it when possible, thank you.

Peppe87 commented 4 years ago

@davestewart any chances you checked this issue?

davestewart commented 4 years ago

Hey, sorry for the long wait! I've had quite a busy couple of weeks myself.

I'll look into this tomorrow

Peppe87 commented 4 years ago

@davestewart any chances you looked to this issue? Thanks

davestewart commented 4 years ago

Still no time... really sorry!

I'm launching a big project I've spent the last year working on and all OSS is on hold until then.

I REALLY want to take some time to attend to the various issues though, so out of sight but not out of mind.

Fingers crossed next couple of weeks...

juangalbea commented 2 years ago

You said this is not working:

export default {
[...]
  data() {
    return {
      index: 0 //this will be dynamic
    };
  },
  computed: {
    quantitySelected: sync("order/order@orderItems[:index].quantity|setOrderItemQuantity")
  }
}

do this instead:

export default {
[...]
  data() {
    return {
      index: 0, //this will be dynamic
      name: 'order'
    };
  },
  computed: {
    quantitySelected: sync("order/:name@orderItems[:index].quantity|setOrderItemQuantity")
  }
}
adamcrown commented 2 years ago

@juangalbea That works! Why does it work!?

This was broken for me:

data() { return { } },
computed: sync('users/users@:id', [...])

and this worked:

data() { return { storeRootAttr: 'users } },
computed: sync('users/:storeRootAttr@:id', [...])

What a weird bug. Thanks for helping me find this workaround. I hope a permanent fix can come soon.