lolmaus / ember-zen

MIT License
3 stars 0 forks source link

Project state #1

Open fpalluel opened 7 years ago

fpalluel commented 7 years ago

Hello, the ideas you describe in the Readme are just brilliant ! Ember really needs to implement the concepts that Redux brought to the community, but ember-redux forces us, Ember developpers, to embrace the whole Redux way of coding (a LOT of boilerplate code !). Personally, I came to Ember just because of that : focus on app functionality with the least amount of boilerplate code. Ember-zen is what we need ;-)

What is the project state ? Is it useable ? Thanks ;-)

lolmaus commented 7 years ago

Thx for the feedback, it's really motivating!

Not usable, I've just started working on it. Experimenting with API design, trying it out in a pet project.

Do you wanna contribute?

fpalluel commented 7 years ago

I'd be glad to help, though I don't know how ;-) Perhaps you should create a RFC and draw some attention from ember-redux authors ? After all, we share the same goal : bring Redux principles to Ember. And there shouldn't be a unique way to achieve this ;-)

lolmaus commented 7 years ago

I'd be glad to help, though I don't know how ;-)

We'll figure something out! I'll keep you in touch and share my progress, we'll see where it goes.

Perhaps you should create a RFC

Great idea, though I'd like to do some experimenting before formalizing the API: trying things out and seeing what's best rather than settling on a hypothetical solution.

draw some attention from ember-redux authors

I don't want to draw too much attention before I have a functioning implementation. Some people might dislike my idea because it's still raw, some may like the idea and make their independent implementation... I don't mind competition, I just want a headstart so that my personal effort is not wasted.

After all, we share the same goal : bring Redux principles to Ember

Actually, there's much more similarity with mobx-state-tree than Redux. Ember kinda has MobX built in already, so ember-zen is to Ember what mobx-state-tree is to MobX.

Actually, mobx-state-tree has much more to offer, e. g. static types, ember-zen isn't going to offer that. But core principles are the same. When I watched the official talk on mobx-state-tree, I was astonished how similar my ember-zen ideas are to theirs. Though implementation details are totally different, goals and values are the same.

fpalluel commented 7 years ago

Thanks for those clarifications ! I had heard of MobX but not specifically mobx-state-tree... Indeed its concept looks so close to yours ! Feel free to me keep in touch of the development of ember-zen ;-)

lolmaus commented 7 years ago

What I would like to discus is ways of mitigating the loss of Ember Data. ED has a lot of good stuff: network layer; serialization layer; querying; optional background fetches; dirty states with rollbacks and shadowing; relationships with auto bookkeeping, foreign key configuration, inverses, polymorphism; transforms...

Going Redux/Zen means losing all those conveniences. There of course are unopinionated libraries that can fill some of the gaps, but it means lots of glue code and no integrated experience.

How can we address that?

fpalluel commented 7 years ago

When searching about ember and redux, some time ago I stumbled upon this addon.

lolmaus commented 7 years ago

Ok, so here are some thoughts.

  1. In order to update the store, you run the dispatch method on the zen service with the following arguments:

    • node that you would like to update, or path to it (in which case the node must be a (sub)property on the service)
    • message, will be used in logs, should describe what you're doing shortly and meaningfully
    • a callback, inside which you're allowed to mutate the node or its child nodes
  2. There are convenience methods:

    • dispatchAction: instead of the callback, you pass an action name. Action is a method on the node, similar to component actions. Uses action name as message.
    • dispatchSet: similar to Ember.set, you pass a key and a value, the service assigns the value to the key on the node.
    • dispatchSetProperties: similar to Ember.setProperties, you pass a hash of key-value pairs.
  3. There should be corresponding template helpers. My prototype works like this:

    • {{action (dispatch-action) node actionName arg1 arg2}}
    • {{action (dispatch-set) node message key value}}
    • {{action (dispatch-set-properties) node message hash}}
  4. There's also a zen.dispatchPromise method. It is a Zen equivalent of Ember.PromiseProxyMixin. You give it a name and a callback that should return a promise. The method will set the properties ${name}IsPending, ${name}IsFulfilled, ${name}IsSettled, ${name}IsRejected, ${name}Response and ${name}Error on the node.

  5. All of those dispatch* methods are also available on nodes.

Problems with the above:

  1. Sometimes you want to pass a message with dispatch-action. Sometimes you don't want to pass a message with dispatch-set. Making the message a required param is bulky. Making a message optional is tricky (what would the template action look like?).

  2. A very common use case is to group multiple dispatches in a single method. The problem is that you can't call a method from a template. And if you make the method an action on a node, then you can call it, but it will also be logged by Zen. And current implementation first runs the action and then logs the result. This means that whan an action calls more actions, first the child actions will be logged and the parent action will come last, which makes little sense.

  3. dispatchPromise is awesome, but it requires the developer to declare all those attributes on the node by hand, which is tedious. One vector for solving this could be defining them dynamically in node's init, but that means more magic and less clarity. It's called Zen because it should give you sense of full control, not frustration.

Conceptual problems:

  1. Integration with ember-concurrency. I was going to offer some decorators for developers to be able to leverage e-c tasks in Zen nodes. But I've quickly realized this is gonna be problematic.

    The problem is that e-c's mission is to remove intermediate "occasional" state, while Zen's mission is to religiously maintain every single bit of state. This is a contradiction. Even though e-c developers are surprisingly open to suggestions, I can hardly imagine e-c delegating its state to Zen.

  2. Same with many other addons and even Ember's own features:

    • ember-simple-auth. Though here I was able to workaround the issue. My authenticator updates the session node every time it changes, so all other parts of the app read session from the node while e-s-a itself reads from its internal state.

    • Query params. The problem is that a query param is a property on a controller and there is no DDAU approach to update it. I. e. you can't define an action which would be invoked every time there's an attempt to update a query param.

      I've resolved this issue with an observer that dispatches an update from query param on controller to its counterpart in a Zen node. But ran into another issue. As the observer won't trigger unless you consume its property, you have to do it in init. But then the observer isthere is first run with a default value of a QP and then run again with a value from the URL. And there's no Controller.didReceiveQueryParams hook similar to Component.didReceiveAttrs!

      I've noticed that QPs are already resolved when controller passes the ball to the template. So I created a trigger helper that calls an action on the controller when rendered. I've called the action didReceiveQueryParams and use it instead of init to trigger the observer.

      This is all very complicated, and I struggle to find a way to make it easy to use.

  3. Without a state inspeciton and time-travelling GUI, Zen's usefulness is limited. Creating own GUI is an enormous task.

    I wish EmberInspector allowed addons to add custom panels, but I highly doubt that's even theoretically possible.

    We could integrate with ReduxDevTools, but I haven't found a guide on how their integration API works, and the API docs are hard to understand. Need help from someone who's faimilar with how ReduxDevTools integrates.

@Moxide So far you're the only one I can discus such matters with. What do you think?

lolmaus commented 7 years ago

Idea: allow node attributes to be aliases. This should resolve the problem with query params, ember-simple-auth and maybe ember-concurrency.

But it would require the developer to wrap changes of aliased properties in actions, e. g.:

// app/pods/index/controller.js

export Ember.Controller.extend({
  queryParams: ['sort'],
  sort: 'price:asc',

  actions: {
    changeSorting (field, order) {
      const sort = `${field}:${order}`

      this
        .get('zen')
        .dispatch('state.pods.index', () => {
          this.setProperties({sort})
        })
    },
  },
})
// app/pods/index/node.js

export Node.extend({
  indexController: Ember.inject.controller('index'),

  attrNames: ['sort'],
  sort: Ember.computed.alias('indexController.sort'),
})