2017-07-25 5 views
1

Ich verwende eine Komponente, um Tabelle wie Daten zu rendern, gebe ich die Liste der Spalten, die Daten, wie die Daten zu jeder Spalte zuordnen, und schließlich eine Liste der Rohre zu gelten jede Spalte Daten.Verwendung von AsyncPipe programmgesteuert - Angular 2

So weit so gut, die einzige Problem ist, wenn eines dieser Rohre das Rohr async ist ...

Nach einigen beim Experimentieren fand ich heraus, dass, wenn der asyncpipe auf der Vorlage verwendet wird, die Transformationsmethode wird mehrmals aufgerufen. Wenn ich es jedoch programmatisch verwende, wird die Transform-Methode nur einmal aufgerufen (die Zeit, zu der ich sie aufruft).

Ich denke, der Grund, warum es mehrmals auf Vorlage aufgerufen wird, ist, weil es ein unreines Rohr ist, aber wie kann ich es programmgesteuert handhaben?

hier ein plunker zeigen, was ich gerade gesagt habe:

@Injectable() 
export class AsyncProvider { 
    constructor() {} 

    getById(id: number) { 
    // Just some mock data 
    return Observable.of({id, times_five: id*5}).delay(1000); 
    } 
} 

@Component({ 
    selector: 'my-app', 
    providers: [AsyncPipe, AsyncProvider] 
    template: ` 
    <div> 
     <p>Template async pipe</p> 
     <p>{{ asyncObj | async | json }}</p> 
     <hr> 
     <p>Programmatically async pipe</p> 
     <p>{{ asyncObjPiped | json }}</p> 
    </div> 
    `, 
    directives: [] 
}) 
export class App { 
    constructor(
    private _provider: AsyncProvider, 
    private _async: AsyncPipe 
) { 
    this.asyncObj = this._provider.getById(123); 
    this.asyncObjPiped = this._async.transform(this.asyncObj); 
    } 
} 

EDIT: Da AsyncPipe eine markForCheck führt() auf einem ChangeDetectorRef uppon neue Werte empfangen, habe ich versucht, auch die folgenden:

export class App { 
    constructor(
    private _provider: AsyncProvider, 
    private _async: AsyncPipe, 
    private _ref: ChangeDetectorRef, 
) { 
    this.asyncObj = this._provider.getById(123); 
    this.asyncObjPiped = this._async.transform(this.asyncObj); 

    setInterval(() => { 
     this._ref.detectChanges(); 
    }, 1000); 
    } 
} 

Ohne Erfolg :(

+0

Was meinst du mit ** handle es programmatisch ** was willst du tun? – Alex

+0

@ AJT_82 Ich erwarte, dass es sich so verhält, als würde ich die Pipe in der Vorlage benutzen (siehe den Plunker für Details), aber wie asyncPipe funktioniert (src code hier verfügbar: https://github.com/angular/angular /blob/master/packages/common/src/pipes/async_pipe.ts) muss die Pipe mehrmals aufgerufen werden, um das korrekte Ergebnis zu erhalten. Dies scheint der Grund zu sein, warum asyncpipe nicht rein ist, aber ich kann weder herausfinden, wie man dieses nicht-reine Verhalten programmatisch auf dem Controller "emuliert", noch habe ich irgendeine Idee, wo es im eckigen Code ist. – Ignasi

Antwort

0

Nach einigem Hin und Her habe ich einige Ergebnisse erzielt, hier ist es, was ich tun musste, um sie später zu finden.

Erste Methode: plunker

export class App { 
    constructor(
    private _provider: AsyncProvider, 
    private _async: AsyncPipe, 
) { 

    this.asyncObj = this._provider.getById(123) 

    let processPipe =() => { 
     this.asyncObjPiped = this._async.transform(new_asyncObj); 
    } 
    let new_asyncObj = this.asyncObj.finally(processPipe).repeat(2); 
    processPipe(); 
    } 
} 

Beachten Sie, dass eine neue Variable (new_asyncObj) benötigt wird, weil schließlich eher ein neues Objekt zurückzukehren scheint als die bestehende modifizieren. Und die Wiederholung (2) nach dem Finale so wird es das Versprechen auspacken.


Zweite Methode: plunker

export class App { 
    constructor(
    private _provider: AsyncProvider, 
    private _async: AsyncPipe, 
) { 

    this.asyncObj = this._provider.getById(123) 

    setInterval(() => { 
     this.asyncObjPiped = this._async.transform(this.asyncObj); 
    }, 500); 

    } 
} 

neu berechnen das Rohr alle 500 ms, einfach und effektiv, auch wenn ich für etwas Besseres erwartet hatte.


letzte Methode: plunker

export class App { 
    constructor(
    private _provider: AsyncProvider, 
    private _async: AsyncPipe, 
    private _ref: ChangeDetectorRef, 
    private zone: NgZone, 
) { 

    this.asyncObj = this._provider.getById(123) 

    this.zone.onMicrotaskEmpty 
     .subscribe(() => { 
     this.asyncObjPiped = this._async.transform(this.asyncObj); 
     this.asyncObjPiped = this._async.transform(this.asyncObj); 
     this._ref.detectChanges(); 
     }); 
    } 
} 

Mit NgZone und ChangeDetectorRef scheint als gut zu funktionieren, dachte sogar einige hässliche Hacking als Aufruf AsyncPipe zweimal, um den Wert auszupacken.

Wie auch immer, hoffe, dass es wirklich jedem helfen kann, der frustriert wird, wenn es darum geht, mit nicht reinen Rohren programmatisch umzugehen!

Jeder Vorschlag oder bessere Antwort immer noch willkommen!

+0

Ich mag die Idee, die asynchrone Pipe zu verwenden, da sie die OnPush-Änderungserkennung auslöst (wobei das manuelle Abonnieren nicht möglich ist). Und häufig benötigen Sie die beobachtete Variable in einer Methode. Gib dir ein A, damit das überhaupt funktioniert. Das Gesagte ... wäre schön, wenn es einen saubereren und besser dokumentierten Weg gäbe. –

Verwandte Themen