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

Standing Challenges #140

Open
kumavis opened this issue Feb 19, 2015 · 19 comments
Open

Standing Challenges #140

kumavis opened this issue Feb 19, 2015 · 19 comments

Comments

@kumavis
Copy link
Collaborator

kumavis commented Feb 19, 2015

Here are a few things we're still trying to figure out how to do best, in no particular order

Animations / CSSTransitionGroup

e.g. a removal animation that keeps a removed item in the dom long enough to animate out.
mostly solved

Not duplicating state serialization

With shared state passed down the hierarchy
you end up with duplicated serialization

Rendering into modals or other places

You can put a render function or vdom into state to hand it off to a modal component, but it doesnt serialize.

Formalized component interface

If we can set expectations of component interface, it will be easier to make reusable pieces for the community.

Non-DOM events

Triggering events on components that don't come from the DOM. see thread.

cc: @neonstalwart @Raynos
some chat logs

@kuraga
Copy link
Collaborator

kuraga commented Mar 25, 2015

About the third and the fourth... Are there some techniques about immutables with object oriented programming?

We have a (static) render function with a state argument. Event handler is a (static) function with a state argument. Can we use classes here?

state is an immutable object - can't we have methods in it? But can we use classes here?

@kumavis
Copy link
Collaborator Author

kumavis commented Mar 25, 2015

I had an idea for Rendering into modals, but haven't tried it

If your components that want inject stuff into modals registered their modal content rendering functions at definition time, then they could set the content state of the modal-component to use their rendering function. Bad pseudo code:

var SaveChangesComponent = function(){
  app.modalController.register( 'save-changes', function(){ return h('.modal-content') } )

  var channels = {
    confirm: function(){ app.modalController.content.set('save-changes') }
  }
}

@kuraga
Copy link
Collaborator

kuraga commented Apr 4, 2015

Components... Is set of channels the one for the whole app? Or can events be component-specific?

@kumavis
Copy link
Collaborator Author

kumavis commented Apr 5, 2015

@neonstalwart differentiates between channels and events. Both are component specific and don't bubble.

@neonstalwart
Copy link
Collaborator

typically for generic components (like a text input component) they either generate generic events (like change) that a parent element can listen to (textInput.events.change(state.firstName.set)) or they accept an observable (like value) that will be updated by the input (state.firstNameEditor.set(textInput({ value: state.firstName }))). usually i've implemented both options but maybe i should just choose one and be consistent with it - that's the kind of thing to decide as part of trying to come up with a component API. if we can agree on a suitable API, i could probably start contributing a few generic components to use as a starting point.

i also use events for my application-level events (loginComponent.events.success(app.events.login) i.e. when a generic login component has a success event, trigger the more specific application-level login event) which is something that can't be done with channels since channels can only be triggered from DOM events. you may wonder why i have a generic loginComponent.events.success and a more specific app.events.login and the reason is that i prefer not to embed application logic directly in components if i can avoid it. this makes it easier to isolate components from the application logic when it comes to testing the components and it also means that i could have multiple places/components that may trigger the same application event - e.g. ctrl+s, File->Save menu, Save button, could all trigger app.events.save(thing) without embedding the save logic in any of those places.

Is set of channels the one for the whole app? Or can events be component-specific?

so, i have events for both - some are for the whole app and some are component-specific and the component-specific ones trigger the ones for the whole app.

@kuraga i don't know if i understand your question properly... no part of that line of code is a handler.

  • textInput is a constructor/factory for a text input component
  • state is a parent component which embeds a textInput component.
  • state.firstNameEditor is the embedded state of the textInput component
  • state.firstName is the observable value for a firstName which is shared between state and state.firstNameEditor - i.e. state.fistNameEditor.value === state.firstName
  • i'm assuming this code is being run in a parentComponent constructor/factory e.g. (i've expanded the one line above into the following few lines to add more context)
function parentComponent(options) {
    var events = hg.input([ ... ]),
        firstName = hg.value(options.firstName),
        state = hg.struct({
            events: events,
            firstName: firstName,
            // equivalent to: state.firstNameEditor.set(textInput({ value: state.firstName }))
            firstNameEditor: textInput({ value: firstName })
        }),
        ...

    return state;
})

does that help at all?

@kuraga
Copy link
Collaborator

kuraga commented Apr 6, 2015

@neonstalwart thanks, very useful! But sorry my question/thought is different.

Is here state - state of the whole app, or is it component's part only?

Acccording to this, context (i.e. state above) is set by channels function. Who does call it? state function does and nobody calls it in examples explicity.

So, channels' context is set by state function. And we're calling it when we're creating whole app state. So, here state is state of the whole app, it's not component specific.

Correct?

@kumavis
Copy link
Collaborator Author

kumavis commented Apr 7, 2015

@kuraga are you talking about my example code or...

@kuraga
Copy link
Collaborator

kuraga commented Apr 7, 2015

@kumavis no. See links in the message.

P.S. Originally it wasn't a FAQ, I wanted to talk about "good way to isolate parts of state when handling a channel event". But seems like we don't understand each other. Just re-read my last message, please... Thanks.

@neonstalwart
Copy link
Collaborator

Is here state - state of the whole app, or is it component's part only?

the example is trivial so it's no surprise that it doesn't help answer the question. the general pattern would be that state is a single component's state. in such a trivial example, the whole application is just one component so that example doesn't help clarify whether this pattern is for the whole app or just a single component.

Acccording to this, context (i.e. state above) is set by channels function. Who does call it? state function does and nobody calls it in examples explicity.

right, again, trivial examples are misleading. think of that hg.state function as a component factory which wires together state and channels. the todomvc example is less trivial and it shows a TodoItem component which uses hg.state to produce a component. the state which gets passed to each channel is the relevant state for a single TodoItem. at the application level, TodoApp also uses hg.state but embeds a number of TodoItem states at state.todos

So, channels' context is set by state function. And we're calling it when we're creating whole app state. So, here state is state of the whole app, it's not component specific.

this is the beauty of all of this... a simple pattern is applicable to a single component and to the whole application (which is really just another component). so, there is no rule that hg.state is only for components or only for the whole app - it is a pattern that can be applied at the macro and micro level because the idea of a component is applicable at both levels. everything is a component.

@kuraga
Copy link
Collaborator

kuraga commented Apr 7, 2015

@neonstalwart

the todomvc example is less trivial and it shows a TodoItem component which uses hg.state to produce a component. the state which gets passed to each channel is the relevant state for a single TodoItem. at the application level, TodoApp also uses hg.state but embeds a number of TodoItem states at state.todos

This is a key. Thanks very much! So, there were nothinng to discuss - it was my misunderstanding only. Feel free to clean up our messages. Thanks!

@alexmingoia
Copy link

Can we add routing to the list of standing challenges? Switch statements in views leads to lots of duplicated code... IMO component-based routing approaches like react-router avoid this problem.

@Raynos
Copy link
Owner

Raynos commented Apr 22, 2015

@crabmusket
Copy link
Contributor

Rendering into modals or other places

You can put a render function or vdom into state to hand it off to a modal component, but it doesnt serialize.

Why would you ever need to change the contents? If you need two different modal contents, render two modals and enable whichever of them is appropriate.

@kumavis
Copy link
Collaborator Author

kumavis commented Oct 30, 2015

Why would you ever need to change the contents?

its more about a deeper component (widgets list) interacting with a component higher in the DOM ( widgets deletion confirm modal )

Right now im thinking of a top-level modal controller that other components that need modals register with when they are defined. Having a single modal controller also guarantees you wont endup with two modals showing at once, overlapping.

@crabmusket
Copy link
Contributor

Something I've come to realise is related to #132. Mercury doesn't just have a problem dealing with non-DOM events, but also non-DOM rendering. For example, you could view notifications as a result of rendering your application state, but virtual-dom doesn't include notifications, so you have to manage them manually somehow. Similarly, network calls are a result of 'rendering' your application state to the network, and the events that come back from the network are analogous to DOM events.

Having a single modal controller also guarantees you wont endup with two modals showing at once, overlapping.

Fair enough, but if that happens unintentionally you probably have a bug elsewhere you'd need to fix anyway.

@kumavis
Copy link
Collaborator Author

kumavis commented Nov 3, 2015

@eightyeight very insightful, hadnt thought of notifications / network requests as a sort of rendering

@crabmusket
Copy link
Contributor

I've been trying to attach audio to the page by rendering an <audio> tag when certain parts of the state exist, but ran into the problem that requestAnimationFrame is often paused when a tab is unfocused. If you're using audio as a notification of, for example, chat messages appearing, then rendering audio as part of your application rendering isn't going to cut it, because that rendering won't happen in the RAF until the user visits the application tab.

I haven't decided how we'll solve this yet; probably just hang a listener on the app state that uses the Audio API directly, rather than rendering an audio tag. Not sure if this is enough of a problem to warrant thoughts about an entire non-DOM 'rendering' pipeline.

@yoshuawuyts
Copy link

just hang a listener on the app state that uses the Audio API directly

Though it's a good idea I believe the Audio API exposes a subset of what an audio tag is capable of doing, so attaching a tag might be what you want to do regardless. I think having a direct listener on the state outside mercury might be the best solution for this.

@crabmusket
Copy link
Contributor

The audio API does enough for our purposes that I don't want to bother with the DOM.

Side-note, observ makes it really easy to listen to state changes accurately, versus Angular's listeners which basically just dump the entire new object on you and let you diff it yourself.

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

No branches or pull requests

7 participants