Search code examples
javascriptfirebasepolymernosql

Firebase events not triggered properly when joining tables with Firebase-util


I'm using Firebase-util's intersection function to find all the comments for a given link. This seems to work fine the first time I call the join, but it doesn't seem to properly notify my value callback when I remove the contents of the database and replace them again. Shouldn't the references keep working as long as the resource path remains the same?

Run this example. When you click the button, it erases and recreates the data. As you can see, the comment list is not repopulated after the data gets recreated.

<link rel="import" href="https://www.polymer-project.org/components/polymer/polymer.html">

<script src="http://cdn.firebase.com/v0/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/firebase-util/0.1.0/firebase-util.min.js"></script>

<polymer-element name="my-element">
  <template>
    <h1>Test of Firebase-util.intersection</h1>
    <div>
      <button on-click={{initializeFirebase}}>Reset data</button>
    </div>
    <ul>
      <template repeat="{{rootComment in comments}}">
        <li>{{rootComment.comment.content}}
          <ul>
            <template repeat="{{subComment in rootComment.children}}">
              <li>{{subComment.comment.content}}
                <ul>
                  <template repeat="{{subSubComment in subComment.children}}">
                    <li>{{subSubComment.comment.content}}</li>
                  </template>
                </ul>
              </li>
            </template>
          </ul>
        </li>
      </template>
    </ul>
  </template>
  <script>
    Polymer('my-element', {
      ready: function() {
        var sanitizeUrl = function(url) {
          return encodeURIComponent(url).replace(/\./g, '%ZZ');
        };
        var baseUrl = "https://nested-comments-test.firebaseio.com";
        var linkUrl = baseUrl +
          '/links/' +
          sanitizeUrl(document.URL) +
          '/comments';
        var commentsUrl = baseUrl + '/comments';
        var root = new Firebase(baseUrl);

        this.initializeFirebase = function() {
          function addLink(url, callback) {
            var key = sanitizeUrl(url),
              newLink = {
                url: url,
                createdAt: Firebase.ServerValue.TIMESTAMP
              };
            root.child('/links/' + key).update(newLink);
            callback(key);
          }

          function addComment(attributes, callback) {
            return root.child('/comments').push(attributes, callback);
          }

          function onCommentAdded(childSnapshot) {
            var newCommentId = childSnapshot.name(),
              attributes = {},
              link = childSnapshot.val().link,
              url = '/links/' + link + '/comments';
            attributes[newCommentId] = true;
            root.child(url).update(attributes);
          }

          root.remove(function() {
            root.child('/comments').on('child_added', onCommentAdded);
            addLink(document.URL, function(link) {
              var attributes = {
                  link: link,
                  content: "This is the first comment."
                },
                firstCommentId, secondCommentId;
              firstCommentId = addComment(attributes).name();
              attributes = {
                link: link,
                content: "This is a reply to the first.",
                replyToCommentId: firstCommentId
              };
              secondCommentId = addComment(attributes).name();
              attributes = {
                link: link,
                content: "This is a reply to the second.",
                replyToCommentId: secondCommentId
              };
              addComment(attributes);
              attributes = {
                link: link,
                content: "This is another reply to the first.",
                replyToCommentId: firstCommentId
              };
              addComment(attributes);
            });
          });
        };
        this.initializeFirebase();

        var findChildrenForComment = function(snapshot, parentCommentId) {
          var returnVal = [];
          snapshot.forEach(function(snap) {
            var comment = snap.val(),
              commentId = snap.name();
            if (comment.replyToCommentId === parentCommentId) {
              var children = findChildrenForComment(snapshot, commentId);
              var obj = {
                commentId: commentId,
                comment: comment,
                parentId: parentCommentId
              };
              if (children.length) {
                obj.children = children;
              }
              returnVal.push(obj);
            }
          });
          return returnVal;
        };

        this.ref = Firebase.util.intersection(
          new Firebase(linkUrl),
          new Firebase(commentsUrl)
        );
        this.comments = {};
        var that = this;
        this.ref.on('value', function(snapshot) {
          that.comments = findChildrenForComment(snapshot);
        });
      }
    });
  </script>
</polymer-element>
<my-element></my-element>

Solution

  • Apparently deleting a path entirely causes all callbacks on it to be canceled. The workaround for this behavior is to remove children one at a time rather than deleting their parent path.