2017-03-17 3 views
4

In Angular2 Ich habe eine Komponente, die einen Dienst zum Hochladen einer Datei auf Amazon S3 verwendet.So bewahren Sie die 'This' Instanz in einer Callback-Funktion

Meine Komponente (vereinfacht):

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 
} 

Mein Service (vereinfacht):

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) 
      }); 
    } 

Das Problem, dass, wenn die Callback-Funktion aus dem Dienst entlassen wird, wird this auf den Dienst bezieht und this._loading kann nicht gefunden werden.

Frage: Wie kann ich die this Instanz in meiner Callback-Funktion erhalten, (this in dem Rückruf zu component und nicht service zeigen muss)

+0

Mögliche Lösung könnte sein, auch Instanz Funktion https: // GitHub. com/Microsoft/TypeScript/wiki /% 27dies% 27-in-TypeScript # use-instance-Funktionen – yurzui

Antwort

3

Während @Gunter richtig ist, ich glaube, Sie die this in den Rückruf behalten möchten Sie actualy dieser Funktion übergeben:

uploadCallback(err, data) { 
    this._loading = false; // this is the "this" you want to keep 
} 

dann wäre es so etwas wie dieses:

this._s3service.uploadFile(fileObject,()=>this._loading = false); 
// or 
this._s3service.uploadFile(fileObject,()=>this.uploadCallback()); 
// or 
this._s3service.uploadFile(fileObject, this.uploadCallback.bind(this)); 

Beachten Sie auch, dass es interessant sein könnte, eine Observable statt vorbei einen Rückruf zu verwenden:

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(); 
     }); 
    }); 
} 

dann:

this._s3service.uploadFile(fileObject).subscribe(data=>console.log(data)) 
+0

Sie verstanden meine Frage richtig. +1 für die Verwendung von Observablen. – Vingtoft

10

Verwenden arrow Funktionen

}, (err, data) => { // <=== What to do here?! 

sie sind für genau diesen Zweck, für this auf die Klasse zu zeigen Instanz, in der die Funktion deklariert ist.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Wenn Sie eine Funktion Verweis übergeben .bind(this) könnte bequemer sein, weil es nicht die Parameter erfordert überhaupt zur Liste während => sie zweimal erfordern würde

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)); 
} 

der gleichen welche Pfeilfunktionen

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)); 
} 
+3

Wenn Sie aus irgendeinem Grund keine Pfeilfunktionen verwenden können oder wollen, können Sie die Funktion "binden" (myFunction() {} .bind (this)). Das war der Weg vor der ES6. –

+0

Ich überlegte, es hinzuzufügen, aber mit dem Beispiel in der Frage denke ich nicht, dass es eine gute Idee ist. Ich habe das Beispiel so angepasst, dass es mehr Sinn ergibt. –

+0

Ich habe vielleicht nicht klar genug gefragt: Das Problem ist, dass der Callback "dies" auf den Dienst statt auf die Komponente verweist. Ich benötige den Code im Callback, der ausgeführt wird, wobei 'dies' auf die Komponente zeigt (nicht Dienst). Wenn ich nicht mistanken, zeigt deine Antwort, wie man "das" an den Dienst bindet. – Vingtoft

0

Sie könnten Ihre uploadFile ändern, um eine promise zurückzugeben.

und behandeln Sie den Fehler Fall von Ihrer Komponente, wie es sein sollte. So etwas wie

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) 
     }); 
     } 
    }); 

Und Sie von Ihrer Komponente könnte dies tun

this._s3service.uploadFile(fileObject).then((err, data)=> { 
    this._loading = false; 
}); 
+0

Warum? (err, data) ', warum nicht' resolve (data) 'und' reject (err) '? – n00dl3

+0

@ n00dl3 Ja Fehler sollte zurückgewiesen werden. Aber in diesem Fall hängt es von den Antwort-Parametern von der Upload-Funktion Anruf – lintu

+0

und, was ist das Problem ??? '(err, data) => err? ablehnen (err): resolve (data)' – n00dl3

Verwandte Themen