/blaze-plus

Adds Props and State Management functionality to Meteor's Blaze Template Engine.

Primary LanguageJavaScript

Blaze Plus (Blaze+)

Adding Props and State Management functionality to Meteor's Blaze templating engine.

Why?

Passing data from the top to the bottom of your UI is common pattern which pretty well, specially in React. But, it's an anti pattern in Meteor becasue, that leads to a lot of template re-renders.

Blaze Plus, is gonna fix it.

Adding Blaze Plus

meteor add kadira:blaze-plus

Passing Data to Templates via Props

Now, we are gonna pass some data to the template as props. Here's our app template which has the data.

Template.App.helpers({
  getUser: function() {
    var name = Session.get("name") || "Arunoda";
    return {name: name};
  }
});

Let's pass this user object into a another template:

<body>
  {{> App}}
</body>

<template name="App">
  <!-- This is the starting point of the Magic functionality -->
  {{>$ template="Welcome" user=prop$getUser}}
</template>

<template name="Welcome">
  <span>Hello, </span> {{getName}}
</template>

Here we simply pass the getUser helpers response to the Welcome template. But, how it access that object. Let's see the getName helper for that:

Template.Welcome.helpers({
  getName: function() {
    // getting props via Template instance
    var props = Template.instance().props;
    return props.user().name;
  }
});

As you can see, Blaze Plus assign all the props you pass into the props property in the Template instance as a function. So, it only invalidate only when used it.

You can clone this app via: https://github.com/arunoda/blaze-plus-props-only

By doing that, we can avoid templates re-renders.

For an example, now you can change the name via: Session.set('name', 'Your Name') and it won't re-render the Welcome template. Just like that, you can pass data to any level you need.

Watch this video to see it yourself.

Using States to Simplify Things

In the above example, getting the name via props is kind a bit difficult and we need to write more code: That's where state's gonnna help us.

Now we are trying to get the name in the Welcome template using states like this:

<template name="Welcome">
  <span>Hello, </span> {{$name}}
</template>

We write states with a $ symbol at the beginning. This is how we create this state:

Template.Welcome.onCreated(function() {
  var self = this;
  this.autorun(function() {
    var user = self.props.user();
    self.setState('name', user.name);
  });
});

We are doing it inside a autorun created inside the onCreated hook. Here autorun will run whenever the name changes. Then it'll set the state accordingly.

It is recommended to run as many as autoruns isolating props into their own autorun. With that, it'll invalidate only the necessory states. But, that's not a rule.

Just like setting state, you can get state as well using getState().

Template.Welcome.onCreated(function() {
  var self = this;
  this.autorun(function() {
    var user = self.props.user();
    var exisingName = self.getState('name') || "";
    self.setState('name', exisingName + ":" + user.name);
  });
});

It is not reactive. That's something we did to prevent invalidating loops.

Known Issues

  • You can only pass helpers and data items as props. (You can't use this, .. and etc).
  • If you are using an state in a template, you need to be define at the top of the autorun.
  • If you use any built-ins like #if, #with, #each there is possibility to have re-renders inside them.