Update
This post is no longer up to date, as you can now bind to the files attribute of your input element:
<input type="file" files.bind="files" />
This would bind to the files property of your viewmodel.
This was the original post:
I’m getting up to steam with Aurelia and really like it. One thing that is missing, though, is one-way databinding from view to viewmodel. There is a GitHub issue for it, so I suspect this will be included at some time. But for now, you need to work around this limitation.
This is a problem when working with <input type="file">
controls. The property you need to get the file is the files
property, but this is a readonly property. Having Aurelia bind to this property via files.bind='viewModelProperty'
will generate an error stating: “setting a property that has only a getter”.
So here’s a workaround. It’s not very elegant, so if you know of something better, please let me know (I don’t have comments on this blog yet, but drop me a line via Twitter).
First, tell the input to call a method on your viewmodel when a file has been selected, and pass in the event ($event
):
<input type="file" change.delegate="fileSelected($event)"></input>
The method that is called will just store the selected file in a property of the viewmodel:
fileSelected() { let reader = new FileReader(); let file = this.$event.target.files[0]; reader.readAsDataURL(file); this.fileName = file.name; reader.onload = () => { this.file = reader.result; }; }
Let’s break that down:
let reader = new FileReader(); let file = this.$event.target.files[0]; reader.readAsDataURL(file);
This uses the new FileReader interface to read out the file that we received from the change
event of our <input>
element. We also store the filename in our viewmodel.
this.fileName = file.name; reader.onload = () => { this.file = reader.result; };
Because the FileReader
works asynchronously, we add a handler to the onload
event and store the result of the read in the file
property. (Thanks to Kamal Bhatt for pointing me to the arrow function technique, which is new in ES6).
You can now put the value of this file
property in a JSON object and post it to the server. It will be a Base64 string that you can get the bytes of by doing something like:
var startIndex = settingsDto.Logo.IndexOf(",") + 1;var base64String = settingsDto.Logo.Substring(startIndex);var imageBytes = Convert.FromBase64String(base64String);var imageExtension = settingsDto.LogoName.Substring(settingsDto.LogoName.LastIndexOf("."));