0

Hi I have a factory that GETs data from a backend. This data is then processed with a controller (as seen below) and injected in the web page with ng-repeat. Due to asynchronous nature of the system, I have troubles when I try to manipulate window. For example I need to use window.scrollTo function but only AFTER data was completely processed and displayed on screen with ng-repeat.

As you can see here, I tried to use a promise early in the controller. But it doesn't work: window.scrollTo is always processed before data has finished being processed on screen. I guess what I need is a way to actually force the data process to be completed and only then process the window.scrollTo function but I don't see how.

    .controller('myCtrl',
    function ($scope, prosFactory, fieldValues, $q ) {
        $scope.listpros = function() {
            prosFactory.getPros()
                .success(function(data, status) {
                    var defer = $q.defer();
                    defer.promise
                        .then(function () {
                          $scope.prosItems = data;  // pass data to ng repeat first                           
                            })
                        .then(function () {
                            $window.scrollTo(0, 66); // then scroll 
                          });
                    defer.resolve();   
                }).error(function(data, status) {
                    alert("error");
                }
            );
        };

I tried with a 2000 timeout on scrollTo function, but due to variation in internet speed, it sometimes delay the scroll to much, or sometime isn't enough.

Robert Brax
  • 6,508
  • 12
  • 40
  • 69

2 Answers2

0

A promise won't help much in this case, because it is not connected to the ng-repeat process by any means. However you could use a custom directive to $emit an event when the last item got processed. Your controller could listen to that specific event and react to it according to your needs.

This answer shows you how to achieve this, it even comes with a Plunker.

EDIT: General approach to control DOM creation

Within the AngularJS world, DOM elements are controlled and, in this case supervised, by directives. The answer I linked to above extends the behavior of a ng-repeat directive, but you could do the same for possibly any DOM element. A directive creation-emitter could look something like this:

myModule.directive('creationEmitter', function () {
    return {
        restrict: 'A',
        compile: function compile(tElement, tAttrs, transclude) {
            return {
                pre: function (scope, iElement, iAttrs, controller) {
                    scope.$emit('preCompile', iElement)
                },
                post: function (scope, iElement, iAttrs, controller) {
                    scope.$emit('postCompile', iElement)
                }
            }
        },
        link: function (scope, iElement, iAttrs) {
            scope.$emit('link', iElement)
        }
    };
});

You could now listen to all those events in a controller that's above the given element in the DOM (for child elements use $broadcast).

Community
  • 1
  • 1
Lukas Bünger
  • 4,257
  • 2
  • 30
  • 26
  • I don't think it's an issue with reacting after the data is processed. It looks like he's correctly handling the promise (I assume) returned from the service. I think what is happening is that there are missing DOM elements when scrollto is called. He's loading the data into a a model called prosItems and then scrolling to a position on screen. I'm guessing that position is based on ng-repeat completing and inserting the new DOM nodes. What's happening is that it's scrolling to BEFORE the html is inserted in the DOM. – Hattan Shobokshi Dec 19 '13 at 12:19
  • Yes, that's what is happening. The solution from Lukas did help and I thank him for that: I achieve what I need to when specifically tying the solution to ng repeat. But if someone knows a more general solution with waiting for dom render completion, it would be interesting to read about. – Robert Brax Dec 19 '13 at 12:45
  • @Hattan: I wasn't talking about the promise returned by the service, but the one Robert is creating himself. For the general approach: Updated my answer. – Lukas Bünger Dec 19 '13 at 14:46
0

Its possible to chain promises and do intermediate processing. The immideate callback must return what the next will receive.

I don't see any immediate problems with this approach:

prosFactory.getPros()
  .then(processCb)
  .then(doSomethingWithProcessedDataCb)
  .catch(errorCb)
  .finally(scrollWindowCb)

Check out the documentation, it's actually quite good.

Kenneth Lynne
  • 15,461
  • 12
  • 63
  • 79