Using 2.3 binding syntax to import and use partials

This is a cool consequence of the new binding syntax. I’ll do my best to explain everything that is going on here.

<can-import from='test/other.stache!' {^@value}='*other' />

{{{*other()}}}

Let’s break this down.

If you haven’t used <can-import> before, it is a way of importing stuff into a template. You can use it the same way you import stuff into JavaScript files. In this case we are importing a stache template. This is equivalent to doing:

var template = require("test/other.stache!");
template(); // DocumentFragment

The value of test/other.stache! is a function.

Back to the can-import. This is what we want to look at: {^@value}='*other' What this does is:

  • Set up a one-way binding from child to parent.
  • On the child (the can-import’s) value property.
  • Using the at operator @ to pass the function instead of trying to read a value from the function.
  • To the parent’s *other.
  • * is a special syntax that binds to the References Scope. Think of *other like a local variable that you can use within the template.

Let’s go point by point. First {^value} sets up one-way binding. {} means one-way. ^ means from child to parent. Without the ^ it would be parent to child.

On the other side of the attribute, the attribute value, we have *other. This specifies the parent as being a reference value. Think of a reference like a local variable in JavaScript:

function someFunction(){
  var other;
}

In this case other is a variable that is scoped to someFunction. In that same way *other is a variable scoped to the template.

Now the fun part, the @ operator. Normally in stache/mustache functions are called to retrieve their value. That’s why if you have a method on a viewModel you can do {{method}}. A side-effect of this is that this means that if you want to pass the function itself there was never really a way to do that before. Can 2.3 introduces Call expressions which are the way forward. With call expressions you call the function like {{foo()}}. In 3.0 Call expressions might be the only way to use functions, but for now we have to deal with backwards compatibility and that means the at operator is needed so that we can hold on to the function itself to use later.

The cool part is that this means you can do things like pass a function on one component to another component.

Ok, let’s continue. can-import is a custom element just like any other custom element. This means it has a viewModel. can-import’s viewModel is a Promise, which is async. So this is why we bind to the import’s value property, the value will be set after the Promise has resolved.

Lastly, now that we have *other as a reference value we can use it; it’s a function. To call a function as a call expression you use the () syntax like in JavaScript.

{{{*other()}}}

Will call the *other function (a stache renderer) which returns a DocumentFragment. The triple curly brace syntax is needed so that the unescaped value is is inserted.

And that’s it! I hope you can see that by solving a long-standing problem in CanJS, the ability to pass functions in templates, we’ve accidentally created a new way to use partials, without any sort of special syntax just for them.

1 Like

Neat.

So including a document fragment and using triple-stache {{{ documentFrag }}} just inserts that fragment there?

Does the scope (context? scope?) keep getting passed to the document fragment too like with the partials?

Good question! First, remember that in this example it is not a fragment but a function that returns a fragment.

I didn’t know the answer to the question so I had to look into it myself. First, let’s look at how partials are rendered using the old syntax which happens here:

{{> partialName}}

It is rendered with:

can.view.render(localPartialName, scope, options );

Which passes the scope. So, no, the scope will not be passed to the partial using this new syntax. We need a way to do this though! I’ll open an issue. Ideally I think I’d want to be able to do something like:

{{{*other(%scope)}}}

Shy don’t allow partials to obtain scope and hashes in the same way as original partials in Handlebars?

{{>partial scope hash=value}}

Basically, make it works and configures similar to can.Component, but with ability to pass the entire parent (.) or custom scope inside and bind to?

Also I don’t see partial import is documented in 2.2 and 2.3 at http://canjs.com/docs/can|view|stache|system.import.html

Does it mean it’s not finalized yet and will be revised to be more seamless/intuitive? I saw a number if discussions in CanJS issue tracker in Gitgub and saw at least 3 different syntax proposes. So could you let us know, where are you on this, guys? Right now we miss this feature. So wondering if we can start using it (at least in the way you outlined above),

As the documentation says CanJS - {^to-parent}
And Justin in this issue Passing a function reference with @ runs it · Issue #2116 · canjs/canjs · GitHub

Shouldn’t the @ be on both sides

I fixed it so it doesn’t need to be anymore.

Is there a way to do this, yet?