Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create ngProgress $http interceptor #10

Closed
alari opened this issue Aug 26, 2013 · 9 comments
Closed

Create ngProgress $http interceptor #10

alari opened this issue Aug 26, 2013 · 9 comments

Comments

@alari
Copy link

alari commented Aug 26, 2013

Hi Victor.

I saw your great ngProgress bar and wished to switch to it from my ugly spinner loader.
Usually I use $http interceptor, like in the following code:

app.config ["$httpProvider", "progressbar", ($httpProvider, progressbar)->
    $httpProvider.responseInterceptors.push('progressAnimation')
    spinnerFunction = (data, headersGetter)->
      progressbar.start()
      data;
    $httpProvider.defaults.transformRequest.push(spinnerFunction)


    $ ->
      $(document)
        .ajaxStart ->
          progressbar.start()
        .ajaxStop ->
          progressbar.complete()
  ]

  app.factory 'progressAnimation', ["$q", "progressbar", ($q, progressbar)->
    (promise)->
      return promise.then (response)->
        progressbar.complete()
        response
      , (response)->
        progressbar.complete()
        $q.reject(response)
  ]

Sure, it doesn't work.

  • To intercept, I have to use $httpProvider, which is available in config -- no $http object is created yet.
  • To use ngProgress, I have to use its singleton object, not a provider, so I should wire it in run.
  • How can I get both objects at the same time?

Also it looks like it is the most simple plain case to use ngProgress, probably it worth to add some function to its api.

@victorb
Copy link
Owner

victorb commented Sep 8, 2013

I will have a look on how you can use ngProgress with $http interceptor but I have not used any interceptors yet so will probably take some time.

@selfinterest
Copy link

Hey Alari,

Here's how we did it in a project:

(1) Create a separate service (call it progressService.)
(2) Have the interceptor fire off a $rootScope event when the request goes out and another one when a response is received.
(3) ProgressService has ngProgress as a dependency and listens for the events, starting or stopping the progress bar accordingly.
(4) We also had to throw in a ngProgress.reset() call in the event handler that starts ngProgress to avoid certain issues when multiple requests go out in quick succession.

Sort of like this:
http://plnkr.co/VQw2Ay3VinuVJvlX3lEw

@alari
Copy link
Author

alari commented Oct 17, 2013

@selfinterest looks simple in implementation and fitting the need, but there's an overhead with emitting and catching events (well, not really heavy). Haven't you tried to pass ngProgress into $httpProvider interceptor?
function($q, ngProgress) -> call ngProgress methods directly?

I will play with your suggestion on our beta, thank you! :)

@selfinterest
Copy link

@alari

Okay, I went back and thought through the issue some more. The problem was, trying to inject ngProgress into the factory function was giving me a circular dependency error.

I did some checking and found this:

angular/angular.js#2367

Using that as a model, I came up with this:

http://plnkr.co/2CxGZ5fBg6BTfalpeBnb

It seems to do the job, without the extra service or using events.

Hope this helps!

@Rodeoclash
Copy link

I just implemented. It sort of works but it seems that the ngProgress bar gets a bit confused sometimes and starts animating all over the place!

Might be a few bugs to debug in it, I'll take a look.

@selfinterest
Copy link

@Rodeoclash

We've had some issues with that. It seems to occur when multiple http requests go out in quick succession (which can easily happen, especially in a single page app.) I've been thinking of a few ways to deal with that:

(1) Simplest way: set a flag in the interceptor when the progress bar starts. Switch the flag off when the response comes in. If the flag is set, don't start the progress bar again.

(2) Some kind of queue system, where you start progress bars sequentially as soon as one finishes. That seems like a bit of overkill to me, but could probably be done.

I haven't dug too much into @victorbjelkholm's code. The circular reference problem in the interceptor happened to me a second time, using an unrelated module (the $modal from angular-ui-bootstrap, actually.) So it has less to do with ngProgress and more to do with the relationship between $http and $compile.

I had to use the same solution in that case, lazily loading the $modal service. In that case, I went with option (1), because I didn't want multiple modal windows popping up for each failed request. It seemed to work okay in that case.

@Rodeoclash
Copy link

Just added the flag system and it works great. This has actually been a good lesson on using the injector service in interceptors as I'd also tried to use the $modal service from AngularUI for error responses and given up.

@Rodeoclash
Copy link

I still had a few issues with the script above and it uses the depreciated reponseInterceptor instead of the generic http interceptor.

This updated code only displays the ngProgress for loading non templateUrl http requests too.

'use strict';

angular.module('kumbyaApp')
    .factory 'interceptorNgProgress', ($injector) ->

        ng_progress = null
        working = false

        getNgProgress = () ->
            ng_progress = ng_progress || $injector.get("ngProgress")
            ng_progress

        complete_progress = () ->
            if working
                ngProgress = getNgProgress()
                ngProgress.complete()
                working = false

        request: (request) ->
            ngProgress = getNgProgress()

            return request if request.url.indexOf('.html') > 0

            if !working
                ngProgress.reset()
                ngProgress.start()
                working = true

            request

        requestError: (request) ->
            complete_progress()
            request

        response: (response) ->
            complete_progress()
            response

        responseError: (response) ->
            complete_progress()
            response

@pascalc
Copy link

pascalc commented Jan 23, 2014

Here's the above code snippet translated to JS:

app.factory('interceptorNgProgress', function ($injector) {
  var complete_progress, getNgProgress, ng_progress, working;
  ng_progress = null;
  working = false;

  getNgProgress = function() {
    ng_progress = ng_progress || $injector.get("ngProgress");
    ng_progress.color("rgb(207,181,150)");
    return ng_progress;
  };

  complete_progress = function() {
    var ngProgress;
    if (working) {
      ngProgress = getNgProgress();
      ngProgress.complete();
      return working = false;
    }
  };

  return {
    request: function(request) {
      var ngProgress;
      ngProgress = getNgProgress();
      if (request.url.indexOf('.html') > 0) {
        return request;
      }
      if (!working) {
        ngProgress.reset();
        ngProgress.start();
        working = true;
      }
      return request;
    },
    requestError: function(request) {
      complete_progress();
      return request;
    },
    response: function(response) {
      complete_progress();
      return response;
    },
    responseError: function(response) {
      complete_progress();
      return response;
    }
  }
});

app.config(function ($httpProvider) {
  $httpProvider.interceptors.push('interceptorNgProgress');
});

@dasois dasois closed this as completed Aug 27, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants