peerlibrary / meteor-blaze-components

Reusable components for Blaze
http://components.meteorapp.com/
BSD 3-Clause "New" or "Revised" License
354 stars 26 forks source link

Give feedback on Blaze Components #80

Closed mitar closed 8 years ago

mitar commented 8 years ago

I am asking the community to give some feedback on how you are using Blaze Components. Examples of apps you build them? Patterns you like? But most importantly what are possible issues you are having? All that would help guide the future of the project.

mquandalle commented 8 years ago

Hi Mitar,

First thank you for blaze-components! I used it for [Wekan]() which is the kind of application that inherently requires a number of UI components to work together (with the ability to compose them) and Blaze didn’t have the long awaited API to handle that. I've been considering the switch to React (and I still do, see https://github.com/wekan/wekan/issues/299) but React still misses a good integration with the Meteor ecosystem, and keeping the Blaze runtime was easier. So I decided to give Blaze-components a try.

I've been pretty happy with that choice. Since you are asking for issues, here are the one I encountered:

Max

bensmeets commented 8 years ago

The main reason to use it for me personally is the OOP approach of coding. It's what felt natural (and logical) to me. I don't use mixins at all though, although I appreciate what their goal and meaning are, I just didn't felt the need and rather went for an inheritance approach.

Something that might become more clear once I start using BC's in more specific use cases, but for now what feels "fuzzy" is a right way for inter-component communication. I come from an Adobe Flex/AS3 world. Where a lot of these challenges have been fixed by relying on event dispatching a lot more. I know BC's support all this as well at it's core, but falls back to DOM events. Just like Blaze (I guess? Correct me if I'm wrong). So something that would allow for direct events/listeners on component level would feel comfortable for me (taking my background into account).

A small (very small) "huh" moment I had, was why I need to register components after defining them. But that might just be js-related.

Cheers!

mitar commented 8 years ago

Thanks for the feedback! My thoughts:

With Blaze I have to rely on the blaze data context (eg ../members) which was rely fragile during the UI refactoring — modifying one thing somewhere always broken something else somewhere else.

That is so interesting to me that people have so much issues with this. I must say that I do not get it. :-) I think maybe the issue is that I have not made it clear enough what is the way to use that in Blaze Components, over Blaze, and people get stuck with Blaze issues? I really do not get the issue. :-)

See my comment here for another take on answer this concern.

The point with Blaze Components is that you should not be using ../members to access the data context, but you use componentParent().data().members (in code). So I find navigating the component tree much more stable than navigating data context tree. Also, internally, I also use some helper functions to find ancestors and descendants based on component class, and then use data context there. So then things work pretty well as you move things around.

This is also why there is no parentData method, because you do not need it in my view. You move between components and then you ask for data context of that component you want.

Maybe I should provide more functions and instructions for this.

Is this addressing what you are talking? Or not? Why are props still better in your opinion?

There is another way to communicate between components and this is to expose APIs between them (methods). And then call them.

Hm, I am seeing that this assumes all those goodies for navigating the tree. Maybe I should really or put this into a package or add it to core Blaze Components. So imagine a jQuery selectors for selecting ancestor or descendant components.

I would kind of like to define a component in a single file, not a .js, .jade and .styl, ie the way it work with React + JSX + inline style.

With new build system this is something you could develop on top of Blaze Components, no? See meteor-sideburns (and my comment). So this could probably work in the other direction as well?

I prefer separate files, but I think this can probably be doable? A package would be welcome. :-)

If the user close these inlined forms by mistake I also want to keep his draft in some sort of global storage

Why global storage? Why not component state? So I could imagine a component like:

<template name="FormInput">
  {{#if editing}}
    <input type="text" value={{value}} />
  {{else}}
    <div>{{value}}</div>
  {{/if}}
</template>

And you switch between editing and non-editing mode, but keep the state of the value as a state of the component?


I am trying to find actionable items, so have also in mind to suggest what concretely to open (tickets welcome).

mitar commented 8 years ago

And you switch between editing and non-editing mode, but keep the state of the value as a state of the component?

So something similar to examples from the tutorial?

mquandalle commented 8 years ago

On the first point, I actually do use the componentParent method. My issue is that a component has to have some knowledge about its external context (in this case, the parent component) instead of the caller explicitly giving the child component the data through props. It also happen that different instances of the component have to get the data from different places, for instance on the board view I get the members using this.getMembers() but on the card I have one level of indirection this.getBoard().getMembers. I believe it is a good separation of concerns for the caller to give the data the components needs to render, without the child having to make assumptions about the parent context.

For the second question, I put the drafts in the global storage and not in a local component state (as I was formerly) because often the component get destroyed and I still want to keep the data, for instance if I'm editing a card description and inexpediently close the card (or if I open another card B on purpose and still expected my draft on A to be there).

Generally speaking I tend to think that component local storage aren’t a good idea, I think that a global storage tree from which each component only see its assigned branch is a more powerful concept because it allows things like serializing the whole state and restore the application in the exact same state, and other things. There are quite a few discussions about this idea in the Redux and Clojurescript communities.

mitar commented 8 years ago

Where a lot of these challenges have been fixed by relying on event dispatching a lot more.

OK, I see I will have to discuss this a bit more in the README. :-)

So no, I am pretty against the event-based communication because it is really hard to manage it as system grows. You have to then make sure about event propagation, stopping and so on.

What I think is a better approach is that you traverse a tree of components and call methods on them.

was why I need to register components after defining them. But that might just be js-related.

You do not have to. You have only if you want them to be accessible from templates. So {{> Foo}}. For that, Foo has to be registered.

And yes, that is a bit of a JavaScript limitation. Open to suggestions, but I think this is the cleanest one.

(BTW, if you do not register them, then you have to still name them by calling componentName.)

mitar commented 8 years ago

I explained a bit about ideas of inter-component communication here: #82

mitar commented 8 years ago

I believe it is a good separation of concerns for the caller to give the data the components needs to render, without the child having to make assumptions about the parent context.

Isn't this what data context is then about? Giving data to a child? Not sure anymore what is then a problem. (Sorry, I would really like to understand the problem.)

Generally speaking I tend to think that component local storage aren’t a good idea, I think that a global storage tree from which each component only see its assigned branch is a more powerful concept because it allows things like serializing the whole state and restore the application in the exact same state, and other things.

Oh, I will ask if we can open source a mixin for this. :-) So we have this, a mixin which ads serializable state to each component, and then store them in a tree structure into the database. So even if you reload the page, you get back the same UI state. We use that for our in-site window manager made with Blaze Components. You do not want for user to reopen all windows all the time when things gets reloaded. I think you could use that as well. You can imagine even storing into it things like scrolling position, which cards are expanded, and also any form input state.

So then you have local state which you do not want to store, and seriazable local state which is stored and populated for you automatically when component is created.

BTW, this is a pretty short piece of code. :-)

northern commented 8 years ago

Not sure if this has been asked before, but, I'm wondering if it is somehow possible to pass data-* attributes to a component without those attributes explicitly being defined in the component itself.

E.g. currently I need to explicitly define the attributes in my component if I want those attributes to be rendered in the page:

<template name="TextInputComponent">
  <input name={{name}} id={{id}} />
</template>

In case of my TextInputComponent I might want to augment it with a data-* attribute so that when it's value changes, in it's listener, I can find out more context to where and what the TextInputComponent actually belongs to. E.g. I might have a table with hundreds of TextInputComponent instances.

The only option I currently have is to create a specialised subclass component that is data-* attribute aware which, as a nasty side effect, has the problem that I have to duplicate it's corresponding template. As you might understand, my actual component isn't as simple as in the example above.

Any ideas and insights much appreciated.

mitar commented 8 years ago

@northern: I think this discussion should be more suited for a dedicated ticket. Please open it and copy the content there.

mitar commented 8 years ago

Thanks for all the feedback. Closing the ticket for now.