Skip to content

Optional closure actions #90

@rlivsey

Description

@rlivsey

Short version

Closure actions break if you give them an undefined value, making it impossible to have optional actions:

{{x-foo on-bar=(action attrs.not-passed baz)}}

Long Version

Consider a hypothetical image component which sends an action when it's loaded:

{{x-image image=image on-load=(action "imageLoaded")}}

Now consider an image-list component which displays a list of images:

{{#each attrs.images as |image|}}
  {{x-image image=image on-load=(action "imageLoaded" image)}}
{{/each}}

So far so good, now this component wants to just pass the loaded event up to its parent component.
Nice and easy to do with closure actions:

{{#each attrs.images as |image|}}
  {{x-image image=image on-load=(action attrs.on-image-loaded image)}}
{{/each}}

This is called as so:

{{x-image-list images=images on-image-loaded=(action "imageLoaded")}}

But we don't want to require having on-image-loaded being passed in, this now breaks:

{{x-image-list images=images}}

I know, we'll make the action optional:

{{#each attrs.images as |image|}}
  {{x-image image=image on-load=(if attrs.on-image-loaded (action attrs.on-image-loaded image))}}
{{/each}}

No dice, (if) isn't lazily evaluated, so the (action) helper gets called with undefined regardless.

Now we're stuck, we can't use closure actions anymore and have to go back to using (action "imageLoaded") and sendAction which is happy if the action isn't wired up.

Possible Solutions

In order of personal preference:

1. Make (if) lazily evaluate

Currently the (if) helper executes both branches before testing (as helpers receive values and not streams), regardless of whether the predicate is interpreted as true or false.

If this instead lazily evaluated then it would be safe to use the action keyword inside an if as the action keyword would only be called if valid:

{{x-image on-load=(if attrs.on-image-loaded (action attrs.on-image-loaded image))}}

2. Add optional=true flag to action keyword

{{x-image on-load=(action attrs.on-image-loaded image optional=true)}}

This would disable the checks in the action keyword.

I personally prefer this to 3. because it doesn't require an entirely new keyword but still makes it clear what's going on.

3. Add (optional-action) helper/keyword

{{x-image on-load=(optional-action attrs.on-image-loaded image)}}

This simply guards against undefined actions before passing along to the action keyword, but is an entirely new helper/keyword just for a guard clause

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions