ember-truth-helpers icon indicating copy to clipboard operation
ember-truth-helpers copied to clipboard

Does not handle aysnc conditions

Open JonForest opened this issue 6 years ago • 2 comments

We recently had a scenario where we were using the and helper like this:

{{#if (and (is-permitted 'deleteFile') showDelete)}}
 <button>Delete</button>
{{/if}}

The is-permitted helper returned a DS.PromiseObject, which is handled correctly by the if component. The and helper evaluates this code with the DS.PromiseObject

if (truthConvert(params[i]) === false) {
      return params[i];
 }

The truthConvert(<DS.PromiseObject>) === false, so the condition passes and returns the Promise object to the if component. When the promise evaluates to true, the Delete button is shown, regardless of the value for showDelete - that param is never evaluated by the truth helper.

I started to mock up a replacement, before I found that ember-promise-helpers could be used.

{{#if (and (await (is-permitted 'deleteFile')) showDelete)}}
 <button>Delete</button>
{{/if}}

So... I wonder if it's worth updating the docs to cover this scenario?

JonForest avatar Jul 15 '18 23:07 JonForest

If you are interested, the code for the async and helper is below:

import { helper } from '@ember/component/helper'
import RSVP from 'rsvp'
import DS from 'ember-data'

export function asyncAnd (params) {
  if (!params || !params.length) return DS.PromiseObject.create({promise: RSVP.resolve(true)})

  // Need to wait for all params to be complete
  const promises = params.map(param => {
    if (typeof param.then === 'function') return param
    return RSVP.resolve(param)
  })

  // Look to see if any of the results returned a 'false'.  If they did, then the whole check fails
  const result = RSVP.all(promises)
    .then(results => {
      for (let i = 0; i < results.length; i++) {
        if (!result) return result
      }
      return results[results.length - 1]
    })

  return DS.PromiseObject.create({promise: result})
}

export default helper(asyncAnd)

It's a bit crude, and might not actually work, but I think it gets the idea across.

JonForest avatar Jul 15 '18 23:07 JonForest

FWIW, I think you can do something a bit simpler in the helper:

export default helper(function asyncAnd (params) {
  let promise = RSVP.all(params)
    .then(results => {
      for (let i = 0; i < results.length; i++) {
        if (!result) return result
      }
      return results[results.length - 1]
    })

  return DS.PromiseObject.create({promise: result})
})

rwjblue avatar Jul 16 '18 13:07 rwjblue

Closing as stale. I see mentions of PromiseObject which is not a recommended API nowaday.

locks avatar Aug 09 '23 02:08 locks