Reputation: 14656
In Angular2 I have a component that uses a service for uploading a file to Amazon S3.
My component (simplified):
private _loading = true;
// use service to upload file
this._s3service.uploadFile(fileObject, this.uploadCallback)
// use this function as a callback
uploadCallback(err, data) {
this._loading = false; // this crashes because of 'this' is referring to service instead of component
}
My service (simplified):
private getS3(): any {
// Get or create AWS instance
return s3;
}
public uploadFile(selectedFile, callback): boolean {
this.getS3().upload({
Key: key_name,
ContentType: file.type,
Body: file,
StorageClass: 'STANDARD',
ACL: 'private'
}, function(err, data){ // <=== What to do here?!
callback(err, data)
});
}
The problem is that when the callback function is fired from the service, this
is referring to the service and this._loading
cannot be found.
Question: How can I preserve the this
instance in my callback function, (this
in the callback must point to component
and not service
)
Upvotes: 4
Views: 520
Reputation: 1102
You could change your uploadFile
to return a promise.
and handle the error case from your component as it should be. Something like
public uploadFile(selectedFile): boolean {
return new Promise((resolve, reject) => {
this.getS3().upload({
Key: key_name,
ContentType: file.type,
Body: file,
StorageClass: 'STANDARD',
ACL: 'private'
}, function(err, data){ // <=== What to do here?!
resolve(err, data)
});
}
});
And you could from your component do this
this._s3service.uploadFile(fileObject).then((err, data)=> {
this._loading = false;
});
Upvotes: 0
Reputation: 21584
While @Gunter is right, I think you want to keep the this
in the callback you actualy pass to that function :
uploadCallback(err, data) {
this._loading = false; // this is the "this" you want to keep
}
then it would be something like this:
this._s3service.uploadFile(fileObject, ()=>this._loading = false);
// or
this._s3service.uploadFile(fileObject, ()=>this.uploadCallback());
// or
this._s3service.uploadFile(fileObject, this.uploadCallback.bind(this));
Also note that it might be interesting to use an Observable
instead of passing a callback :
public uploadFile(selectedFile): Observable<any> { // "<any>" because I don't know what is the type of "data"
return Observable.create((observer) => {
this.getS3().upload({
Key: key_name,
ContentType: file.type,
Body: file,
StorageClass: 'STANDARD',
ACL: 'private'
}, (err, data)=> {
if(err)
observer.error(err);
else
observer.next(data);
observer.complete();
});
});
}
then:
this._s3service.uploadFile(fileObject).subscribe(data=>console.log(data))
Upvotes: 3
Reputation: 658027
Use arrow functions
}, (err, data) => { // <=== What to do here?!
they are for exactly that purpose, for this
to keep pointing at the class instance where the function is declared.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
If you pass a function reference .bind(this)
might be more convenient, because it doesn't require to list the parameters at all while =>
would require them twice
myCallback(err, data){ // <=== What to do here?!
callback(err, data)
}
public uploadFile(selectedFile, callback): boolean {
this.getS3().upload({
Key: key_name,
ContentType: file.type,
Body: file,
StorageClass: 'STANDARD',
ACL: 'private'
}, this.myCallback.bind(this));
}
The same which arrow functions
public uploadFile(selectedFile, callback): boolean {
this.getS3().upload({
Key: key_name,
ContentType: file.type,
Body: file,
StorageClass: 'STANDARD',
ACL: 'private'
}, (err, data) => this.myCallback(err, data));
}
Upvotes: 11