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

What are some solutions to the error message "Error: Cannot find any matching routes" that appears when trying to switch between tabs following a successful login?

I am facing an issue with my Ionic 4 (4.10.2 with Angular 7.3.1) app where I want to make it accessible only after login. Following a tutorial from , I encountered a problem when trying to access the ion-tabs section of my app. Chrome keeps showing this er ...

Typescript Error: TS2339: The property 'faillogout' is not found within the type '{ failed(): void; onSubmit(): void; }'

I encountered an issue with my Vue.js app using TypeScript. The error message I'm getting is: Property 'faillogout' does not exist on type '{ failed(): void; onSubmit(): void; }'. 101 | failed () { This snippet shows the s ...

Utilize GroupBy and tally up items within an array using typescript

Here is a representation of my array, which is not a type of string but its own object called MyObject (similar to setter and getter objects in Java) ["Car","model","year","color","price"] ["Table" ...

Ensuring the dropdown menu's functionality in AngularJS

I'm currently working on a code snippet that validates when the user moves away from a specific select element. The goal is to change the border color of the select element to either red or green only when the user tabs away from it or moves the mouse ...

Is Angular 11 Compatible with Internet Explorer 5?

Is there a way to make Angular 11 compatible with Internet Explorer 5? I am developing an angular solution for a client whose default browser is Internet Explorer running on version 5 (by default). Initially, I am not supposed to change any browser configu ...

Error encountered while trying to install the Angular-Fullstack generator using npm on Windows

Currently attempting to set up yo angular fullstack with the following version specifications: node v5.7.0 npm 3.7.3 yo 1.6.0 bower 1.7.7 Encountering some errors related to npm npm ERR! Windows_NT 10.0.10586 npm ERR! argv "C:\\Program Files& ...

Mental stability groq fails to provide the requested information

Having difficulty using Groq to fetch data. All other Groq queries work fine, except this one. When manually setting the $slug variable, the data I'm trying to query works with the Sanity Groq VS plugin but returns undefined in my web app. Query: exp ...

What causes a standard React component with a default render prop to not pass PropTypes validation successfully?

I'm currently working on a React component with a render-prop that has a generic type. To improve usability, I want to set a default value for the render-prop. The code is functioning correctly, but during type-checking, I encountered a warning regard ...

bespoke filter designed to conceal any negative figures

I am trying to implement a feature where a text box will display nothing if the ng-model contains a negative value. I want the ng-model to remain unchanged while ensuring that negative values are not displayed. I am looking for a custom filter to achieve t ...

How to update the <DIV> in Angular JS after submitting data?

I am looking for a way to dynamically display the total direct sales in AngularJS without having to reload the page when processing the cart. Previously, I used to reload the page every time the cart was processed. Javascript .controller('CartCtrl& ...

Angular input form is throwing an error because it is unable to retrieve the property 'name' of an undefined value

I've been working on creating a simple Angular component following a tutorial I found. The component fetches data from an angular-in-memory-web-api using a service called UserService. I have also added an input form for creating new users. The issue ...

I'm curious if there is a method to validate HTML elements within AngularJS. You can check out my app on Plunker [here](https://jsfiddle.net/4jttdczt/)

I'm a newcomer to angularjs and I am attempting to test HTML elements, but my tests are failing. Specifically, I would like to test the value or text contained within an HTML element. Can anyone provide guidance on how I can achieve this? Below is my ...

Challenges in Testing Angular 2

During my efforts to conduct integration tests using Angular 2 and Karma test runner, I encountered a perplexing issue. Regardless of the expected outcome, a particular test was consistently passing instead of failing as it should have been. The problem ar ...

Keep an eye on any developments within a specific area

In my project, I have a variable called $scope.activites where the data received from a service is stored. activitiesService.loadActivities().then(function(response) { $scope.activities = response.data; console.log ($scope.activities) }) I want to ap ...

Currying disrupts the inference of argument types as the argument list is divided in half, leading to confusion

One of my favorite functions transforms an object into a select option with ease. It's written like this: type OptionValue = string; type OptionLabel = string; export type Option<V extends OptionValue = OptionValue, L extends OptionLabel = OptionL ...

Exploring the Power of Vercel Deployment: Crafting a Custom CORS Middleware for Your API

Recently, I have been testing different methods to avoid a CORS error in my upcoming app deployed on Vercel. The only solution that worked for me was manually setting the headers for each API request, as shown below: export default async function handler( ...

Dynamically incorporating validation to an ngModel input

Utilizing NgForm, I dynamically added a validator to the input field. In my first example, everything works perfectly when I use the button setValidation to validate the input. However, in the second example where I add formGroup, I encounter an error whe ...

Obtaining a customized variation of a class identified by a decorator

I am working with a class that is defined as follows: class Person { @OneToOne() pet: Animal; } Is there a method to obtain a transformed type that appears like this? (Including {propertyKey}Id: string to properties through the use of a decorator) ...

What is the best way to prevent jest.mock from being hoisted and only use it in a single jest unit test?

My goal is to create a mock import that will be used only in one specific jest unit test, but I am encountering some challenges. Below is the mock that I want to be restricted to just one test: jest.mock("@components/components-chat-dialog", () ...

Get the azure blob storage file in angular by downloading it

Can anyone provide guidance on how to download a wav file from Azure Blob storage using Angular for the purpose of playing it with wavesurfer.js? ...