Proposal for Blaze 2

Does Blaze Components follow the Meteor Style Guide?

No, it follows PeerLibrary one: https://github.com/peerlibrary/peerlibrary/wiki/Code-Style

How do those of us working in JavaScript lint it?

No, it uses CoffeeLint: http://www.coffeelint.org/

I can make our linting config public, if anyone is interested. I use it my IDE, but I have not yet put it into Travis CI for automatic testing.

Our IDEs and scripts are all set up for .js files, not .coffee files.

I think many IDEs support CoffeeScript.

So I bring the issue of JavaScript up, not as a criticism, but as an encouragement on how to get more widespread adoption.

Yes, but my look is that if the library is done well, you should not have to care in which language is implemented. See it as a black box.

5 Likes

So @mitar, in hope of returning to the original topic, could you elaborate on the design of Blaze Components? Which decisions did you make while building it, and what was your reasoning? What needs to happen for components (the concept in general, not necessarily your package) to be adopted into Blaze itself?

I am still on the Blaze train, so Iā€™d really love to see our minds meld and create something awesome based on the groundwork thatā€™s already been done.

But you and others did. If someone wants to write .coffee, let him. Donā€™t use it if you donā€™t like it. Same vice versa, someone wants to write .js, let him. Someone using Emacs and not your Editor, let him, donā€™t try to impose your mindset onto other people at all cost, it only creates things like derailed threadsā€¦ at best.

Thanks for your work Mitar, whether itā€™s .coffee or .js, written using Emacs on Windows while having a coffee or Webstorm on OSX while having a cup of tea, your work is welcome sir!

1 Like

ANYONE WORKING FOR THE BENEFIT OF THE COMMUNITY AND DOING IT FOR FREE - like package authors - DESERVE OUR SUPPORT AND GRATITUDE.
Whether they do it in Coffee or Assembler.
I propose we end this and start two new topics for Blaze 2 and Components - if there is such a need.

3 Likes

I think the main point is that this is a template instance everywhere. And that you then use it to access data context (this.data()), or any other reactive variables/fields you put on. And when resolving symbols in templates, both template instance and data context are searched.

This allows a very powerful pattern: you have data context provided from outside to your component, and then you can create internal state in onCreated for the template instance (like attaching reactive var to the template instance as a property). And you can access both from the template itself. This enables then that event handlers can be modifying that internal state and Blaze then rerenders accordingly. So it makes it really easy to not fall into the direct DOM manipulation hole.

<template name="Foo">
  {{#if displayed}}
    <p>{{fromDataContext}}</p>
  {{/if}}
  <button>Toggle</button>
</template>
BlazeComponent.extendComponent({
  onCreated: function () {
    this.displayed = new ReactiveField(true);
  },

  events: function () {
    return [{
      'click button': this.onClick
    }];
  },

  onClick: function (event) {
    this.displayed(!this.displayed());
  }
}).register('Foo');

The interesting thing is that then this.data() in template helpers is just the same as any other access to the reactive variable, just that it is provided automatically for you by Blaze. Also, interesting to notice is that if you do not access this.data(), then no reactive dependency on the data context is created. So you can have a component which completely ignores the current data context and will thus not be rerendered at all if data context changes.

I think such approach makes it much more Meteor-like. If you look what is the main pattern when programming Meteor applications is that you have some state (MongoDB collections, Minimongo collections on the client, Session, reactive vars). Often that state is also layered. So MongoDB collections get copied to Minimongo collections on client (through publish). And then you define Blaze template how that state should be rendered. So this can be seen as a declarative definition. Any time you change the state, DOM gets updated. You do not have to care about that. Similar ideas are used in Tracker and reactivity in general in Meteor, that you define how some values are mapped to new values and Meteor will make sure to update things.

Now, a common anti-pattern observed in Meteor applications is that when you want to handle some user interactions, you manually/directly modify things which are managed by the Meteor. For example, if you have MongoDB state which is copied to Minimongo on the client, which is then used to render DOM. It is wrong to try to modify Minimongo copy directly (because it will be overridden by future changes from the server, and luckily this is not even so easily to do, because it is not directly exposed to you), and in the same way it is wrong to try to modify DOM (but that one is easy to do, so it is easy to fall into this), because it will be again modified soon by Meteor, and then you are fighting the system.

The right approach is that you always go and modify directly the state, and then you let the change gets propagated through to the end, automatically, by the Meteor. So for DOM events, this means often that you call some Meteor method which modifies MongoDB state and then you wait for it to propagate to the client and rerendered. Instead of you directly modifying DOM. (And Meteor support latency compensation to make this waiting easier.) So you declaratively define how the data changes propagate from the database all the way to the DOM, and then in events you modify the database only, letting Meteor take care of the rest.

This works well even with animations. You simply declaratively define how you want state changes to be animated into DOM and then things just work. No need to worry about handling all the timing issues associated with animations when you are doing them imperatively. Can I add another DOM element to DOM, or is maybe DOM element already there, just it is being still animated and moved in place? You do not have to worry about that, just change the state and Blaze will take care to eventually get to render that, together with any animation you wanted.

But the issue with current Blaze approach is that there is no easy way to have some local state to use this pattern. You are only provided data context which is coming from that long pipeline of changes, possibly all the way from the server side. If you try to do the simple displayed thing above you have to do a lot of boilerplate code which means that most beginners to Meteor which are not familiar with the pattern and boilerplate code will not do it. And will instead try to modify DOM directly (because it is so easy with jQuery). And then fight Meteor.

So, Blaze Components make it really easy to have that additional local state you can use for things you need for UI only. So displayed does not have to be in the database. Data from the database you get in the data context (and is automatically managed), but you might want to also have some local state for DOM. And the beauty is that it can be nice and declarative. So you define once how local state is rendered (displayed shows or hides a DOM fragment) and then you do not have to think again how to remove and add nodes, you just modify that state. From whichever code path you have to, function calls, event handlers, timers, whatever you want.

Moreover, Blaze Components are much less often destroyed and recreated? Because they do not depend so much on the data context. So this means that you can really persist some local state in a component, even while data context is being changed.

With all this being said, I do not think there is really a backwards compatible way to change Blaze to support something like that. But I think there is a really easy way to make something on top of Blaze which allows reusable stuff. And Blaze Components are implementation of that. So Blaze is then a simple declarative language which renders some state into DOM, and Blaze Components are then something which provides maintenance of that local state and other goodies which are so useful.

I do think that it would be a bad approach to try to add some hacks to Blaze to support some of these ideas (like adding attributes and state). From my perspective, they are just adding even more confusion to the mix. I do not think that data context is bad, just that there is no alternative to it (like having access to template instance). But having then data context, template instance, state, and attributes, which one should one use? Explain good rule of thumb? It is really complicated. So instead of trying to deprecate data context by introducing new ways to store and pass state (which for me are just special cases of attaching local state to the template instance), we can simply reuse existing stuff. Because there is already everything needed there. It is just cumbersome to access and use. We have data context for data coming from higher up in the hierarchy, and we have template instance for the local state (which can also be coming from the database through local subscribe, BTW).

So, I think the best direction would be to leave Blaze clean and simple as it is, and MDG creates or adopts something like components on top. I think everything is already there, the only remaining issue is this one.

Oh, and have I mentioned that Blaze Components were designed (but no time to implement) to work well with React and Web Components as well? So that the same API can be used no matter what engine to render state to DOM is used? So one can prefer to render state to DOM using React, another one Blaze, and they can work together in Meteor. That is what base component is about. But I have not had time to really try this out so this is for now only at the concept stage.

Or there could even be some mix between Blaze and React, where React (or virtual DOM) is used to render the state to DOM, but templates, event handlers and methods are the same as in Blaze (and Blaze Components).

6 Likes

What about passing around state/props from parent to child components? This is something Blaze currently is notoriously bad at, and an area where React has the clear advantage. Does Blaze Components offer a good answer for this?

Can you explain a bit more? What is the use case you are thinking here? So I am not good at discussing tools for toolsā€™ sake. Each tool (data context, state/props) has a reason in a wider design. Just having a tool because somebody else has it in your design is not a good design.

So what need/use case are state/props are addressing for you in React that is lacking in Blaze (or Blaze Components)?

1 Like

I am not using React yet, but I like the idea of dividing an app into components that are self-contained, you just pass properties and they will render accordingly. With ā€˜vanillaā€™ Blaze, this (to my knowledge) is almost impossible to do without using Session (which, due to their global nature, are bad for scaling, flexibility, reusability ā€” let alone the predictability of the code).

An example: an app that Iā€™m working on has a <main> element, and an <aside> for an optional sidebar, that you can expand and collapse with a button in the top <nav>. All these components influence each other: when you activate the sidebar, the content of the <main> changes (slightly) and the <nav> will highlight the icon of the sidebar. For this kind of ā€˜cross-compent communicationā€™ I had to resort to session variables.

A second (more complex) example: one of the sidebars you can activate is for giving feedback on the <main> content (slides in this case). You can click one of the slides in <main> to link your feedback to that specific slide. Vice versa you can click on ā€˜linked feedbackā€™ in the <aside> which will highlight the slide in <main>.

Ideally I would have some kind of parent ā€˜app controllerā€™ component that handles this stuff, like as I understand React solves this.

I hope these examples help clarify!

Hm, is there a difference between passing properties and passing a data context?

But yes, Blaze Components provide a great alternative to this. As I mentioned, each component contains an arbitrary local state. And it renders itself using that local state (and other things, like data context). I wrote about how you can manipulate that local state from event handlers attached to that component. But there is completely no reason why you could not manipulate the state from outside of the component as well.

Imagine such code:

BlazeComponent.extendComponent({
  onCreated: function () {
    this.displayed = new ReactiveField(true);
  },

  events: function () {
    return [{
      'click button': this.toggleDisplay
    }];
  },

  toggleDisplay: function () {
    this.displayed(!this.displayed());
  }
}).register('Foo');

(You can imagine that you can decide that some fields and methods are private, some are public, you can maybe prefix private ones with _, this is all to you to decide how you want the API to your component to look like.)

And now you just have to get access to the component instance and call toggleDisplay on it. There are many ways to get access to the component instance. You can use ways where you couple things together, but I prefer a more decoupled approach where traverse the tree of components and find the component you are looking for. (Core methods available only traverse one level, but you can imagine a jQuery-like language or helper functions to traverse recursively automatically. I didnā€™t want to put too much into the core, but somebody can make a package with them.)

So for your example, you could do something like (Assuming that both NavComponent and AsideComponent are direct children of MainComponent, and are siblings):

var MainComponent = BlazeComponent.extendComponent({
  // You can use this method in the template to modify <main> slightly.
  asideDisplayed: function () {
    // Here I would put some ? in CoffeeScript.
    return this.componentChildren(AsideComponent)[0].displayed();
  }
}).register('MainComponent');

var AsideComponent = BlazeComponent.extendComponent({
  onCreated: function () {
    this.displayed = new ReactiveField(true);
  },

  events: function () {
    return [{
      'click button': this.toggleDisplay
    }];
  },

  toggleDisplay: function () {
    this.displayed(!this.displayed());
  }
}).register('AsideComponent');

var NavComponent = BlazeComponent.extendComponent({
}).register('NavComponent');

A neat thing is that you can also access those things from the template:

<template name="NavComponent">
  {{#if componentParent.asideDisplayed}}  
    <icon>
  {{/if}}
</template>

Frankly, the code I made above is a really ugly design. Those components are too tightly coupled for my taste. Like NavComponent has to be exactly the sibling of the AsideComponent, and communication is happening over the method in MainComponent. But I wanted to showcase some ways to communicate. Probably a cleaner design would be to have some state in the MainComponent. In general state about if a given component is rendered should be in the parent component. Because parent component then renders it or not. But you can imagine probably now that this is really anyway how you design.

Again, current limitation is that those methods go only one level, but you can imagine helper functions to traverse recursively. Then you simply for example say ā€œgive me ancestor which is an instance of MainComponent and call this method on itā€. Or even "find MainComponent ancestor and then search its descendants for AsideComponent. (Again, someone could make a nice library for traversing the tree easier. A set of nice utility functions.)

So in this case maybe MainComponent would be more like you have in mind of ā€œapp controllerā€ where it would have some (local to it) state which then children components would expect to find in a MainComponent ancestor instance and call methods.

So because all communication happens between methods (that is a recommended way) it is easy to use OOP to extend components and extend those methods. You want something else to happen in your version of AsideComponent before or after it is toggled? Easy. Just extend toggleDisplay. And all other components will continue to work.

Also notice that I am using in examples reactive-field package, which combines the most common uses: a setter, getter, and internal reactive state. So then you do not have to create a state + methods to access it and set it.

There is also computed-field available, which is great for stuff which should be declaratively computed. For example, we use it to dynamically compute width of a sidebar based on which panels are open. So you simply query which panels are open and their width and compute how much space is left for a sidebar. And that is all done automatically then. You just define it once. And you can start using it in your templates.

So for your second example, you could imagine that your AsideComponent would have a state which would tell to which slide the feedback should be linked. And when you click on a slide in MainComponent it finds AsideComponent, if displayed, and sets the state. And MainComponent can have a state of a highlighted slide which can be set from AsideComponent. Or anywhere else, that is, as your app expands. You probably do not care from where the slide was highlighted. You simply have a state and define how a highlighted state is rendered. And then it can happen because you clicked in sidebar, or because URL changed, or anything.

One last thing. I already mentioned that I am not really satisfied with examples here, because they contain some bad practices and are too coupled (feel free to create a better example). One similar issue is also that displayed is probably the worst example to have as a local state. Because displayed is often implicitly available from the components tree. If a component is in the tree, it is displayed, if it is not, it is not. So you just traverse the tree and this is it. highlighted is probably a better example for a local state.

2 Likes

Canā€™t help myself with replyingā€¦ sorry :smile:

I canā€™t understand why anybody writing in coffescript gets so passionate in itā€™s defence. Look, I think what people are trying to say in a nutshell is A) ā€œWe love what you did hereā€, B) ā€œWe would love if more people used itā€, C) ā€œIf you want that too, a tip might be to write it in plain JSā€. Thatā€™s it. Nothing more nothing less. Nobody is trying to force anybody into doing something they donā€™t want to do. But the fact that e.g. this package is being promoted here, means a wider adoption is preferred. Soā€¦ people are providing helpfull tips to get there.

1 Like

I personally donā€™t like CS as well, but if you really want it in js, why not convert it yourself? (put it through a cs-js converter and adjust the output a bit).
I donā€™t understand why people feel they deserve someone elseā€™s code to be adjusted to their preference. If he prefers CS thatā€™s his prerogative, no?
Anyway, sorry for keeping the off-topic train going.

ok, but my point is, that people are all saying this not in the sense of ā€œchange it to my preferenceā€, but ā€œif you want to make more people use itā€¦ā€. Which I assume thatā€™s a (side) goal, or else it wouldnā€™t have been mentioned here.

Assume that if people are replying, itā€™s because they like the package and want to see it succeed. Not because they want to trash it :smile:

1 Like

Like @mitar said, you wonā€™t even have to write a single line of cs to use it, so whatā€™s the deal? Yeah you could rewrite the tutorial but come on, itā€™s not hard to read cs, even if you donā€™t like it. If you want to continue the discussion itā€™s probably best to create a new topic as weā€™re making the thread confusing :wink:

1 Like

Ok, Iā€™ll stop but plz donā€™t forget it :wink:

Just one more thing, semi on-topic, @mitar: Thanks for the great package. I believe this is what Blaze should look like, at the very least in itā€™s core concepts. Kudoā€™s. Iā€™m using Blaze Components every day with pleasure.

1 Like

@mitar where is the Blaze 2 proposal at? Seems like just a lot of discussion thus far.

I can understand why the mdg might be putting blaze 2 on hold. With 1.2 they fixed some major weak points. Now I feel blaze can solve my challenges well enough. For me blaze 2 seems more about semantic and stylistic changes.

That said Iā€™m writing template.instance a lot! Feels verbose and redundant

Pā€™s. Iā€™ve been watching the hackpad for months. @sashko

BTW, tutorial is already in JavaScript, too. You can switch to it if you want. Maybe I should make it a default. But then, people would not see how easier it looks in CoffeeScript. :slight_smile:

mitar, youā€™ve mentioned that this is suitable for webcomponents. Iā€™m curious if you have any patterns/tests how to best use this package for webcomponents and/or with Polymer. Iā€™m curious as to how I can leverage this for my appā€™s webcomponents that Iā€™m currently writing. Iā€™m in the process of converting my ugly Blaze code to Polymer components at the moment. Any thoughts or experiences here?

Sorry, that is for me only an idea. Like conceptually this is something I would want to happen, but I have not done any code for it and validated the concept. There was a ticket and discussion about it here, though: https://github.com/peerlibrary/meteor-blaze-components/issues/14

I have been converting README of Blaze Components to ES2015 and I must say that ES2015 did not convince me to switch to it, I will be sticking with CoffeeScript for the time being. Things one misses:

  • no ? operator
  • no way to call a static method inside a class body:
class Foo extends Bar
  @register 'Foo'
  • super is really awkward, instead of just calling super to call parent method with all arguments, you have to do super.methodName(argument1, argument2) and so on, or super.methodName.apply(this, arguments)
  • all those { and } around functions just make code so much longer
  • all those { and } you have to start adding again back for objects
  • no array comprehensions
  • no verbose operators like or and and
  • comeback of !== and ===
  • no function arguments assignment from the signature: constructor: (@argument) ->
  • no suffix if and unless
  • have to use this instead of @

ES2015 is a good fix over vanilla JavaScript, but I really suggest to people to learn CoffeeScript. :slight_smile: It is worth.

5 Likes

Also, can people very familiar with ES2015 and check if I made the examples according to best practices? Maybe there are some hidden gems I do not know about and could use to make code simpler/nicer.

1 Like