Referencing other VM collections from inside a repeat.for

I’m building a simple file upload to get familiar with Aurelia and ran into this seemingly simple issue.

If you just want to see a quick example of the issue, https://gist.run/?id=e04b44bf6fd5c53668517fb9c5faaa04

Say I have an array of files and an array of uploadProgresses. In my view, I have
<li repeat.for='file in files'>${file.name} - ${uploadProgresses[$index]}</li>

I want this to be updated whenever the uploadProgresses changes. However, repeat.for doesn’t seem to watch other VM collections (works for primitives).

Currently, I have 3 solutions:

  • Merge both arrays into an array of objects (file, progress) and just use it in repeat.for. It works but requires this higher level array

  • Aurelia gitter suggested using a signal whenever uploadProgresses changes. Again it works but this has extra boilerplate

  • @Alexander-Taran suggested reassigning the array with a modified temp array. For example:

    const newRandoms =  this.randoms.slice()
    newRandoms.splice(1, 1, Math.random())
    this.randoms = newRandoms 
    

    https://gist.run/?id=e04b44bf6fd5c53668517fb9c5faaa04
    This works but it seems inconsistent with other observable or binding behaviors. Using splice should work as well?

I am wondering if there’s a better way to achieve this or am I missing something basic?

uploadProgresses[$index] expression will be updated when either uploadProgresses or $index changes.

----- Update:

You already stated the following snippet in your post. Sorry I didn’t read carefully


To work around this, I suggest use different model. The model can be a wrapper for both the file and uploading progress, this way, it works better and helps group your data better;

class FileUploadModel {
  constructor(file) {
    this.file = file;
    // other fields
    // ...
    // listen to progress event
    this.progress = 0;
  }
}

class App {
  constructor() {
    this.uploads = [];
  }
  onSelectFiles(files) {
    for (let i = 0, ii = files.length; ii > i; ++i) {
      this.uploads.push(new FileUploadModel(file));
    }
  }
}
<template>
  <input type='file' multiple change.trigger='onSelectFiles($event.target.files)' />
  <ul>
    <li repeat.for='upload of uploads'>
      # ${$index} Time - ${upload.estimateUploadTime} <-> Random - ${upload.progress }
    </li>
  </ul>
  <h2>Randoms</h2>
</template>

gist https://gist.run/?id=93563c62b0d964a0b622d163ceea580d

2 Likes

Sorry if I wasn’t clear. This is similar to the first solution with the added benefit of using a class. So far I like this solution the best although I still wonder if we can do something to make it just work. But I still don’t know enough about Aurelia to guess why this wouldn’t just work - particularly why reassigning works but splice doesn’t.

@lovetostrike take the win (-:
or pretty soon you’ll want to add a third array for another property of the “object”.

2 Likes