Asked By: Anonymous
I have the following code in my user-profile.html
:
<template>
<!--Fine-Uploader HTML is here
...-->
<script>
var uploader = new qq.FineUploader({
element: document.getElementById("uploader"),
debug: true,
cors: {
expected: true
},
request: {
endpoint: configOptions.baseUrl + '/api/assets',
customHeaders: {
Authorization: "Bearer " + localStorage.getItem("aurelia_id_token")
}
},
multiple: false,
callbacks: {
onComplete: function (id, fileName, responseJSON){
updateProfile(responseJSON.displayUrl);
}
}
});
</script>
</template>
As you can see, I would like to invoke updateProfile
upon successful completion of the upload. I’m getting as far as the onComplete
callback, and am correctly parsing the JSON response object. The problem is that the updateProfile
function is in a separate .js
file, called user-profile.js
.
I’m working in Aurelia, and this is the standard convention for creating an MVVM module, that is naming the files the same with .html
and .js
extensions. I’m not sure why they are not sharing scope in this case, but I’m getting the error:
Caught exception in 'onComplete' callback - updateProfile is not defined
I’ve also tried simply adding a standard script tag in the index file directly referencing the .js
file but it doesn’t work either, and seems like a bad idea anyway.
Any direction on this would be greatly appreciated.
Solution
Answered By: Anonymous
For third-party libraries, jQuery plugins, etc. it’s always a good idea to encapsulate it within custom attribute or custom element. Also, as Ashley said, you cannot have script
elements in a template.
fine-uploader
is a good candidate for custom element. Here’s a simplified version of one that I’m using in one of my projects:
<!-- fine-uploader.html -->
<template>
<a href="#" class="btn btn-default" role="button">Upload image</a>
</template>
// fine-uploader.js
import fine from 'fine-uploader';
import {bindable, inject} from 'aurelia-framework';
@inject(Element)
export class FineUploader {
@bindable endpoint; // URL to upload to
@bindable params = {}; // Any additional upload params
@bindable uploaded = () => {}; // Uploaded callback for consumers
constructor(element) {
this.element = element;
}
attached() {
this.uploader = new fine.FineUploaderBasic({
debug: true,
button: this.element.children[0],
callbacks: {
onComplete: (id, name, response) => {
if (this.uploaded) {
this.uploaded(response);
}
}
},
request: {
endpoint: this.endpoint,
customHeaders: {
'authorization': `Bearer ${localStorage.getItem("aurelia_id_token")}`
},
method: 'PUT',
params: this.params
},
scaling: {
sendOriginal: false,
sizes: [{ maxSize: 500 }]
},
validation: {
acceptFiles: '.jpg,.png'
}
});
}
detached() {
// Apparently, no destroy() method
// https://github.com/FineUploader/fine-uploader/issues/1038
//this.uploader.reset();
}
}
This way, you can use the same component from multiple places. Here’s the sample usage for your case:
<!-- user-profile.html -->
<template>
<require from="../resources/elements/fine-uploader"></require>
<!-- ... -->
<fine-uploader endpoint="/api/assets" params.bind="uploadParams"
uploaded.call="imageUploaded($event)"></fine-uploader>
</template>
// user-profile.js
export class UserProfile {
activate(id) {
this.uploadParams = {
userId: id // And/or any other data you need to send with the image upload
}
}
imageUploaded(response) {
this.updateProfile(response.displayUrl);
}
updateProfile(url) {
// ...
}
}