0

As far as I understand, JavaScript objects are an unordered collection of properties, whereas Firebase lists use IDs as property keys, which the docs say are chronologically ordered.

Taking Firebase's AngularFire chat code example (below), which stores messages as a Firebase list, the client presumably always displays the messages in the correct order, even though the object pointed to by $scope.messages is, I assume, a JavaScript object like any other.

How are the messages always displayed in the correct order?

angular.module("myChatRoom", ["firebase"])
  .factory("ChatService", ["$firebase", function($firebase) {
    var ref = new Firebase("https://<my-firebase>.firebaseio.com/chat");
    return $firebase(ref);
  }])
  .controller("ChatController", ["$scope", "ChatService",
    function($scope, chatService) {
      $scope.user = "Guest " + Math.round(Math.random()*101);
      $scope.messages = chatService;
      $scope.addMessage = function() {
        $scope.messages.$add({from: $scope.user, content: $scope.message});
        $scope.message = "";
      };
    }
  ]);

HTML:

<html ng-app="myChatRoom">
  <head>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
    <script src="//cdn.firebase.com/js/client/1.0.6/firebase.js"></script>
    <script src="//cdn.firebase.com/libs/angularfire/0.7.1/angularfire.min.js"></script>
    <script src="app.js"></script>
  </head>
  <body ng-controller="ChatController">
    <ul ng-repeat="message in messages">
      <li>{{message.from}}: {{message.content}}</li>
    </ul>
    <form ng-submit="addMessage();">
      <input type="text" ng-model="message"/>
      <input type="submit" value="Send Message"/>
    </form>
  </body>
</html>

Other Firebase examples use .on('child_added', cb) and new messages are added to an array in the callback, or are simply displayed. With these examples I understand how the messages are displayed in order as I'm guessing that the callback is passed the message snapshots in the order determined by Firebase. However that isn't the case with the example above.

KnewB
  • 353
  • 4
  • 16
  • Hey KnewB. Firebase keeps a (normally hidden) priority for each node in a collection. You'll be able to see the priority if you export your Firebase to JSON from the dashboard. – Frank van Puffelen Jun 05 '14 at 15:09
  • @FrankvanPuffelen I see that ID strings are used as keys for list properties, which I assume are the priorities. But how is the order maintained when the object is in the browser? The object pointed to by `$scope.messages` above has these same ID strings as its keys, but how are the property values rendered in order by the `ng-repeat` if JS objects are unordered? – KnewB Jun 05 '14 at 16:03
  • sorry @FrankvanPuffelen, priorities aren't what I thought they were, but there aren't any set on this data anyway so my question remains – KnewB Jun 06 '14 at 13:43
  • If I look on https://www.firebase.com/docs/angular/ it says that "In addMessage, we use the Firebase API's push method to append a new chat message to the list". The `push` method mentioned here will add a .priority to the newly created node, which is how it maintains order. – Frank van Puffelen Jun 06 '14 at 18:06
  • I quickly looked up the code for angularFire.js and that confirms my suspicion: `object.$add` invokes `push` on the underlying Firebase ref. So that adds the implicit priority needed to maintain the order of the nodes. – Frank van Puffelen Jun 06 '14 at 18:09

2 Answers2

0

Perhaps you can utilize the setPriority API to order your data. I believe with setPriority, children are sorted based on this priority using the following rules:

  1. Children with no priority come first.
  2. Children with a number as their priority come next. They are sorted numerically by priority (small to large).
  3. Children with a string as their priority come last. They are sorted lexicographically by priority.
  4. Whenever two children have the same priority (including no priority), they are sorted by
  5. name. Numeric names come first (sorted numerically), followed by the remaining names (sorted lexicographically).

So if you set the priority numerically for all your children, you can expect your set to behave in an ordered manner. Check docs on setPriority [here][https://www.firebase.com/docs/javascript/firebase/setpriority.html]

Sean
  • 779
  • 6
  • 18
0

In general, objects are ordered correctly. There are only a couple of notable exceptions, like chrome's inexplicable handling of numeric keys. So your data appears correct because as long as you haven't run afoul of any of chrome's thick skull.

To ensure proper ordering when working with raw Firebase data, you can utilize forEach:

firebaseRef.once('value', function(snap) {
   snap.forEach(function(ss) {
      console.log(ss.name()); // prints names in guaranteed order
   });
});   

The child_added events provide a prevChild that helps you order incoming values in your UI:

firebaseRef.on('child_added', function(snap, prevChild) {
   console.log('put ' + snap.name() + ' after ' + prevChild);
});

AnglularFire provides special tools for dealing with this. In the upcoming 0.8 release (releasing in a couple weeks), there will be a $asArray() method for getting sorted results in guaranteed order.

$scope.messages = $firebase(ref).$asArray();

And in the current iteration, you can utilize the orderByPriority filter (less efficient, but effective):

<li ng-repeat="message in messages | orderByPriority">{{message|json}}</li>
Community
  • 1
  • 1
Kato
  • 40,352
  • 6
  • 119
  • 149