Skip to content
This repository has been archived by the owner on Jun 16, 2020. It is now read-only.

Non-DOM events #132

Open
kuraga opened this issue Feb 13, 2015 · 26 comments
Open

Non-DOM events #132

kuraga opened this issue Feb 13, 2015 · 26 comments
Labels

Comments

@kuraga
Copy link
Collaborator

kuraga commented Feb 13, 2015

Can I trigger an event outside of template?

var app = App();
mercury.app(document.body, app, App.render);
mercury.send(app.channels.checkDemonState); // doesn't work
@kumavis
Copy link
Collaborator

kumavis commented Feb 13, 2015

@neonstalwart was talking about using 'events' instead of 'channels' but im not sure how he did this

@neonstalwart
Copy link
Collaborator

correct - the function associated with a channel can only be triggered from dom-delegator. only dom-delegator has access to the handler store that uses the handle as a key to get the function associated with the handle.

if you want something that will be triggered by the DOM and give you the ability to trigger it yourself, use hg.input (which is what becomes the events key in the state of the markdown example)

@kumavis kumavis added the FAQ label Feb 13, 2015
@neonstalwart
Copy link
Collaborator

hg.input is marked as deprecated but i think it's still a necessary part of inter-component communication. channels are strongly tied to the DOM and i think that's not a good thing.

function App() {
  var state hg.struct({
    events: hg.input([ 'checkDemonState' ]),
    // ...
  });

  state.events.checkDemonState(function (thing) {
    console.log(thing);
  });

  return state;
}

var app = App();
hg.app(document.body, app, App.render);
app.events.checkDemonState('hello world');

@kumavis
Copy link
Collaborator

kumavis commented Feb 13, 2015

@Raynos thoughts? why did you mark it as deprecated?

@kuraga
Copy link
Collaborator Author

kuraga commented Feb 13, 2015

Thanks for everybody! I see one more difference than hg.channels and hg.input. That's hg.struct(...) and hg.state(...) as an app. Can you explain please? Thanks.

@neonstalwart
Copy link
Collaborator

@kuraga hg.state is just a helper that wires things up for you. i have a similar helper i call autoWire

function autoWire(state, events, update) {
    Object.keys(events).forEach(function (key) {
        var respond = update[key];

        if (respond) {
            events[key](respond.bind(null, state));
        }
    });
};

you use it like this:

var update = {
  checkDemonState: function (state, thing) {
    console.log(thing);
  }
};

function App() {
  var state = hg.struct({
    events: hg.input([ 'checkDemonState' ]),
    // ...
  });

  autoWire(state, state.events, update);

  return state;
});

back to channels though... channels are a way to guarantee that your handler can only be triggered by a DOM event. with that guarantee comes the restriction that your handler can only be triggered by a DOM event 😄

maybe i'm undisciplined but i find it very useful to be able to trigger events without necessarily having a user interaction or be able to translate a DOM event handled on one component to an event that is part of another component.

an example of doing something without a DOM event might be a toaster component that shows messages to a user and allows the user to click 'X' to dismiss the message or after a few seconds it closes itself (without a DOM event). in both cases i would want toaster.events.dismiss to be called since that is my way to encapsulate the logic of dismissing the toaster. i could achieve this with channels and some indirection where toaster.channel.dismiss calls toaster.events.dismiss and have the timer call toaster.events.dismiss directly but i'm ok with not having that extra indirection and just using hg.input everywhere.

@kuraga
Copy link
Collaborator Author

kuraga commented Feb 13, 2015

Nice hook!

Unify channels and input maybe?

@kuraga
Copy link
Collaborator Author

kuraga commented Feb 15, 2015

@Raynos
...

why did you mark input as deprecated?

...

Can't we unify channels and input?

@neonstalwart
Copy link
Collaborator

I don't know if there's much to unify. input is more or less a superset of channel since everything you can do with channels you can do with input except for the guarantee that an event was generated by a DOM event if you use channels. so ultimately it's just your choice of which one you need.

@neonstalwart
Copy link
Collaborator

to put it another way, the things which are different about channel and input is not something which can be unified because the unique part of each one is incompatible with the unique part of the other.

@Raynos
Copy link
Owner

Raynos commented Feb 15, 2015

why did you mark input as deprecated?

Because input was never meant to be written to by other components. It's a convenient way to allow siblings to communicate and to allow grandchildren & grandparents to communicate.

What I really wanted input to be, is channels, channels are only for DOM events.

The reason inputs won't work is because they cannot be serialized, one of the less documented design goals of mercury and mercury applications is to be able to serialize the entire application state into a blob, save it to disk or share the blob with a friend over IM, then load it and be at the correct application state.

This means that the event communication system we use between multiple mercury components must be resilient to serialization, either by being statically configured at startup and not being dynamic or being serializable itself.

This is an open problem that is not solved yet, I started experimenting with a solution here ( https://github.com/Raynos/mercury/blob/master/examples/todomvc/todo-app.js#L34-L36 ) but it's not proven yet.

@Raynos
Copy link
Owner

Raynos commented Feb 15, 2015

Can't we unify channels and input?

See @neonstalwart answer. They do different things. We need to find something like input but ensure its serializable.

Although if you do not care about serialization you can always just use input.

@kuraga
Copy link
Collaborator Author

kuraga commented Feb 15, 2015

Thanks to all!

@kuraga kuraga closed this as completed Feb 15, 2015
@kumavis
Copy link
Collaborator

kumavis commented Feb 18, 2015

@Raynos I guess I don't understand how using events violates serializability of state. Are you trying to cut out a class of subtle hidden-state bugs? or what is your thinking there?

@Raynos
Copy link
Owner

Raynos commented Feb 18, 2015

@kumavis

Imagine if you have an empty app. You then serialize a very complex state into the app.

You now have a ton of state, but no constructor or factory functions were called.

There are functions all over the place that set the event relationships with the mercury.input() between two mercury components.

Every time you saturate a complex data structure into your app you have to make sure all the event communication listeners and emitters are set up correctly, this is non trivial.

@kumavis
Copy link
Collaborator

kumavis commented Feb 18, 2015

@Raynos wouldnt setting up the event listeners be a natural product of defining the parts and passing the state through the component hierarchy ?

@kumavis
Copy link
Collaborator

kumavis commented Feb 18, 2015

maybe i need a more concrete failure case to wrap my head around the aversion

@neonstalwart
Copy link
Collaborator

maybe i need a more concrete failure case to wrap my head around the aversion

👍

@Raynos
Copy link
Owner

Raynos commented Feb 18, 2015

@kumavis think of the todomvc case.

It has a list of todos.

Each time it creates a todo it pushes it into the list and listens for events on the todo item.

It only listens for events when it pushes the todo item into the list.

How does it listen for events when the entire list is hydrated with the new state.

There are just so many edge cases around dynamic components, lists of child components, hashes of child componenets.

@kumavis
Copy link
Collaborator

kumavis commented Feb 18, 2015

@Raynos wouldn't it start listening for events as part of the instantiation process of each child element?

Let's say each item in the list represents a twitter tweet, and it listens for 'wasStarred' events over the network to update itself. The TweetComponent would setup a new network i/o listener on instantiation. no?

@Raynos
Copy link
Owner

Raynos commented Feb 18, 2015

@kumavis your assuming we create a tweetcomponent.

When we hydrate an array we just put data into the array and that's it. At which point do we convert plain data structures into instances of TweetComponent ?

@kumavis
Copy link
Collaborator

kumavis commented Feb 18, 2015

yeah you're right...

we'd need something like

tweets: hg.componentMap( TweetComponent, initState.tweets )

where componentMap handles life cycle of array element - component matching

@Raynos
Copy link
Owner

Raynos commented Feb 18, 2015

@kumavis so instead of doing that I have EventMap which handles listening for events on a map of components.

@kuraga
Copy link
Collaborator Author

kuraga commented Mar 1, 2015

After (start of) using mercury in my project - seems like @neonstalwart 's autoWire function from here is not needless. Let's include it?

Repeat it:

function wire(state, events, update) {
    Object.keys(events).forEach(function (key) {
        var respond = update[key];

        if (respond) {
            events[key](respond.bind(null, state));
        }
    });
};

@Raynos
Copy link
Owner

Raynos commented Mar 1, 2015

No. hg.event() has to be replaced. It's staying in mercury until we have a better solution.

@kumavis kumavis reopened this Jul 5, 2015
@kumavis kumavis changed the title FAQ: Can I trigger an event outside of template? Non-DOM events Jul 5, 2015
@crabmusket
Copy link
Contributor

What's the standard way to respond to events such as websockets or AJAX? Simply go with input for now? Same question for testing - how would you test a channel in a situation without DOM? Or should all tests simply involve a DOM and rendering?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants