Skip to content

Instantly share code, notes, and snippets.

@rlivsey
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rlivsey/9410694 to your computer and use it in GitHub Desktop.
Save rlivsey/9410694 to your computer and use it in GitHub Desktop.
Ideas on replacing content in components

A Popover Menu Component

<div class="pop-over">
  <span class="pop-over-anchor">
    Pick Something
  </span>

  <div class="pop-over-body">
    <ul>
      <li>One</li>
      <li>Two</li>
      <li>Three</li>
    </ul>
  </div>
</div>

Current

components/pop-over.js

PopOverComponent = Ember.Component.extend({
  classNames: ["pop-over"],
  anchorText: "Anchor",
  isOpen: false,
  selected: null,
  options: null,
  actions: {
    toggle: function(){
      this.toggleProperty("isOpen")
    },
    select: function(option) {
      this.set("selected", option)
    }
  }
})

templates/components/pop-over.handlebars

<span class="pop-over-anchor" {{action "toggle"}}>{{anchorText}}</span>

{{#if isOpen}}
  <div class="pop-over-body">
    <ul>
      {{#each option in options}}
        <li {{action "select" option}}>{{option}}</li>
      {{/each}}
    </ul>
  </div>
{{/if}}

application.handlebars

{{pop-over anchorText="Pick Something" options=someOptions selected=selectedOption}}

Things that we can't do easily:

  • change the option template content
  • change the anchor HTML / class etc...
  • change the layout of the popover body

Future

This assumes HTMLBars as there's stuff here that plain handlebars couldn't do.

Specify insertion points with content

Here we specify that the anchor has a content name of "anchor" and default content of {{anchorText}}

templates/components/pop-over.handlebars

<span class="pop-over-anchor" {{action "toggle"}} {{content select="anchor"}}>{{anchorText}}</span>
...

We can set the content for that with content for=xxx:

application.handlebars

{{#pop-over options=someOptions}}
  {{#content for="anchor"}}
    {{#if selected}} {{selected}} {{else}} Pick Something {{/if}}
  {{/content}}
{{/pop-over}}

We could even set attributes of the insertion point:

application.handlebars

{{#pop-over options=someOptions}}
  {{#content for="anchor" tagName="div" class="some-other-class"}}
    {{#if selected}} {{selected}} {{else}} Pick Something {{/if}}
  {{/content}}
{{/pop-over}}

Yielding content

To override the option, we need to be able to use its value, so the content helper can pass values by listing them:

templates/components/pop-over.handlebars

...
  <div class="pop-over-body" {{content select="body"}}>
    <ul>
      {{#each option in options}}
        <li {{action "select" option}} {{content select="item" option}}>{{option}}</li>
      {{/each}}
    </ul>
  </div>
...

Here we can use option when overriding item with ruby-style block syntax and choose an appropriate name etc:

application.handlebars

{{#pop-over options=people}}
  {{#content for="item" |person|}}
    {{person.fullName}}
  {{/content}}
{{/pop-over}}

Contexts

The context of the block is the parent, not the component, as it is now:

application.handlebars

{{#pop-over}}
  {{this}}       <-- the controller, not the component
  {{action "foo"}} <-- goes to the controller / route
{{/pop-over}}

The component would yield itself to the block so that you can address it from inside in a similar manner to content overrides:

application.handlebars

{{#pop-over |pop|}}
  {{pop.selected}}          <-- can now access the component's values
  {{action "foo" target=pop}} <-- and trigger actions on the component
{{/pop-over}}

Yielding

For block components which yield, anything not marked as content would be the template.

Eg, if our popover was instead built as follows:

templates/components/pop-over.handlebars

<span class="pop-over-anchor" {{action toggle}} {{content select="anchor"}}>{{anchorText}}</span>

{{#if isOpen}}
  <div class="pop-over-body">
    {{yield}}
  </div>
{{/if}}

Then we could override the anchor and set the body like so:

application.handlebars

{{#pop-over |pop|}}
  {{content for="anchor"}}
    Select {{#if pop.selected}}({{pop.selected.firstName}}){{/if}}
  {{/content}}

  {{#each person in people}}
    <ul>
      <li {{action "select" person target=pop}}>{{person.firstName}}</li>
    </ul>
  {{/each}}
{{/pop-over}}

This would output:

<div class="pop-over">
  <span class="pop-over-anchor">
    Select (Tom)
  </span>

  <div class="pop-over-body">
    <ul>
      <li>Tom</li>
      <li>Dick</li>
      <li>Jane</li>
    </ul>
  </div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment