Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new basic helpers to Ember #388

Closed
178 changes: 178 additions & 0 deletions text/0000-basic-template-helpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
- Start Date: 2018-10-13
- RFC PR: https://github.com/emberjs/rfcs/pull/388
- Ember Issue: (leave this empty)

# Add new basic template helpers to Ember

## Summary

Add new built-in template helpers to perform basic boolean operations and comparisons in templates, identical to some of the
helpers in [ember-truth-helpers](https://github.com/jmurphyau/ember-truth-helpers).

This is a resurrection of [RFC #152](https://github.com/emberjs/rfcs/pull/152) that was opened over
by @martndemus two years ago, updated to reflect the new start of the things in Ember, with the glimmer
VM powering our templates.

## Motivation

It is a very common necessity to almost every Ember app to perform certain operations like compare
by equality, negate a value or perform boolean operations, and often the most convenient place to
do it is right in the templates.
Because of that, `ember-truth-helpers` is probably the single most installed addon that exists, either
directly by apps or indirectly by other addons that those apps use.

The fact that this addon is so popular is very telling and Ember.js should consider moving into the
core of the templating engine at least some of those helpers.

A second reason is that I believe it would help making Ember more approachable by newcomers
that have _some_ experience in other frameworks. One of the most shocking moments that developers
that are familiar with React, Vue or Angular experience when trying Ember is that they cannot,
at least out of the box, perform the most basic logical comparisons and operation they are so used to
in JSX or Vue/Angular templates.

A third reason is that if we implement those super common helpers in the Glimmer VM, that would open
a important vector of low level optimization.

Consider the following template:

```hbs
{{#if (and @featureEnabled this.expensiveComputedProperty @model.asyncEDRelationship.length)}}
{{!-- some logic --}}
{{else}}
{{!-- some other logic --}}
{{/if}}
```

Because of the way Ember helpers work, all their input parameters are eagerly evaluated by the
Glimmer VM and passed to the helpers. This might include computationally expensive computed properties,
or hitting code paths that trigger network requests.

Implementing helpers like `and` or `or` at a lower lever would allow those helpers to be evaluated in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Implementing helpers like `and` or `or` at a lower lever would allow those helpers to be evaluated in
Implementing helpers like `and` or `or` at a lower level would allow those helpers to be evaluated in

short circuit, so if `@featureEnabled` is false, neither of the following arguments will ever evaluated.

Going one step further into the optimizing compiler world, if **every** invocation of the component
with this template receives `@featureEnabled={{false}}`, the compiler could even completely remove
the conditional and the truthy branch of the if from compiled output.

The last reason is that if the [RFC #367](https://github.com/emberjs/rfcs/pull/367) eventually gets merged,
these helpers are the perfect candidates to create adoption friction in the community because of how
pervasive its usage it in so many templates, forcing them to explicitly importing them on most templates
or adding them to the proposed `prelude.hbs` file.

## Detailed design

The process consists on deciding what helpers from `ember-truth-helpers` we consider the most important
and move them into Ember.js itself or even the Glimmer VM, in a fully backwards compatible way.

I propose to add to core at least:

- `eq`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will that be == or === semantics?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, IMHO, it should be ===

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to maintain backwards compatibility it has to be ===. And even if compatibility wasn't a concern I'd still prefer triple equal

- `not-eq`
- `not`
- `and`
- `or`
- `gt` and `gte`
- `lt` and `lte`

Those helpers are very low lever and generally useful in both Ember and Glimmer.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cibernox low lever typo

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Those helpers are very low lever and generally useful in both Ember and Glimmer.
Those helpers are very low level and generally useful in both Ember and Glimmer.


I propose to not add:

- `is-array` (uses `Ember.isArray`)
- `is-empty` (uses `Ember.isEmpty`)
- `is-equal` (uses `Ember.isEqual`)
- `xor` (not very common)

When this feature is implemented, we would update `ember-truth-helpers` to automatically remove
the promoted helpers from the code when the Ember is above a certain version number.

The implementation details of each helper:

#### `{{not}}`

- Unary operation. Throws an error if not called with **exactly** one argument.
- Equivalent of `!<argument>`

#### `{{eq}}`

- Binary operation. Throws an error if not called with **exactly** two arguments.
- Equivalent of `<arg1> === <arg2>`

#### `{{not-eq}}`

- Binary operation. Throws an error if not called with **exactly** two arguments.
- Equivalent of `<arg1> !== <arg2>`

#### `{{gt}}`

- Binary operation. Throws an error if not called with **exactly** two arguments.
- Equivalent of `<arg1> > <arg2>`
- Lazy: If the first argument is `null` or `undefined`, the second argument is never evaluated

#### `{{gte}}`

- Binary operation. Throws an error if not called with **exactly** two arguments.
- Equivalent of `<arg1> >= <arg2>`
- Lazy: If the first argument is `null` or `undefined`, the second argument is never evaluated

#### `{{lt}}`

- Binary operation. Throws an error if not called with **exactly** two arguments.
- Equivalent of `<arg1> < <arg2>`
- Lazy: If the first argument is `null` or `undefined`, the second argument is never evaluated

#### `{{lte}}`

- Binary operation. Throws an error if not called with **exactly** two arguments.
- Equivalent of `<arg1> <= <arg2>`
- Lazy: If the first argument is `null` or `undefined`, the second argument is never evaluated

#### `{{and}}`

- Binary or greater operation. Throws an error if called with **less than** two arguments.
- Equivalent of `<arg1> && <arg2> && ... && <argN>`. That means it returns the last truthy value or the first falsy value.
- Definition of truthiness: The same the `&&` operator has in javascript.
- Lazy: It starts evaluating arguments in order and short-circuits as soon as one of them is falsy.

#### `{{or}}`

- Binary or greater operation. Throws an error if called with **less than** two arguments.
- Equivalent of `<arg1> || <arg2> || ... || <argN>`. That means it returns the first truthy value or the last value.
- Definition of truthiness: The same the `||` operator has in javascript.
- Lazy: It starts evaluating arguments in order and short-circuits as soon as one of them is truthy.

## How we teach this

The introduction of these helpers does not impact the current mental model for Ember applications.

In addition to API and Guides documentation with illustrative examples of usage of the various helpers,
and explanation of their short circuiting nature might be warranted.

## Drawbacks

We are increasing the public API of the framework, and every line of code is a liability, although
those helpers are extremely straightforward.

## Alternatives

An alternative path would be to include `ember-truth-helpers` in the default blueprint for apps and
addons.
However, this alternative loses strength due to the fact that it is not possible to implement short
circuiting helpers in Ember's userspace, and because even if an addon is added by default, user
can still choose to remove it, so addons would not just be able to rely on them.

## Unresolved questions

The main unresolved question is what helpers we deem worthy of being moved into the core, and also
if we want any other helpers not mentioned above.

In particular, I'm sitting on the fence about `not-eq`.
It is not _really necessary_, in the same way `{{unless foo}}` can also be expressed as `{{#if (not foo)}}`,
but it may be convenient.

Other helpers that come to mind that _could_ also be worth adding:

- `add`, `subtract`, and other arithmetic operators.
- `{{#await promise as |value|}}` to render a block conditionally if a promise resolves or fails.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely useful IMHO, but should be a separate RFC.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


If there is interest in them, I advocate creating standalone RFCS for each one of them.