Balázs
Balázs

Reputation: 2929

Aurelia - bound control's backing property setter called twice

I implemented a custom control which is basically a styled file uploader specifically for images, which allows the preview of images. The problem is that I experience a weird behavior: the setter of the property to which the regular HTML <input type="file"> is bound gets called twice in FF 53, IE 11, Edge 38, but not in Chrome 57. I have no idea why this happens, perhaps some of you have experienced similar behavior and know a solution.

The markup is the following:

<template>

  <!-- markup for preview functionality, irrelevant here -->

  <input type="file" class="sr-only" id="${id}" aria-describedby="${id}-help" multiple accept="image/*" files.bind="imageGroup" />

  <label class="btn btn-default btn-sm" aria-hidden="true" for="${id}" role="button">
    <i class="fa fa-plus" aria-hidden="true"></i> Add files
  </label>

</template>

The backing TypeScript code:

import { bindable, bindingMode, autoinject } from 'aurelia-framework';
import { DialogService } from 'aurelia-dialog';
import { Confirm } from '../confirm';

@autoinject
export class ImageUploader {
  @bindable({ defaultBindingMode: bindingMode.twoWay }) imageArray: Array<File> = null;
  @bindable id: string = null;
  @bindable maxImageCount: number = 10;
  @bindable maxFileSizeKiloBytes: number = 2048;

  constructor(private dialogService: DialogService) { }

  idChanged(newValue: string) {

    if (!newValue) {
      throw Error('The id parameter needs a value in order to make the file uploader work.');
    }

  }

  set imageGroup(fileList: FileList) {
    console.log('imageGroup');
    if (!fileList || !fileList.length) return;
    if (!this.imageArray) return;

    for (let i = 0; i < fileList.length; ++i) {
      let file = fileList.item(i);

      if (this.imageArray.length >= this.maxImageCount) {
        // TODO: Alert: maximum number of images reached
      } else {
        if (file && file.type.startsWith('image/')) {
          // Size is in bytes. Div by 1024 to get kilobytes.
          if (file.size / (1024) > this.maxFileSizeKiloBytes) {
            // TODO: Alert: Image file too large.
          } else {
            this.imageArray.push(file);
          }
        } else {
          // TODO: Alert: unsupported media type.
        }
      }
    }
  }
}

As you can see, I use the "hide the default file uploader and style a bound label instead" trick to style the uploader itself - I only say this to point out that I've checked if maybe this is the cause, but it's not, the same behavior can be experienced if I show the default file uploader and use that.

You can also see that I've bound the <input type="file"> to a setter-only property. I've done that because this property is basically just a proxy which populates another array (which is not in the control, this is what is bound to the control) which is necessary because I need to allow the user to upload files in multiple turns so that (s)he can select files from different folders.

The rest of the code is irrelevant, because the console.log line runs twice whenever I select some files, so the error is located somewhere here - I am just unable to figure out exactly what is causing this to happen.

Any help is appreciated.

Upvotes: 0

Views: 311

Answers (1)

jmvtrinidad
jmvtrinidad

Reputation: 3658

I confirmed that using setter on that case causes to call your setter two times in FF, aurelia created another setter for every property that needs to observe. However you can achieve same behavior using observable with minimal changes and also provided encapsulation in this case. See those gist.run link. More info here.

UPDATE

For Firefox this is still open as a bug. But a workaround using change.delegate as elefantino and abemeister said works on FF, see gist here.

Upvotes: 1

Related Questions