ember.jsember.js-3ember-octane

How do I make an optional action for an Octane component?


I want to write an Octane/Glimmer style component where passing in the action is optional. What is the best way to write this?

For example, I want both of these uses of the component to be valid:

<MyComponent />
<MyComponent @validate={{action this.validate}} />

Right now, my component class has a lot of code that looks like this, checking to see if an action was passed in before calling it:

if (this.args.validate) {
  this.args.validate()
}

That works fine for one action, but not if I need to call several optional methods in succession. What other options are there?


Solution

  • There are several options available to make this optional actions code neater - use a getter, use tryInvoke, use a helper, or write a decorator.

    Using a getter is vanilla JavaScript and may be easiest for others reading the code to understand:

    import Component from '@glimmer/component';
    import { action } from '@ember/object';
    
    export default class MyComponent extends Component {
      get validate() {
        return this.args.validate || function() {};
      }
    
      @action
      someOtherAction() {
        this.validate()
      }
    }
    

    tryInvoke is an Ember API method that checks if a function exists before calling it. The drawbacks are that when other people are searching the code for uses of the function, their search may not find it:

    tryInvoke(this.args, 'validate'); 
    

    You could install or create your own optional helper. The ember-composable-helpers addon has an optional helper. Use it like this in a template.

    {{action (optional @validate) someArg}}
    

    Finally, you could possibly write your own decorator and call it something like @argumentFallback, then use it to label the defaults your component should use if no arguments are provided to the component.

    Thanks to bendemboski and theroncross for the information!