2017-10-30 1 views
2

Ich habe eine Liste von Objekten. Der Benutzer kann auf einen klicken, der dann eine untergeordnete Komponente lädt, um diese Komponente zu bearbeiten.Angular4 - wie sicherzustellen, dass ngOnDestroy endet, bevor Sie weg navigieren

Das Problem, das ich habe, ist, dass, wenn der Benutzer zurück zu der Liste Komponente, die Kind-Komponente muss einige Aufräumarbeiten in der ngOnDestroy-Methode - die einen Aufruf an den Server, um eine abschließende "Patch" der machen müssen Objekt. Manchmal kann diese Verarbeitung etwas langsam sein.

Natürlich passiert, der Benutzer kommt zurück auf der Liste, und dieser API-Aufruf wird abgeschlossen, bevor die Datenbanktransaktion von ngOnDestroy abgeschlossen ist, und somit der Benutzer veraltete Daten sieht.

ngOnDestroy(){ 
    this.destroy$.next(); 
    this.template.template_items.forEach((item, index) => { 
     // mark uncompleted items for deletion 
     if (!item.is_completed) { 
     this.template.template_items[index]['_destroy'] = true; 
     }; 
    }); 
    // NOTE 
    // We don't care about result, this is a 'silent' save to remove empty items, 
    // but also to ensure the final sorted order is saved to the server 
    this._templateService.patchTemplate(this.template).subscribe(); 
    this._templateService.selectedTemplate = null; 
    } 

Ich verstehe, dass synchrone Anrufe nicht zu tun ist, da es die Benutzeroberfläche/den gesamten Browser blockiert, was nicht toll ist.

Ich bin sicher, es gibt mehrere Möglichkeiten, dies zu lösen, aber wirklich nicht wissen, welche die beste ist (vor allem, da Angular keine Sync-Anfragen unterstützt, so würde ich auf Standard-Ajax zurückgreifen müssen).

Eine Idee, die ich dachte, war, dass die ngOnDestroy einen "Marker" an die API übergeben konnte, und es könnte dann dieses Objekt als "Verarbeitung" markieren. Wenn die Listenkomponente seinen Aufruf des Fall ist, könnte es jedes Objekt untersuchen, um zu sehen, ob es diesen Marker hat und zeigt eine Taste in diesem Zustand für jedes Objekt ‚veraltete Daten aktualisieren‘ (die 99% der Zeit nur ein einzelne Element ohnehin wäre, der letzte, den der Benutzer bearbeitet hat). Scheint ein bisschen eine Mist Workaround und erfordert eine Tonne zusätzlichen Code im Vergleich zu nur einen asynchronen Anruf zu einem Synchronisierungsanruf ändern.

Andere haben müssen ähnliche Probleme auftreten, aber ich kann keine klare Beispiele außer this sync one zu finden scheinen.

EDIT

Beachten Sie, dass dieses Kind Komponente bereits eine CanDeactive Wache auf sie. Er fordert den Benutzer auf, zu bestätigen (dh Änderungen zu verwerfen). Wenn sie zum Bestätigen klicken, wird dieser Bereinigungscode in ngOnDestroy ausgeführt. Beachten Sie jedoch, dass dies keine typische eckige Form ist, bei der der Benutzer Änderungen wirklich "verwirft". Bevor diese Seite verlassen wird, muss der Server eine gewisse Verarbeitung der endgültigen Datenmenge vornehmen. Im Idealfall möchte ich nicht, dass der Benutzer verlässt, bis ngOnDestroy fertig ist - wie kann ich es zwingen, zu warten, bis dieser API-Aufruf beendet ist?

Mein CanDeactive-Wächter ist fast genauso implementiert wie in der official docs für die Hero-App, indem er sich in einen allgemeinen Dialogdienst einklinkt, der den Benutzer auffordert, auf der Seite zu bleiben oder fortzufahren. Hier ist sie:

canDeactivate(): Observable<boolean> | boolean { 
    console.log('deactivating'); 
    if (this.template.template_items.filter((obj) => { return !obj.is_completed}).length < 2) 
     return true; 

    // Otherwise ask the user with the dialog service and return its 
    // observable which resolves to true or false when the user decides 
    return this._dialogService.confirm('You have some empty items. Is it OK if I delete them?'); 
    } 

Die docs machen es nicht für meine Situation klar aber - auch wenn ich meine Bereinigungscode bewegen sich von ngOnDestroy zu einem „JA“ Methodenhandler zum Dialog, es hat immer noch die api anrufen , also würde der YES-Handler immer noch vervollständigen, bevor die API es getan hat und ich bin mit dem gleichen Problem zurück.

UPDATE

Nachdem alle Kommentare lesen Ich vermute, die Lösung so etwas wie dieses.Ändern Sie die Wache aus:

return this._dialogService.confirm('You have some empty items. 
     Is it OK if I delete them?'); 

zu

return this._dialogService.confirm('You have some empty items. 
     Is it OK if I delete them?').subscribe(result => { 
     ...if yes then call my api and return true... 
     ...if no return false... 
     }); 
+0

Schauen Sie sich CanDeactivate oder Wächter im Allgemeinen an, siehe [hier] (https://stackoverflow.com/questions/41148020/angular2-candeactivate-guard) zum Beispiel. – Matt

+0

Danke, das ist nicht die Lösung. Ich werde die Frage aktualisieren, um zu erklären, warum. – rmcsharry

+0

@rmcsharry das ist eine Lösung. Ihre Benutzerbestätigung in 'CanDeactivate' ist keine Lösung. –

Antwort

2

Wie Sie gesagt haben, gibt es viele Möglichkeiten, und sie hängen von anderen Details, wie Sie Ihre gesamte App, Datenfluss und ux-Flow-Setup, aber es Es fühlt sich an, als ob Sie einen Blick auf die CanDeactivate Guard-Methode werfen möchten, die sicherstellt, dass der Benutzer die Route nicht verlassen kann, bis Ihre Observable<boolean>|Promise<boolean> auf true aufgelöst sind.

So ist es eine Möglichkeit für asynchrone warten, bis Ihr Dienst bestätigt, dass die Dinge auf dem Server geändert werden.

[UPDATE]

es entlang dieser Linien auf der Benutzerbestätigung Umsetzung aber etwas hängt ...

waitForServiceToConfirmWhatever(): Observable<boolean> { 
    return yourService.call(); //this should return Observable<boolean> with true emitted when your server work is done 
    } 

canDeactivate(): Observable<boolean> { 

    if(confirm('do you want to leave?') == true) 
     return this.waitForServiceToConfirmWhatever(); 
    else 
     Observable.of(false) 
    } 
+0

Danke, aber die Route hat bereits einen CanDeactive Wächter. Es wird der Benutzer mit Ja/Nein aufgefordert, und wenn er mit Ja antwortet, wird der ngOnDestroy-Code ausgelöst, der die Bereinigung durchführt. Ich kann die Reinigung vorher nicht tun. Daher ein Catch-22. – rmcsharry

+0

was ich tun würde ist Ja/Nein-Eingabeaufforderung nur als eine normale Click-Handler in der Komponente zu haben. Wenn der Nutzer dann auf "Ja" klickt, würde ich "router.navigate ['whereveryYourListIs']' '. und dann 'CanDeactivate' wie in meiner Antwort beschrieben. Ich sehe keinen Grund, dass Sie in der 'CanDeactivate'-Wache mit Ja/Nein umgehen. Das gehört zur Komponente selbst, nicht einmal in Komponenten 'ngOnDestroy', da es nur eine normale Benutzeraktion ist wie alles andere, was nicht notwendigerweise mit der Lebensdauer der Komponenten in Beziehung steht, da das einfache 'Nein' vom Benutzer nichts tun sollte. hoffe das macht Sinn. –

+0

Ich handle nicht mit ja/nein. Der Benutzer kann zu irgendwohin navigieren und ich weiß nicht (oder kümmert sich nicht), wohin er als nächstes gehen möchte. Der CanDeactive wird ausgelöst, wohin auch immer er geht. Es erkennt, ob noch Arbeit zu erledigen ist (dh das Objekt befindet sich nicht in einem "endgültigen" Zustand) und der Wächter gibt die Aufforderung auf (im Grunde, willst du wirklich gehen und diese anderen Sachen wegwerfen?). Wenn Sie auf NO klicken, wird nichts getan, wie es CanDeactive beabsichtigt - der Benutzer bleibt auf der Seite. Wenn sie Ja anklicken, erlaubt der Wächter ihnen fortzufahren, die Komponente wird entladen und ngOnDestroy wird automatisch aufgerufen. – rmcsharry

1

One „Abhilfe“ ich denken kann, haben, ist die Liste mit Sitz in Klient. Sie haben die Liste als JS-Array oder -Objekt und zeigen die darauf basierende Benutzeroberfläche an. Nachdem Sie auf dem Detailbildschirm bearbeitet haben, haben Sie ein veraltetes Flag für das Element, das der Dienst ngOnDestroy aufgerufen hat, während die anderen zugehörigen Daten aktualisiert werden.

+0

Das ist eine gute Idee, die Ich mag und muss wahrscheinlich in v2 tun, um den "Offline" -Modus zu unterstützen (gekoppelt mit lokalem Speicher). Also +1, danke für die Idee. – rmcsharry

Verwandte Themen