SoluableNonagon
SoluableNonagon

Reputation: 11755

Ember and Handlebars - wrapper component to decrease repetition

I have a template that has some repetition in it and am wondering if there's a good way in Ember/Handlebars to handle this situation to decrease the amount of repeat code.

{{#if this.isClickable}}
  <button {{on "click" this.onClick}} >
    <span>
      {{#if this.useText1}}
        Text 1
      {{else}}
        Text 2
      {{/if}
    <span>
    <img src=someIcon />
  </button>
{{else}}
  <div>
    <span>
      {{#if this.useText1}}
        Text 1
      {{else}}
        Text 2
      {{/if}
    <span>
    <img src=someIcon />
  </div>
{{/if}}

I thought of creating a variable for the text to use and that would shorten my code a bit

{{#if this.isClickable}}
  <button {{on "click" this.onClick}} >
    <span>
      {{this.textToUse}}
    <span>
    <img src=someIcon />
  </button>
{{else}}
  <div>
    <span>
      {{this.textToUse}}
    <span>
    <img src=someIcon />
  </div>
{{/if}}

Is there a way to create variables in the hbs file to assign so I can dynamically create a button or a div rather than if statements that wrap repeated code? Also, would like to avoid creating new components if possible:

Ex:

  <ButtonOrDiv clickAction={{onClick}}>
    <span>
      {{this.textToUse}}
    <span>
    <img src=someIcon />
  </ButtonOrDiv>

Maybe something like:

{{#let 
  (if this.isClickable 'button' 'div')
  (hash
    onClick=this.onClick
  )
  as | Component options |
}}
  <Component {{...options}} />
{{/let}}

Upvotes: 0

Views: 116

Answers (1)

NullVoxPopuli
NullVoxPopuli

Reputation: 65173

There are a few options,

Using a getter

this approach assumes your component already has a backing class and offers the (which enables your second snippet)

export default class MyComponet extends Component {
  get textToUse() {
    return this.useText1 ? 'Text 1' : 'Text 2';
  }
  // ...
}
{{#if this.isClickable}}
  <button {{on "click" this.onRepostsTotalClickAction}} >
    <span>
      {{this.textToUse}}
    <span>
    <img src=someIcon />
  </button>
{{else}}
  <div>
    <span>
      {{this.textToUse}}
    <span>
    <img src=someIcon />
  </div>
{{/if}}

Docs: https://guides.emberjs.com/release/components/component-state-and-actions/#toc_computed-values

Using let

You can define variables straight in the template with let

{{#let (if this.textToUse "Text 1" "Text 2") as |theText|}}
  {{#if this.isClickable}}
    <button {{on "click" this.onRepostsTotalClickAction}} >
      <span>
        {{theText}}
      <span>
      <img src=someIcon />
    </button>
  {{else}}
    <div>
      <span>
        {{theText}}
      <span>
      <img src=someIcon />
    </div>
  {{/if}}
{{/let}}

Docs: https://api.emberjs.com/ember/4.4/classes/Ember.Templates.helpers/methods/let?anchor=let

Using a component

You could make another component, that would be:

ember g component the-text -gc
<span>
  {{#if this.useText1}}
    Text 1
  {{else}}
    Text 2
  {{/if}
<span>

and then back your component with the button, you'd have:

{{#if this.isClickable}}
  <button {{on "click" this.onRepostsTotalClickAction}} >
    <TheText />
    <img src=someIcon />
  </button>
{{else}}
  <div>
    <TheText />
    <img src=someIcon />
  </div>
{{/if}}

Using the element helper:

This is a polyfill: https://github.com/tildeio/ember-element-helper

{{#let (element (if this.isClickable
  "button"
  "div"
)) as |Tag|}}
  <Tag {{if this.isClickable 
      (modifier "on" "click" this.onClick)
    }}
  >
    <span>
      {{#if this.useText1}}
        Text 1
      {{else}}
        Text 2
      {{/if}
    <span>
    <img src=someIcon />
  </Tag>
{{/let}}

I think this requires at least [email protected] to get the modifier helper

Upvotes: 2

Related Questions