Worried about what Ember 2.x will become

Not the best example perhaps but I think it does have some merit.

I think:

<p>{{item.name}}</p> {{adBox}}

Is a lot better readable than:

<p>{{item.name}}</p> {{component item.adBoxComponent item=item}}

It does have some obfuscation on what adBox does with what. But the question is whether it should be enforced to make that clear in the template. Should how we render something, how we bind data to the html, contain so much wiring?

<p>{{item.name}}</p> {{itemAdBox}}

Proper naming makes it a bit clearer. We’re not sure which component is going to be used. But we can’t see that either when using the computed property.

On a side note: you made the item decide how it should be rendered. But this would really be up to the component rendering the item, not the model itself, but that’s beyond the scope of your argument.

Extreme readability of the template isn’t my goal though. I think maintainability is much more important. Let say we started with this:

<p>{{item.name}}</p> {{item-ad-box item=item}}

I have several templates using this way of including the item-ad-box. I now have determined I need another component to render the on sale item in another component. Since I don’t want to have to change all my templates to:

<p>{{item.name}}</p> {{component this.adBoxComponent item=item}}

I’m going to need 3 components. The item-ad-box, item-ad-box-default and item-ad-box-sale. The item-ad-box will determine which actual ad-box needs to be shown. While I don’t want that extra component, I could live with that. But in the end I think this will just be more obfuscating and complex than some well designed implicits.

Now assume we need an additional binding:

<p>{{item.name}}</p> {{item-ad-box item=item index=currentIndex}}

Now there is no way around editing all the templates using the item-ad-box. Or I need to work around it with some service, increasing complexity and decreasing cohesion.

I strongly disagree. The dash makes it clear it’s a component rather than a helper, and makes it a mechanical change to go from the current handlebars syntax to angle bracket syntax at a future date. I wasn’t sold on the dash initially either (and I would rather have had them to be not required at all) but leaving it as is I consider to be the lesser of these two evils.

I agree on that. But then we should keep it clear all the time it’s just an example of how this could be addressed. Otherwise people that are late to the party might mistake it for a more concrete proposal.

Why is the concern of the template to figure out which DOM elements to render, but not which strings to render (which is what you do with computed properties)? My opinion is that the template’s job is to render stuff. Where that stuff comes from, whether it’s injected from outside or constructed from within, is not so important.

I think, given enough exposure, all of these variants are essentially more or less the same with respect to readability. Think of it this way: When you write a function, sometimes you extract some calculation into a helper function, sometimes you don’t. It’s a readability tradeoff, do you want to see the “guts” immediately, or do you want to put an abstraction layer on top of it? In the same vein, I think nobody is advocating that this be the only way to do things. Same as you can decide whether to do

{{item.name}

or

{{itemName}}

you would be able to decide what to keep in the template and what to encapsulate into its own function.

What my proposal gives you, however, is the ability to:

  • completely change everything about that adBox thing. You could even render a static string, if you for some reason need it.
  • properly unit test which component will be rendered
  • properly debugging when something goes wrong (I’ve tried debugging templates, and it’s no fun)

It also gives you transparency. The template doesn’t need to know whether what it renders is a component, a string, whatever.

But that’s the ultimate example of logic being recreated in the template layer.

Please reread my comment. If you want to use it as a keyword or angle-bracket component you would be required to use the dash for recognition. At this time however, the entire lookup requires the dash. I can’t do:

{{component "post/navigation"}}

Again, while good from standardization point of view, it is too restrictive in my opinion.

I don’t entirely agree with this. In my opinion it is up to the template to describe the DOM and “what data” gets “bound how” to that DOM.

When we’re going down the tree and we want to render our item-ad-box it’s not necessarily a concern of that template which component is used and what should be bound to that component. In the template we just want to say “the item-ad-box must be rendered/included in the DOM here”. The template shouldn’t “have” to be aware of which component to use and the logic/binding required to include that component.

To bring up a slightly different, but related example, I’ve been working on PDF generation lately. If you want to render a (dynamically generated) image in that PDF you can either

  • call the image rendering from the PDF template
  • render the image in the caller and pass it to the template

Both are viable, but I prefer the second option. This is basically not much different from the discussion at hand.

To me, the components paradigm aims at creating something close to native html elements. It then comes naturally that the template should be the one selecting to render data with a <ul>, a <table>, a {{list-view}} or a {{product-list}}.

Or, another way to put it, like someone said above, template as a function: template(data) = dom

right, only “data” can be anything. It can also include HTML. Think: main_template({ foo: 'bar', sub_template(data) }) = dom. In types: render_template :: Hash[Printable] -> DOM, where DOM is also a Printable (or some such).

I think all we suggested was to be able to invert the dependency the same way we can invert the dependency from item.name with a computed property itemName, if you want to.

I think that’s the we do not put the same thing behind data. To me, data only means pure data, no html, no rendering information. Those would be model data, validation results, computed statistics, operation statuses, … All raw, no preprocessing whatsoever, leaving the template fully in charge of telling how to render that data into html elements.

Even things such as how to format the date, or how many digits after the comma for a number should be decided by the template (using helpers).

Kinda like Django or Jinja2 templates: you give the template the querysets, untouched, and the template builds the html.

That’s where I disagree entirely. Templates should not be in charge of any logic. The reason for that is that a templating language is badly suited for these things. Syntax gets clunky, debugging is a pain and testing is impossible without rendering the whole thing. That is unless we rethink what “templating” means. If you have something like JSX in React you can make an argument that it’s only sugar for regular JS functions anyway and that you can properly unit test and stub and debug that stuff (I think, but I never really worked with it). (Yes, I know HTMLBars compiles into JS as well, but that’s not something you want to really work with).

I’m not arguing for mixing up presentation logic with business logic. Presentation logic should still belong to the component. But why should it be in the template? I think it’s worth reconsidering why we use templating: We do it, IMHO, because writing out all that HTML stuff inside a JS file would be extremely painful and unreadable. That’s it, otherwise we’d just use template strings or something. But whenever you have logic, an actual programming language is much better suited for the task than a markup language.

It’s true however that helpers somewhat help with this. At least with helpers you’re putting logic back into the JS and you can unit test that stuff properly (although I’m not 100% convinced that it works so well, helpers seem quite restricted in what they can do). In that case it ultimately comes down to what is more readable in the current situation and in which direction you want data to flow.

I’m aware of how templating is traditionally done. Django, Rails, etc. are all the same here. And I’m pretty much convinced that, for the most part, it’s a disaster. At least HTMLbars keeps you from doing the worst stuff by not allowing as much logic in the templates.

I would like to correct that certain business logic (might) belong to the component, not the actual presentation logic. We or at least I argue that for the rendering of certain data were a child component is required/desired for rendering that data, selecting that component should not always be up to the template, specifically in the cases where business logic requires the use of one specific component. While we can already use the dynamic component helper, this doesn’t account for different bindings required for different components.

Considering the use of a possible proposal should be optional, this behavior would remain exactly the same. Except for an alternative to the dynamic component helper where explicitness comes from the contextual component instead of from the template.

Your point however does reveal a flaw in my suggested “component defined keyword approach”. You would be tempted to define keywords like “product-list”. I would even like that by that you also define the tag name to be used to wrap the component. But this gives a lot of undesired ambiguity. While I personally don’t mind some obfuscation in my templates due to abstraction, you would now encounter this:

<product-list> or {{product-list}}

There is no way of telling this will invoke the generic product-list component or that it will be swallowed by a component defined keyword. I’m not sure that’s really as bad as it may sound, because it allows me to override all the use of existing product-list definitions if I want to refactor them. They other way around however is indeed bad, when I’m putting that tag in my template while not realizing it’s a keyword for that component…

Have issues like these already been addressed with the implementation of angle bracket components? What happens when I do this:

<product-list> <product-list-item /> </product-list>

Is product-list always required to be resolved as a component? Or can I still use custom tags without them being backed by an actual component?

Whoah. Writing templates without even an if helper or an each helper? That can get painful. I generally agree with you, but lets not go quite that far. </end strawman argument>

Seriously, whenever you feel the need to unit test something, move it out of the template and put it in the component. Otherwise, do whatever’s more readable to you. There is always a balance to these things.

2 Likes

We’re not advocating templates must or should be dumb. I like my templates as dumb as possible but that’s personally. I like to think more of this as a request to supply the tools to dumb down templates.

1 Like