angularjs - Hur man uppdaterar vyn när Angular $http promise-anrop är omslutet i en rxjs Observable
Jag har ett projekt som använder Angulars $http-tjänst för att hämta data från en fjärrplats. Jag vill använda rxjs Observables så att anropet i min tjänst ser ut så här:
userInfo() : Rx.Observable<IUserInfo> {
var url : string = someUrl + this._accessToken;
return Rx.Observable.fromPromise<IUserInfo>( this.$http.get<IUserInfo>( url ) );
}
och detta prenumereras på av min kontroller så här:
getUserInfo() : void {
this._googleService.userInfo().subscribe(
( result ) => { this.handleUserInfo( result ) },
( fault : string ) => this.handleError( fault )
)
}
private handleUserInfo( result : IHttpPromiseCallbackArg<IUserInfo> ) : void {
console.log( "User info received at " + new Date() );
this._name = result.data.given_name + " " + result.data.family_name;
this._email = result.data.email;
this._profilePicUrl = result.data.picture;
}
problemet är att trots att namn, e-post och profilbild uppdateras är dessa ändringar inte synliga. Så snart något annat triggar en angular $apply visas ändringarna, men på grund av Observable sker dessa ändringar i controllern efter den angular digestive loop som triggas av $http-anropet. Detta fungerar korrekt om min tjänst bara returnerar ett löfte till controllern.
Hur uppdaterar jag min vy i det här fallet? Jag vill inte manuellt behöva koppla varje observable för att trigga en digest-cykel. Jag vill att alla observables triggar en digest-cykel när de får ett nytt värde eller ett fel.
Answer #1
Vi kan använda ScopeScheduler från rx.angular.js för detta. Vi behöver bara skapa en ny där vi skapar vår Angular-modul och skickar $rootScope till den:
const module : ng.IModule = angular.module( 'moduleName', [] );
module.run( ["$rootScope", ( $rootScope ) => {
new Rx.ScopeScheduler( $rootScope );
}]);
Det är allt du behöver göra. Nu triggar alla Rx.Observables ett $apply när de får ett nytt värde.
Av någon anledning togs ScopeScheduler bort när rx.angular.js-biblioteket uppgraderades till rxjs version 4. Vi måste använda rx.angular.js version 0.0.14 för att använda ScopeScheduler.
Jag vet inte vad den föreslagna lösningen på detta är i version 4.
Ett projekt som använder denna lösning kan ses här:
https://github.com/Roaders/Typescript-OAuth-SPA/tree/observable_apply_issues
Svar #2
Jag kunde inte få Rx.ScopeScheduler-metoden att fungera, så jag skrev helt enkelt över rx observable subscribe-metoden istället och omslöt callbacks i $rootScope.$apply :)
module.run(['$rootScope', 'rx', function ($rootScope, rx) {
rx.Observable.prototype.subscribe = function (n, e, c) {
if(typeof n === 'object') {
return this._subscribe(n);
}
var onNext = function(){};
if(n) {
onNext = function(value) {
if($rootScope.$$phase) {
n(value);
}
else {
$rootScope.$apply(function(){ n(value); });
}
};
}
var onError = function(err) { throw err; };
if(e) {
onError = function(error) {
if($rootScope.$$phase) {
e(error);
}
else {
$rootScope.$apply(function(){ e(error); });
}
};
}
var onCompleted = function(){};
if(c) {
onCompleted = function() {
if($rootScope.$$phase) {
c();
}
else {
$rootScope.$apply(function(){ c(); });
}
};
}
return this._subscribe(
new rx.AnonymousObserver(onNext, onError, onCompleted)
);
};
}]);
