$whoami

Michał Staśkiewicz



@mikoscz
@staskiewiczmiko
@mikoscz
Building extendable boxes
Higher order components
Agenda
- Higher order functions/components
- Extending component in a lame way
- Higher Order pattern to the rescue
Higher order functions
const createAdder = (incBy) => {
  return (number) => number + incBy;
};
const incByFour = createAdder(4);
incByFour(10); // => 14
const incBySix = createAdder(6);
incByFour(4); // => 10Higher Order Function
Function (takes some args) -> Function
Higher Order Component
Component (takes some args) -> Component
Can component return value?
{{yield args}}
almost like functions
// components/custom-button.hbs
<button class="my-custm-button">
  {{yield "Click me"}}
</button>
// Usage
{{#custom-button as |value|}}
  {{value}}
{{/custom-button}}
Example return by yielding
"I want new feature"
Users profile box - everywhere
// user-profile/template.hbs
{{#if showAvatar}}
  {{user-avatar img=user.avatarUrl size=avatarSize}}
{{/if}}
{{#if showName}}
  {{user-name name=user.name format=nameFormat}}
{{/if}}
// Usage
{{user-profile
  user=user
  showAvatar=true
  avatarSize="big"
  showName=true
  nameFormat="long}}Mess in the air
// user-profile/template.hbs
{{#if showAvatar}}
  {{user-avatar img=user.avatarUrl size=avatarSize}}
{{/if}}
{{#if showName}}
  {{user-name name=user.name format=nameFormat}}
{{/if}}
// user-profile/component.js
export default Component.extend({
  showName: true,
  showAvatar: true,
  avatarSize: 'big',
  nameFormat: 'long'
});Trust me I'll refactor this
{{user-profile user=user}}{{#if showAvatar}}
  {{user-avatar img=user.avatarUrl size=avatarSize}}
{{/if}}
{{yield}}
{{#if showName}}
  {{user-name name=user.name format=nameFormat}}
{{/if}}
export default Component.extend({
  showName: true,
  showAvatar: true,
  avatarSize: 'big',
  nameFormat: 'long'
});
{{#user-profile user=user}}
  Some additional info
{{/user-profile}}Infinity crap
How about nope
Let's use Higher Order
{{component}}
{{component 'custom-button'}}// user-profile/template.hbs
{{yield (component 'user-avatar' img=user.avatarUrl size="big")
        (component 'user-name' name=user.name format="long")}}
// Place where we're using component
{{#user-profile user=user as |avatar fullName|}}
  {{component fullName}}
  {{component avatar}}
{{/user-profile}}More complex example
// user-profile/template.hbs
{{yield (component 'user-avatar' img=user.avatarUrl size="big")
        (component 'user-name' name=user.name format="long")}}
// Place where we're using component
{{#user-profile user=user as |avatar fullName|}}
  {{component fullName format="short"}}
  {{component avatar size="small"}}
{{/user-profile}}Overwriting defaults
// user-profile/template.hbs
{{yield (hash
  avatar=(component 'user-avatar' img=user.avatarUrl size="big")
  name=(component 'user-name' name=user.name format="long"))}}
// Place where we're using component
{{#user-profile user=user as |profile|}}
  {{component profile.name}}
  {{component profile.avatar}}
{{/user-profile}}Using hash helper
// user-profile/template.hbs
{{yield (hash
  avatar=(component 'user-avatar' img=user.avatarUrl size="big")
  name=(component 'user-name' name=user.name format="long"))}}
// Place where we're using component
{{#user-profile user=user as |profile|}}
  {{profile.name}}
  {{profile.avatar}}
{{/user-profile}}Shorthand syntax
// user-profile/template.hbs
{{yield (hash
  avatar=(component 'user-avatar' img=user.avatarUrl size="big")
  name=(component 'user-name' name=user.name format="long"))}}
// Place where we're using component
{{#user-profile user=user as |profile|}}
  {{#profile.name as |name|}}
    {{name}}
    <p>Some additional information</p>
  {{/profile.name}}
  {{profile.avatar}}
{{/user-profile}}Flexibility
Do I need to remember the API of a component?
// user-profile/template.hbs
{{#if hasBlock}}
  {{yield (hash
    avatar=(component 'user-avatar' img=user.avatarUrl size="big")
    name=(component 'user-name' name=user.name format="long"))}}
{{else}}
  {{user-avatar img=user.avatarUrl size="big"}}
  {{user-name name=user.name format="long"}}
{{/if}}
// Place where we're using component
{{user-profile user=user}}
Default behaviour
More examples
- https://github.com/cibernox/ember-power-select
- https://github.com/cibernox/ember-power-datepicker
- https://github.com/kaliber5/ember-bootstrap
- https://github.com/miguelcobain/ember-paper
Thanks!




@mikoscz
@staskiewiczmiko
@mikoscz
Higher order components
By Michał Staśkiewicz
Higher order components
- 854
 
   
   
  