Quantcast
Channel: Ember.JS - Latest topics
Viewing all articles
Browse latest Browse all 4829

Contextual Helpers, Composable Helpers, or Helper Macros (!?)

$
0
0

@Spencer_Price wrote:

Helpers are becoming more-and-more used in Ember templates and as far as I can tell, there is not a great way to compose helpers together. As Edward Faulkner put it,

I think we need a story for composing helpers directly out of other helpers.

I agree. Because, let's be honest, stuff like this is not easy to read (but super powerful!):

{{hash
  name="parent"
  children=(array
    (hash
      name="child1"
    )
    (hash
      name="child2"
    )
  )
}}

And, with addons like ember-composable-helpers, the need for a better composability story only increases. The only real alternative is to use computed properties inside of your component, but invoking a component has quite a bit of overhead if the goal is only to call a pure function.

So, before opening an RFC, I'd like to see if there's any feedback that this discussion forum can provide to my proposal.

I think there's a few things we could call this: "Composable Helpers", "Helper Macros", "Partial Helpers", "Block Helpers", "Anonymous Helpers" or "Contextual Helpers". For now, let's go with "Contextual Helpers" because I think that how I would want to use these aligns fairly closely with how contextual components.

I recently opened up an issue against a promising new visualization library, Maximum Plaid, which shows an example of how we might use something like this. Say we have a set of helpers (fyi: this exists) that we can use to put together the d3 scales needed to construct a simple bar chart. That template might look like this:

<svg width={{width}} height={{height}}>
  {{#with (hash 
      xScale=(band-scale people (append 0 width))
      yScale=(linear-scale 
        (append 0 (max people 'age')) 
        (append 0 height) 
        accessor='age'
      )
    ) as |scales|}}
    {{#each people as |person|}}
      <rect
        x={{compute xScale person}}
        y={{subtract height (compute yScale person)}}
        width={{compute xScale.bandwidth}}
        height={{compute yScale person}}
      />
    {{/each}}
  {{/with}}
</svg>

Because d3 scales are just functions, to actually invoke them later on, we can use the compute helper from ember-composable-helpers. Like so:

x={{compute xScale person}}

But, ideally, we should be able to express xScale as a helper itself. The above would become:

x={{xScale person}}

...but this is not currently possible with Ember (as far as I can tell!)

So there's two parts to this: I would like to be able to return a helper from another helper. Also, the complexity of the logic inside the {{#with}} is quite cumbersome and not reusable, even though this pattern might exist everywhere we want to create a bar chart. Instead, I would love to be able to see the template above expressed like this:

<svg width={{width}} height={{height}}>
  {{#bar-scales height width people y-accessor='age' as |barDataMaker|}}
    {{#each people as |person|}}
      {{#with (barDataMaker person) as |barData|}}
        <rect
          x={{barData.x}}
          y={{barData.y}}
          width={{barData.width}}
          height={{barData.height}}
        />
      {{/with}}
    {{/each}}
  {{/bar-scales}}
</svg>

In this template:

  • bar-scales is my Contextual Helper which, like {{with}} is expressed as a block.
  • bar-scales yields barDataMaker, which is a new anonymous helper that is returned by the bar-scales helper.

If we were to peek inside that bar-scales helper, it might look something like this:

import Ember from 'ember';
import { ordinalScale } from 'ember-d3-scale'; // another helper

export function barScales([height, width, data], opts) {
  let xScale = ordinalScale([data, [0, width]], opts);
  // etc...
  return Ember.Helper.helper(([itemContext]) => {
    return {
      x: xScale(itemContext),
      // ...etc
    };
  });
}

export default Ember.Helper.helper(barScales);

An RFC for this would require some Drawbacks and Alternatives that I have not really though through just yet. I'd love to get some feedback on this before going that route.

So, please, lend me your ears (Robin Hood: Men in Tights style) and your thoughts. I'd be curious too if y'all have any other ideas for how composed helpers could be used.

Posts: 13

Participants: 4

Read full topic


Viewing all articles
Browse latest Browse all 4829

Trending Articles