ember.js icon indicating copy to clipboard operation
ember.js copied to clipboard

[Bug] hasMany related records referenced with {{each}} helper are not await able

Open ztjohns opened this issue 2 years ago • 0 comments

🐞 Describe the Bug

{{each}} helper while iterating a hasMany array returns an array of Model objects rather then an expected array of Proxy Model Objects. This causes issues with components within the block not re-rendering or operating off of the hydration of the loaded record.

🔬 Minimal Reproduction

Setting up a simple model representation that would mimic my experience. The code below may have simple syntax errors so apologies, as it was hand typed not copy and pasted.

# primary model loaded in route
export default class Project extends Model {
  @hasMany('book', {async: true}) books
}
# secondary model
export default class Book extends Model {
  @belongsTo('author', {async: true}) author
  
  get authorId() { return this.belongsTo('author').id(); }
}
# example component
export default class TheBook extends Component {
  constructor() {
    this.super(...arguments);
    this.doAsync();
  }
  async doAsync() {
    console.info(this.args.book.authorId, this.args.book.isLoaded) // null, false
    let book = await this.args.book
    console.info(book.authorId, book.isLoaded) // null, false
  }
}
# example template referencing 'each' and 'hasMany'
{{#each this.model.books as |book|}}
  <TheBook @book={{book}} />
{{/each}}

😕 Actual Behavior

In the doAsync method the passed in reference is not await/then able. A workaround for this is to wrap the block in a check, but this prevents any type of rendering until all the hydration is complete which is not optimal.

{{#each this.model.books as |book|}}{{#if book.isLoaded}}
  <TheBook @book={{book}} />
{{/if}}{{/each}}

🤔 Expected Behavior

Because related records are always treated as promises, I would expect a proxy model to be yielded to the block that way if the related record was not hydrated you could await for the record to load.

# example component function expected behavior
async doAsync() {
  console.info(this.args.book.authorId, this.args.book.isLoaded) // null, false
  let book = await this.args.book
  console.info(book.authorId, book.isLoaded) // <an id>, true
}

🌍 Environment

  • Ember: - 3.28.5
  • Node.js/npm: -14.19,2
  • OS: - unix
  • Browser: - chrome

➕ Additional Context

Add any other context about the problem here.

ztjohns avatar Jul 14 '22 15:07 ztjohns