Ember templates are missing block params.
{{#form-for model |form|}}
{{form.input 'first_name'}}
{{form.input 'last_name'}}
{{/form-for}}
The goal here is to avoid complicated rules about scope that are
hard to reason about and lead to brittle code that's hard to refactor.
One of the trickiest parts of Ember (and any framework that tries
to solve similar problems) is that it's often difficult to figure
out and keep in mind what {{this}}
is in a template, and
what object/context/controller values like {{foo}}
are looked
up from.
Block helpers complicate this; {{#each items}}
will change the context of the provided block template to the
item being rendered. {{#with}}
's sole purpose is to change
scope.
Present-day components do not change the scope of the provided template block:
{{this}} and {{foo}}
{{#x-component}}
are the same as {{this}} and {{foo}}.
{{/x-component}}
This is desirable, but there's a major piece missing from the component (and helper) toolkit: the ability for a component or helper to provide data to the template block without changing the scope that the template block runs in.
Try writing a component that iterates over a list and provides the iterated element to the multiply-rendered template block. Can't really do it, not without digging into internals, adding keywords, or changing the scope of the template block through sorcery, which neuters the template block's ability to render stuff from the outer scope (which is kinda what all components are fundamentally supposed to support).
What we really need is block params.
# `self` doesn't change between here...
fibonacci.each do |n|
# ...and here
puts "The next number is #{n}"
end
The passed in block runs in the same scope as outside, with
the only addition to the scope being n
, a parameter name that
the block is free to change at any point; in other words, fibonacci
doesn't care what you name a parameter (this should be fairly obvious
coming from a Ruby mindset, but this concept is easy to get wrong in
template-land).
We need to support this in Ember/Handlebars-land. The closest thing we
have is {{#each person in people}}
, which achieves ruby block semantics,
but it doesn't carry over well into component-land (since ordered params
aren't supported and can't really support afaik if we're targeting Web
Components), it doesn't readily support more than a single block param,
and the code to achieve this is pretty ugly and not conducive to fostering
a component ecosystem given the boilerplate involved.
So we need a Handlebars equivalent:
{{#fibo-nacci |n|}}
The next number is {{n}}
{{/fibo-nacci}}
This
- Preserves scope inside and outside the scope.
- Allows for
fibo-nacci
consumers to change the name of the block paramn
to something else in case, say, it started to shadow some othern
on the outer scope. Other solutions, such as Angular's approach to havingfibo-nacci
augment the inner template's scope with values that the inner template can refer to, impose the component's internals and assumptions (and future changes) into the namespace of identifiers that the template block would like to use for its own purposes. Also, debugging and testing when many layers of scope are involved is very hard compared to block params. - Would be supported at a Handlebars level.
I took a stab at "context-specific helpers" (helpers that only exist and can only be invoked within certain helpers or components) using child containers, but I think using block params to achieve this is way more safe and straightforward (if not somewhat familiar from the Rails world).
{{#form-for model=person |form|}}
{{f.input 'first_name'}}
{{f.input 'last_name'}}
{{#f.fields-for 'friends' |f|}}
{{f.input 'first_name'}}
{{f.input 'last_name'}}
{{/f.fields-for}}
{{/form-for}}
Note that the above is actually shorthand for the uglier:
{{#form-for model=person}} {{|f|}}
{{f.input 'first_name'}}
{{f.input 'last_name'}}
...
{{/form-for}}
This uglier form is necessary for template blocks that might live in their own file and still need a way of choosing the name of the block param.
- Support for block param syntax, both the within-mustache shorthand
{{#each foos |foo|}}
and long form{{#each foos}} {{|foo|}}
. - Support for invoking
{{f.input}}
as a full on helper; right now, iff.input
is a path on the current context that refers to a function, the function will be called, but it's not treated the same way as a helper, doesn't get passed any args, etc. See: http://jsbin.com/uguhuqoK/3/edit
Ember.Component
s are still targeting Web Components, so everything
we do with block params needs to, at the very least, boil down to
and HTML representation. See the section below for what I think
an HTML-y approach would probably look like.
I bet in a year this will exist:
<ng-fibonacci ng-params="n">
The next number is {{n}}
</ng-fibonacci>
with the longer form
<ng-fibonacci>
<ng-param>n</ng-param>
The next number is {{n}}
</ng-fibonacci>
to support the inner template coming from somewhere else.