What is the best practice to pass action down?

I have next line in my template

{{some-fancy-component mydata=mydata editAction=(action 'edit') deleteAction=(action 'delete') addAction=(action 'add')}}

And in my component some-fancy-component I have next lines:

    @action
    callEditAction(...attrs){
        if (this.editAction){
            this.editAction(...attrs);
        }
    }

    @action
    callDeleteAction(id){
        if (this.deleteAction){
            this.deleteAction(id);
        }
    }

    @action
    callAddAction(...attrs){
        if (this.addAction){
            this.addAction(...attrs);
        }
    }

Yes, I should avoid duplication of code but it’s to make the example more verbose. I think should be a possibility to avoid of creation these dummy action in some-fancy-component component.

The example doesn’t make much sense to me. None of the names used seem to connect to anything. There are also many patterns you could use and it is up to you to understand the context in which to use them.

It is also unclear what your question is.

I’m guessing you’re trying to do something like this in your some-fancy-component:

<button {{on='click' (action 'callEditAction' someArg)}}>Edit</button>
<button {{on='click' (action 'callDeleteAction' theId)}}>Delete</button>
<button {{on='click' (action 'callAddAction' someArg someOtherArg)}}>Add</button>

Assuming that’s the case, you can just get rid of the intermediate actions and do this instead:

<button {{on='click' (action @editAction someArg)}}>Edit</button>
<button {{on='click' (action @deleteAction theId)}}>Delete</button>
<button {{on='click' (action @addAction someArg someOtherArg)}}>Add</button>

(Here the @ signifies that this is an argument which has been passed into the component and not modified in any way.)

1 Like

Yes, if the question is just how to call the actions you were given directly from the template, @chriskrycho’s answer is what you want.

But if the question was how to insert the conditionals like

if (this.editAction){
  this.editAction(...attrs);
}

so that there’s no error when no action was passed, one way to do that entirely from the template is to make a helper like

export default helper(function maybeCall([handler]) {
  return function(...attrs) {
    if (handler) {
       handler(...attrs);
    }
  }
});

And use it like:

<button {{on='click' (action (maybe-call @editAction) someArg)}}>Edit</button>
1 Like

I also like:

tryInvoke(this, 'editAction', attrs);

so that there’s no error when no action was passed

I believe there is the optional helper, which you can use just like you mentioned:

<button {{on='click' (action (optional @editAction) someArg)}}>Edit</button>

optional is not in Vanilla Ember. You are thinking of the ember-composable-helpers addon.

tryInvoke is in Vanilla Ember. So is doing

if (this.editAction) { this.editAction(); }