Composable Components 2

Miguel Camba

@miguelcamba

@cibernox

miguelcamba.com

Why?

  • Wrappers around jQuery libraries
  • They are not idiomatic to use
  • Tricky to use with Ember Data colletions
  • Nearly impossible to customize

They are monolithic blackboxes

L

M

A

O

arge

onolith with an

rmy of

ptions

‎(ノಥ益ಥ)ノ ┻━┻

We know better

Built with composition

{{power-select}}

{{basic-dropdown}}

{{ember-wormhole}}

{{power-select-multiple}}

Built for composition

You can't cover all use cases.

Face it.

But users can if you let them.

Demo

Why composition is so awesome?

  • Stand on the shoulder of giants
  • Compose focused components into complex ones
  • Don't reinvent the wheel. 
  • Don't ship the same wheel twice
  • More reliable results, faster than ever

{{power-select}}

{{basic-dropdown}}

{{ember-wormhole}}

{{power-select-multiple}}

{{power-select-sortable}}

{{ember-sortable}}

Composition 101

Use {{yield}} to expose yourself 

<div class="basic-dropdown-trigger" onmousedown={{action "toggle"}}>
  {{yield to="inverse"}}
</div>
{{#if opened}}
  {{#ember-wormhole to=_wormholeDestination}}
    <div class="ember-basic-dropdown-content">
      {{yield (action "open") (action "close")}}
    </div>
  {{/ember-wormhole}}
{{/if}}
      {{yield (action "open") (action "close")}}

Use {{yield}} to expose yourself 

{{#basic-dropdown as |open close|}}
  <button onclick={{close}}>Dismiss</button>
{{/basic-dropdown}}

Expose your public API only

<div class="basic-dropdown-trigger" onmousedown={{action "toggle"}}>
  {{yield to="inverse"}}
</div>
{{#if opened}}
  {{#ember-wormhole to=_wormholeDestination}}
    <div class="ember-basic-dropdown-content">
      {{yield (action "open") (action "close")}}
    </div>
  {{/ember-wormhole}}
{{/if}}
      {{yield (hash open=(action "open") close=(action "close"))}}

Expose your public API only

{{#basic-dropdown as |dropdown|}}
  <button onclick={{dropdown.close}}>Dismiss</button>
{{/basic-dropdown}}

Shape your public API as your component

<div class="basic-dropdown-trigger" onmousedown={{action "toggle"}}>
  {{yield to="inverse"}}
</div>
{{#if opened}}
  {{#ember-wormhole to=_wormholeDestination}}
    <div class="ember-basic-dropdown-content">
      {{yield (hash 
          opened=opened 
          actions=(hash 
              open=(action "open") 
              close=(action "close")
          )
      )}}
    </div>
  {{/ember-wormhole}}
{{/if}}
      {{yield (hash 
          opened=opened 
          actions=(hash 
              open=(action "open") 
              close=(action "close")
          )
      )}}

Shape your public API as your component

{{#basic-dropdown as |dropdown|}}
  <button onclick={{dropdown.actions.close}}>
    Dismiss
  </button>
{{/basic-dropdown}}

Compose APIs and expose it as one

{{#basic-dropdown as |dropdown|}}
  {{#with (hash
    isOpen=dropdown.isOpen
    actions=(hash
      open=(action dropdown.actions.open)
      close=(action dropdown.actions.close)
      select=(action "select" dropdown)
      highlight=(action "highlight" dropdown)
      search=(action "search" dropdown)
    )) as |select|}}

    <input type="text" oninput={{select.actions.search}}>

  {{/with}}
{{#basic-dropdown as |dropdown|}}
  {{#with (hash
    isOpen=dropdown.isOpen
    actions=(hash
      open=(action dropdown.actions.open)
      close=(action dropdown.actions.close)
      select=(action "select" dropdown)
      highlight=(action "highlight" dropdown)
      search=(action "search" dropdown)
    )) as |select|}}

    <input type="text" oninput={{select.actions.search}}>

  {{/with}}

Compose APIs and expose it as one

{{#power-select ... onchange=(action "foo") as |opt|}}
  {{opt}}
{{/power-select}}
actions: {
  foo(newValue, select, e) {
    this.set('selected', newValue);
    select.actions.close();
  }
}

Compose for fun and profit

Thanks

Ember Power Select

By Miguel Camba

Ember Power Select

  • 2,074