Transclusion in Blaze


#1

Hello! How do we transclude elements in a Blaze block helper? F.e., in the following, how do we place the div.content somewhere inside the layout?

{{#layout}}
  <div class="content"></div>
{{/layout}}

In case it’s not clear what I mean, in React the equivalent would be

<Layout>
  <div class="content"></div>
</Layout>

where Layout might do render something like

<div class="deeply-nested">
  <div class="put-children-here">
    {this.props.children}
  </div>
</div>

Angular has the same feature, coined “transclusion”. Web Components achieve the same with Shadow DOM.

In the case of Blaze it’s not clear in the guide how to make these “Block Helpers” transclude things.


#2

Ah, seems Template.contentBlock defines where content is “transcluded”, but it’s limited to transcluding children into a single place (unlike Angular, React, and ShadowDOM). (Or did I miss how to do that?)

Although the guide shows a brief example of Template.contentBlock, it’s not immediately clear that that is what places the children. This article in addition to the guide helped:

http://blog.east5th.co/2015/01/13/custom-block-helpers-and-meteor-composability


#3

You are basically correct. The idea of “multiple” slots for children and treating children like arguments to a function is rare even in React/Angular. Blaze simply has no concept of that as it is always providing content to it’s own rendering system.

The three systems you mentioned pass pieces of an XML tree around until you are done “playing” with them, and then does the patching. This results in cleaner faster updates while also allowing you a lot more flexibility in mangling/managing that tree, especially with regards to things like this.


#4

I think I’m missing something, is this what you want?

<body>

  {{#Layout }}
    <p>Hello</p>
  {{/Layout }}

  {{#Layout }}
    <p>World</p>
  {{/Layout }}

</body>

<template name="Layout">
  <h1>Content:</h1>
  {{> Template.contentBlock}}
</template>

Result:

<body>
  <h1>Content:</h1>
  <p>Hello</p>

  <h1>Content:</h1>
  <p>World</p>
</body>

#5

That’s partially what I want. I was hoping for something like this:

<body>

  {{#Layout }}
    <p>Hello</p>
    <p>World</p>
  {{/Layout }}

</body>

<template name="Layout">
  <h1>Second paragraph:</h1>
  {{> place 1st paragraph here}}
  <h1>First paragraph:</h1>
  {{> place 2nd paragraph here}}
</template>

Result:

<body>
  <h1>First paragraph:</h1>
  <p>Hello</p>
  <h1>Second paragraph:</h1>
  <p>World</p>
</body>

Is it possible to place different parts of the children of a block helper (i.e. first child, second, child, etc) into different places like we can do in React, Angular, and ShadowDOM?


#6

How do you do that in React?


#7

Thanks for checking out my article. I wrote another article that discussed meteor composability and doing things like this using Template.dynamic. It’s a little verbose, but it might get you closer to where you want to be, or give you a few ideas, at least.

Check it out: http://blog.east5th.co/2015/03/09/meteor-composability/

You may also be able to tear apart your Template.contentBlock within your block helper and pull out each individual DOM node. From there, you might be able to find a way to inject them where you see fit. I’m just thinking out loud here…


#8

In react the contents of the inner children get passed to the component as an array:

class Layout extends React.Component {
  render() {
    return (
      <div>
        <h1>First Paragraph</h1>
        {this.props.children[0]}
        <h1>Second Paragraph:</h1>
        {this.props.children[1]}
      </div>
    );
  }
}

class Content extends React.Component {
  render() {
    return (
      <Layout>
        <p>Hello</p>
        <p>World!</p>
      </Layout>
    );
  }
}

which results when rendering Content in:

<div>
  <h1>First Paragraph</h1>
  <p>Hello</p>
  <h1>Second Paragraph:</h1>
  <p>World!</p>
</div>

#9

Like what @lassombra said. Here’s a fiddle: https://jsfiddle.net/d26h2sqn/1/.

That seems like the only way to do it, with the only downside as you mentioned that the content isn’t inline.


#10

@gadicc Any other possibilities? Just wondering since I know in famous-views you were able to have nested components work well with each other (f.e. DOM elements inside your Surface block helpers).


#11

You can pass pure data into the template, and generate the html as normal within the template itself:

Parent Template:

Template.someParentTemplate.helpers({
paragraphs: ()=>['First Paragraph', 'Second Paragraph']
});

Parent Template:

{{> MyLayoutTemplate paragraphs=paragraphs}}

MyLayoutTemplate:

<template name="MyLayoutTemplate">
    {{#each paragraphs}}
       <h1>{{this}}</h1>
    {{/each}}
</template>

#12

Hey, no, I’m not aware of any way to split up a contentBlock. If you really want everything in-line, I guess you could use another helper to populate an array in the parent:

{{#Layout}}
  {{#p}}First Paragraph{{/p}}
  {{#p}}Second Paragraph{{/p}}
{{/Layout}}