0

I am building an Ionic App with Firebase / AngularFire. And I am facing a tricky situation: I want my ng-repeat to be more performance optimized. And I heard the BEST way to do it is infinite scroll. I used AngularFire to get all my data in a service/factory - more specifically, $firebaseArray.

I google and found this: http://firebase.github.io/firebase-util/#/toolbox/Paginate/example/ionic

Question 1: is the infinite scroll with Ionic example using Firebase util library a server side infinite scrolling or a client side? Does this library address the performance issue of ng-repeat like mentioned here: (http://www.alexkras.com/11-tips-to-improve-angularjs-performance/)

Question 2: my firebaseArray actually need to orderBy a dynamically calculate variable called distance. Can I still use .indexOn rule in my Security Rules to improve query performance? (sound impossible to me since the distance is not stored in firebase at first)

Here is my code: My factory: taskService.js

    var ref = new Firebase(FirebaseUrl);
    var tasks = $firebaseArray(ref.child('tasks'));
    var Task = {
        all: tasks,
        getAllTasks: function() {
            if (tasks.length == 0) {
                tasks = $firebaseArray(ref.child('tasks'));
            } else {   
                tasks = tasks;
            }
            return tasks;
        },
     }

My controller for the ng-repeat view:

    $scope.tasks = taskService.getAllTasks();
    $scope.tasks.$loaded().then(function(data){
            calculateDistance($scope.tasks, $scope.userCurrentLocation);  
    });
    var unwatch = $scope.tasks.$watch(function(event) {
          calculateDistance($scope.tasks, $scope.userCurrentLocation);
        });
    $scope.$on('$ionicView.leave', function(){
          // Anything you can think of
          unwatch();
        });
    }
    //--------------GeoLocation Calculation
    var calculateDistance = function (tasks, userCurrentLocationData) {
        for (var id in tasks) {
            var task = tasks[id];
            if (task.addressLat) {
                var taskLocation = new google.maps.LatLng(task.addressLat, task.addressLng);
                var userCurrentLocation = new google.maps.LatLng(userCurrentLocationData.lat, userCurrentLocationData.lng);
                var distance = google.maps.geometry.spherical.computeDistanceBetween(userCurrentLocation, taskLocation);
                $scope.tasks[id].distance = parseInt(distance*0.000621371192);
            }
        }
    }

And my view:

<a class="item item-avatar" ng-repeat="task in tasks | filter: searchTask | orderBy: ['-status', 'distance', '-datetime'] track by task.$id" ng-href="#/app/task/{{task.$id}}">
....
</a>

As you see here, I added the distance property after I get the firebaseArray object. And this distance is calculated dynamically based on user current device location. So the ng-repeat will show item closest to user current location. As distance is dynamic, I can not save it before hand in my Firebase so the firebase rule won't work.

Question 3: How can I use Firebase.util infinite scroll on a dynamic value - as in here, the "distance" property?

As I saw the doc of firebase util, I will need to do that:

   // create a scrollable reference
  var scrollRef = new Firebase.util.Scroll(baseRef, 'distance');

But distance is not there in the baseRef. It generated after the firebaseArray loaded. So how should I approach this?

Also, from a structure stand point, I will put both the scrollRef and the firebaseArray in my factory - taskService.js

// create a scrollable reference var scrollRef = new Firebase.util.Scroll(baseRef, 'distance');

// create a synchronized array on scope return $firebaseArray(scrollRef);

Then, since the factory return just the $firebaseArray(scrollRef), how do I get access into scroll.next function stated in the doc like scrollRef.scroll.next(3); in my controller? Since I do not have the scrollRef object, just the FirebaseArray...

Hugh Hou
  • 2,344
  • 5
  • 31
  • 55

1 Answers1

5

Re Q1: firebase-util's Scroll implements scrolling by updating query parameters and synchronizing the changes from the server as you scroll. The server participates by caching and prefetching to make the scrolling updates fast.

The ng-repeat performance problem seems to be that ng-repeat doesn't handle very large numbers of elements. Using Scroll helps address this by limiting the number of elements repeated.

Re Q2: Indexing on the great circle distance between a user and the elements you want to display will be impossible. A better approach might be to use a geohashing. The geofire library implements this directly for firebase, making it easy to find things nearby a user.

Re Q3: GeoFire itself provides a means of limiting results (by limiting the radius), so it's possible that you may not even have to solve this problem. If you want to limit to a fixed number of elements you'll need to post-process the results of the geofire query for yourself.

Sara
  • 2,729
  • 7
  • 22
  • 30
  • Thank Sara! I have no idea there is a GeoFire library! That turn my question completely 180 degrees. Please give me some time to read the GeoFire docs...which it seem pretty complicated. And I will report back if this is THE SOLUTION for my problem. – Hugh Hou Sep 16 '15 at 23:54
  • Awww! For the GeoFire, is there an official AngularJS wrapper for GeoFire 3.1.x? – Hugh Hou Sep 17 '15 at 00:40
  • There's no Angular wrapper for GeoFire, but you can use the Firebase JS SDK directly from your AngularFire app. – Sara Sep 17 '15 at 02:55
  • I ran into a lot of issues with $q and not updating the view without Angular Wrapper. Then I found this: https://github.com/mikepugh/AngularGeoFire As suggested in GeoFire google group. As I use AngularFire as well. Is this AngularGeoFire library usable? So far I do not see any error when I am using it with GeoFire 3.2.2. Will you suggest it? I just do not know how to roll out my own Angular wrapper for GeoFire to get the digest circle right. – Hugh Hou Sep 17 '15 at 05:46