michaelolof / vuex-class-component

A Type Safe Vuex Module or Store Using ES6 Classes and ES7 Decorators written in TypeScript.
217 stars 21 forks source link

Feature Request: A Bind decorator for class components #88

Open HeshamMeneisi opened 4 years ago

HeshamMeneisi commented 4 years ago

I really like the idea of putting a proxy over the stores (vxm) and abstracting all the mutations and mapX decorators. I found your package while doing research on how to achieve this myself and decided to use it (nice work btw, very neat). However, in order to achieve 2-way binding, I realized I had to define getters and setters on the component like so:

get foo {
    return vxm.example.foo;
}
set foo(value: string){
    vxm.example.foo = value;
}

Not bad given the usual boilerplate required to do that, but I decided to go one step further and just define the store module on the component (would be nice to include this in README):

private example = vxm.example;

However, this meant that every time I need to use a field, I would have to address it with the full path (e.g. example.foo). So, I came up with the following decorator:

export function Bind(module: Record<string, any>, field: string) {
  return function(target: Vue, propertyKey: string) {
    const getter = function() {
      return module[field];
    };
    const setter = function(newVal: any) {
      module[field] = newVal
    };
    Object.defineProperty(target, propertyKey, {
      get: getter,
      set: setter
    });
  }
}

In the class component:

@Bind(vxm.example, "foo") private foo: string;

In the template:

<VTextField v-model="foo" />

This does the job but it's just too loose. There's no guarantee that the types are the same or that this field exists in the store at all so you will only find out you made a mistake during runtime. Any way to make it cleaner?

Ideally, it should be something like this:

@Bind(vxm.example.foo) private foo;

However, this requires that foo is passed by reference, which can only be achieved if there's a proxy over primitive fields and I assume this isn't exactly performance friendly (I might be wrong about this), so to dodge that maybe something like:

@Bind(vxm.example.bindable.foo) private foo;

Which can perhaps be achieved with an extending submodule?

What do you think? Just over-engineering or a nice feature to add? I haven't read your code yet so there might be a much easier way to do this.