Introducing Blaze "modules": locally scoped Blaze templates

Eat this, all you people who claim Blaze has no future. :wink:

12 Likes

I think it’s awesome. I wish I had it when I was making the ViewModel documentation. As @nathantreid mentioned, I wouldn’t have had to name stuff like viewmodelsChildrenPeople

I want to review the whole “turn off hot code push persistence” thing though. It’s taking forever to update to rc.12 so I haven’t tried it yet =(

1 Like

Yeah, turning off state persistence on hot code push would be painful.

I’ve tracked the state persistence issue down to lines 110 / 111 in viewmodel.coffee:

if migrationData = Migration.get(vmHash)
            viewmodel.load(migrationData)

At line 109, viewmodel.message() (from my example app) returns a Template, but Migration.get returns the saved data (which of course is an Object) of that from prior to the reload, and viewmodel.load overwrites the Template with the object. I’ll see if I can fix it and submit a pull request.

1 Like

After further review of the play, I think it’s working as it should. Take the following piece:

import hello from './hello.html';
import message from './message.html';

hello.viewmodel({
  message
});

What you’re telling ViewModel is that it should store the template message in a property of the same name. As far as ViewModel is concerned, that template object is part of the view model’s state. When a HCP occurs ViewModel restores the message property to its previous state (with a template object which is now invalid).

The solution is to use a function to lazily evaluate the message template.

import hello from './hello.html';
import message from './message.html';

hello.viewmodel({
  message() {return message; }
});
1 Like

I’d probably just do:

import hello from './hello.html';
import message from './message.html';

hello.helpers({
  message
});

hello.viewmodel({
  // View model as usual
});
2 Likes

Ahh, can’t believe I missed that - that makes total sense!
“To a man with a hammer, everything looks like a nail”

3 Likes

Thanks a lot @nathantreid for this awesome package! :thumbsup:

Cheers!

1 Like

How far is it from hot module reload for Blaze? :wink:

5 Likes

@gadicc, continuing the discussion from What is import of html file?:

I wanted to bring that into this main discussion since I think it bears more thinking about.

Right now there is a limit of 1 per file. Perhaps I should do away with that in favor of one instance of a name per file (so if you have a <body> tag, you can’t have <component name="body"> in the same file; likewise if you have a <template name="hello"> you can’t also have a in the same file.
Then all templates / components could be imported; the downside is that being able to import a <template> might give the impression that templates are locally scoped when they aren’t.
I’m not sure which is better:

  1. only <component>s can be imported
  2. <template> and <component> can both be imported, but <template>s are still globally scoped, so given
<template name="hello"></template>

These are equivalent:

Template.body.helpers({ // helper code ....
// or
import { hello } from './hello.html';
body.helpers({ // helper code ....

I like having the default export, so perhaps I’d just make the first template / component in the file be the default export. Alternatively there could be a “default” attribute that could be set, although from where I stand right now I don’t see that being worth the effort.

I don’t think the scoping is the end of the world, it’s kind of how import statements are in Meteor 1.3 right now… in theory they’re optional because of the backwards compatibility (with globals), but we know it’s the right practice moving forward, and if you do import the component, you’re sure of what you’re getting. So I quite like the idea that it works on the <template />'s we know already too.

I had a few ideas re the default export, but none seemed like a perfect solution to me. e.g.

<!-- one unnamed component allowed per file, that will be the default export -->
<component>
  <!-- access to the local scope without helpers -->
  {{> component2}}
</component>

<!-- available via named imports -->
<component name="component2">
  ...
</component>

Problems:

  • In a big project, editing multiple files simultaneously, confusing not having named components.

I guess the first template in the file being the default export is ok… my only concern is that it’s not explicit enough. But I guess if you allow both it’s not really a big deal. I also thought about different behaviour depending on how many components were in the file, but obviously it’s a bad idea to have an existing imports in the code no longer getting what they expect if a 2nd component is added.

But yes, I think multiple components per file is a must. And I guess I raised the idea of local scoping inside the file, above. Which in the longer term, makes me wonder also about things like @import something from './someOtherFile.html too :> But now I’m just causing trouble.

P.S. In the interests of full disclosure, I’m no longer developing in Blaze. I wish the community hand off had happened much earlier.

1 Like

Just released nathantreid:blaze-modules@0.0.2, where I’m experimenting with support for multiple components in one file as well as exporting Blaze templates.
I’m still not sure how I feel about it exporting Blaze <template>s, but it could help ease the transition to <component>s.
The first template or component in the file will be the default export. All components, and all templates with a valid JS variable name, will also be exported by name.

components.html

<component name="component1">
Testing {{> component2}}
</component>

<component name="component2">
1 2 3.
</component>

<template name="template1">
a b c
</template>

Component names must be valid JS names, but for backwards-compatibility Template names don’t have to be valid JS names - so a Blaze Components template named Buttons.Red will not be exported by name, because that’s simply not valid, but if it’s the first <template/component> in the file it will be exported as the default.

The above templates can be imported like so:

import component1, { component2, template1 } from './components.html';

The other improvement is that all components below a given template or component in the file will automatically be added as helpers to above templates/components. So in the example above there is no need to do the following because the compiler does it for you:

component1.helpers({ component2 });

This is especially useful for dumb components (for example I regularly stick inline SVGs into their own template so the main markup is still readable). Since only components are added as helpers, it won’t break any existing Blaze templates.

4 Likes

This is awesome! Kudos on all the hard work.

1 Like

maybe we can work this up into a package? I just thought of a name for it: Blazy :stuck_out_tongue:

PS: mean’t to respond this in the lazy load thread… but looks like you’ve got another big piece here

2 Likes

Unfortunately, when I remove the blaze-templates and add your package, this happens and nothing gets rendered:

app.js:60 Uncaught SyntaxError: Unexpected token export
router.js:347There is no route for the path: /Router._notfoundRoute @ router.js:347

Are you on the latest version (0.0.5)? I forgot to bump a package version when I published a while back, so my local copy (and clones of the repo) worked fine but the Atmosphere package wasn’t compiling correctly. I updated it earlier this evening with the fix.

I have only just tried the blaze-modules and it appears to be exactly what I wanted, and after first tries it looks and ‘feels’ very simple and clear. The package is not very popular though, are there any better options for Blaze or everyone just left to React? :slight_smile:

Btw, it seem to be a great option for Meteor 1.5 with lazy loading…

I have converted rather substantial part of my app to use nathantreid:blaze-modules. So far I love it because it adds clear components structure similar to React yet keeps in line with standard Blaze callbacks, reactivity and other working code - seem to be a real evolution without any hassles. I have still to keep some templates because BlazeLayout does not work with them out of the box, but it seem to be not a big issue.

Guys, could you please comment on this until I convert most of the app to blaze-modules:

  1. @nathantreid, did you use it in any production environment, how stable it is and what kind of caveats there might be? Do you use BlazeLayout or render the templates/components directly with Blaze?
  2. @sashko, @benjamn, are there any plans for meteor 1.5 lazy loading to be used with Blaze? How it could look like? Can it break the proposed blaze-modules approach?
  3. Everyone who uses Blaze, did you try or use any similar components package?

I haven’t used this package in production, although I may soon. Ideally it or something similar will be integrated into Blaze proper soon!

1 Like

If anything, modular Blaze templates should work better with Meteor 1.5 dynamic imports and any other module-centric strategies we adopt in the future. For example, if we do some kind of tree-shaking (aka dead code elimination) within modules, then the import declarations you use to import the templates will make it easier to eliminate any not-imported templates.

4 Likes