Tips for updating the display after making an angular $http request using rxjs Observables

I have a project where I am utilizing angular's $http service to fetch data from a remote endpoint. I am keen on incorporating rxjs Observables, hence the call in my service is structured as follows:

    userInfo() : Rx.Observable<IUserInfo> {
        var url : string = someUrl + this._accessToken;

        return Rx.Observable.fromPromise<IUserInfo>( this.$http.get<IUserInfo>( url ) );
    }

This method is then subscribed to by my controller as shown below:

    getUserInfo() : void {
        this._googleService.userInfo().subscribe(
            ( result ) => { this.handleUserInfo( result ) },
            ( fault : string ) => this.handleError( fault )
        )
    }

    private handleUserInfo( result : IHttpPromiseCallbackArg<IUserInfo> ) : void {
        console.log( "User information received at " + new Date() );

        this._name = result.data.given_name + " " + result.data.family_name;
        this._email = result.data.email;

        this._profilePicUrl = result.data.picture;
    }

The issue arises when the name, email, or profile picture fields are updated but not reflected in the view. The changes only become visible after another action triggers an angular $apply. This delay is due to the Observable causing the changes in the controller to occur after the angular digest cycle initiated by the $http call. Interestingly, this problem does not arise if my service simply returns a promise to the controller.

How can I ensure my view gets updated in this scenario? I prefer not to manually connect each observable to trigger a digest loop. Instead, I want all Observables to automatically trigger a digest cycle upon receiving new values or errors.

Answer №1

To address this issue, we can implement the ScopeScheduler from rx.angular.js. All that is needed is to create a new one within our angular module and then pass the $rootScope to it:

const module : ng.IModule = angular.module( 'moduleName', [] );

module.run( ["$rootScope", ( $rootScope ) => {
    new Rx.ScopeScheduler( $rootScope );
}]);

By following these steps, all Rx.Observables will automatically trigger an $apply when they receive a new value.

It should be noted that the ScopeScheduler was removed during the upgrade of the rx.angular.js library to rxjs version 4. Therefore, in order to utilize the ScopeScheduler, it is necessary to use rx.angular.js version 0.0.14.

The solution for this issue in version 4 remains uncertain at this time.

To see an example project implementing this fix, you can visit:

https://github.com/Roaders/Typescript-OAuth-SPA/tree/observable_apply_issues

Answer №2

After struggling with the Rx.ScopeScheduler method without success, I decided to take a different approach. Instead of trying to make it work, I ended up overriding the rx observable subscribe method itself. I wrapped the callbacks in $rootScope.$apply and managed to get it working smoothly :)

module.run(['$rootScope', 'rx', function ($rootScope, rx) {
    rx.Observable.prototype.subscribe = function (onNext, onError, onCompleted) {
        if(typeof onNext === 'object') {
            return this._subscribe(onNext);
        }

        var next = function(){};
        if(onNext) {
            next = function(value) {
                if($rootScope.$$phase) {
                    onNext(value);
                }
                else {
                    $rootScope.$apply(function(){ onNext(value); });
                }
            };
        }

        var error = function(err) { throw err; };
        if(onError) {
            error = function(error) {
                if($rootScope.$$phase) {
                    onError(error);
                }
                else {
                    $rootScope.$apply(function(){ onError(error); });
                }
            };
        }

        var completed = function(){};
        if(onCompleted) {
            completed = function() {
                if($rootScope.$$phase) {
                    onCompleted();
                }
                else {
                    $rootScope.$apply(function(){ onCompleted(); });
                }
            };
        }

        return this._subscribe(
            new rx.AnonymousObserver(next, error, completed)
        );
    };
}]);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Executing a function to erase the stored value in local storage during an Angular unit test

Looking to verify whether the localStorage gets cleared when I execute my function. Component ngOnInit() { // Logging out when reaching login screen for login purposes this.authService.logout(); } authService logout() { // Removing logged i ...

TS2322 error: What does it mean when the type is both not assignable and assignable?

I've been delving into the world of generics with Java and C#, but TypeScript is throwing me for a loop. Can someone shed some light on this confusion? constructor FooAdapter(): FooAdapter Type 'FooAdapter' is not assignable to type 'A ...

Incorrect object being returned from Angular 2 HTTP request

For my data fetching from the server, I am using @angular/http get method. Below is the code snippet: private _currentPT: any; public phongtroDetailChange = new Subject(); layPhongtro(id: number): Promise<any> { return new Promise((resolve, reject) ...

The instantiation of the angular-jwt module failed because of a MIME type mismatch (X-Content-Type-Options: nosniff), leading to an error

In my AngularJS project, I am facing an issue while trying to initialize angular-jwt. Despite having the following code in my app.js file: angular.module('xxx', ['ngRoute', 'angular-jwt']) .config(config).run(run); And i ...

Steps for creating a copy of an Angular component

https://i.stack.imgur.com/4RMsR.png Whenever the user clicks on the Create Copy button, I aim to replicate the content of the DashboardComponent and position the duplicated version below the original one (the DashboardComponent featuring four dark blue sq ...

Utilizing Typescript for Axios Response

Incorporating Typescript into my project, I encountered a tedious issue while making an API call using axios. The problem lies within handling nested data properly. Despite my belief that I have correctly typed everything, I persistently face a Typescript ...

Is it possible to utilize href alongside the urlRouterProvider?

Within my angularjs application, I opted to switch from using ngRoute (routeProvider) to ui.router (urlRouterProvider) module and stateProvider for transitioning between different states in the app. However, I recently discovered that ui-router only suppo ...

After upgrading from Angular 7 to 12, the module './rest.service.interface' does not export 'RestService' (imported as 'RestService'), causing it to not be found

Hey everyone, I've been struggling with a problem for hours now and I can't seem to fix it. Here is the interface I'm working with: import { HttpClient } from '@angular/common/http'; import { Response } from '@angular/http&apo ...

What is the best way to evaluate typing into an input field?

My objective is to test the 'typing' functionality in an input element. The aim is to insert a value into the input element, verify that its binding successfully captures the value, and observe the entered value within the input element. Below i ...

What is the process of generating a row in AngularJS?

I am struggling to create a row as shown in the image. Currently, my circle is not positioned on the right side and my "P" element is not receiving the background color. Here is the code snippet: http://plnkr.co/edit/qIz2rFgW8n3J92evCRTd?p=preview Is it p ...

Managing HTTP errors using async/await and the try/catch block in a React application with TypeScript

Below is a snippet of code I am working with! import React, { useState } from "react"; function App() { const [movies, setMovies] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState<string ...

Modify the length of an array using a number input field

Currently, I am working with an array that contains objects and I want to dynamically change the number of objects in this array based on user input from a number type input box. Whenever the number in the input box is increased, I need to increase the len ...

Tips for preventing the rxjs error "TypeError: Cannot read properties of undefined" in the Angular framework

When I try to open the page in Angular, I encounter this error: core.mjs:6485 ERROR TypeError: Cannot read properties of undefined (reading 'getDocumentContent') In my Angular component, I have an observable like this: selectedDocument$ = this.s ...

Node Express Heroku Issue: 'apiKey.id is missing'

I have a unique setup for my Express/Angular app with Stormpath handling authentication. The local implementation works flawlessly, but when I try to deploy it on Heroku, it crashes and shows the error message below. Error: "015-10-07T03:40:29.632276+00: ...

There seems to be a troublesome character in the Nuxt3 production server causing some issues

When submitting an HTML encoded text to the server, everything runs smoothly on the development environment. However, once it is deployed to a Netlify server, the same request triggers a 500 error and the server side logging middleware only recognizes a PO ...

Developing AngularJS components with proper controller inheritance methods

Is there a way to inherit controllers from angular components? In the previous version of Angular, I used $controller or $injector, but how should I approach it when dealing with isolated scopes? ...

"Exploring the possibilities of integrating Typescript into Material-UI themes: A step-by

I'm experiencing some issues with Typescript pointing out missing properties in the palette section. Although adding //@ts-ignore resolves the problem temporarily, I would prefer to find a cleaner solution. As a newbie to Typescript, here is my attemp ...

Explore ways to incorporate special symbols in a jQuery array

I'm looking to include special characters in a jQuery array. I'm not quite sure how to do this. Currently, my code is: $scope.categories = ['Red', 'White', 'Rose', 'Sparkling']; and I would like it to be: ...

Automatically identify the appropriate data type using a type hint mechanism

Can data be interpreted differently based on a 'type-field'? I am currently loading data from the same file with known type definitions. The current approach displays all fields, but I would like to automatically determine which type is applicab ...

The 'src' properties in nextjs/image are of different types and therefore cannot be used interchangeably

I'm currently using React Dropzone to upload multiple images in my basic application. To display the types of images that are being dropped, I created a separate component with TypeScript. However, Next.js is throwing an error when it comes to the ima ...