2

I'm having trouble decorate the objects in my list returned by $asArray in angularfire with a new method (not decorating the array itself).

The angularfire documentation seems to suggest that the right way to do this is to override the $$added method in the factory for $FirebaseArray, returning a new object that either encapsulates or extends the snapshot that gets passed in to that method. From the documentation:

// an object to return in our JokeFactory
app.factory("Joke", function($firebaseUtils) {
  function Joke(snapshot) {
    this.$id = snapshot.name();
    this.update(snapshot);
  }

  Joke.prototype = {
    update: function(snapshot) {
      // apply changes to this.data instead of directly on `this`
      this.data = snapshot.val();
    },

    makeJoke: function() {
      alert("Why did the " + this.animal + " cross the " + this.obstacle + "?");
    },

    toJSON: function() {
      // since we didn't store our data directly on `this`, we need to return
      // it in parsed format. We can use the util function to remove $ variables
      // and get it ready to ship
      return $firebaseUtils.toJSON(this.data);
    }
  };

  return Joke;
});

app.factory("JokeFactory", function($FirebaseArray, Joke) {
  return $FirebaseArray.$extendFactory({
    // change the added behavior to return Joke objects
    $$added: function(snap) {
      return new Joke(snap);
    },

    // override the update behavior to call Joke.update()
    $$updated: function(snap) {
       this.$getRecord(snap.name()).update(snap);
    }
  });
});

However, when I do this in my code, nothing ever gets added to the array, although I can see from outputting to the console that it is getting called.

var printMessageObjConstructor = function(snap) {
      this.$id = snap.name();
      this.snapshot = snap;
      this.$update = function(snap) {
        this.snapshot = snap;
      };
      this.printMessage = function() {
        return this.author + "'s question is: " + this.body;
      };
    };

    var ref = new Firebase("https://danculley-test.firebaseio.com/questions");
    //What Am I Doing Wrong Here?
    var arrayFactory = $FirebaseArray.$extendFactory({
      $$added: function(snap, prevChild) {
        var x = new printMessageObjConstructor(snap);
        console.log("I am being called from FirebaseDecoratedCtlOverloadAddedinNewObj.");
        return x;
      },
      $createObject: function(snap) {
        return new printMessageObjConstructor(snap);
      },
      $$updated: function(snap) {
        var i = this.$indexFor(snap.name());
        var q = this.$list[i];
        q.$update(snap);
      }
    });
    var sync = $firebase(ref, {arrayFactory:arrayFactory});

    var list = sync.$asArray();

    list.$loaded(function(list) {
      $scope.questions = list;
    });

I've set up a new plunk stripped down to show the issue with a couple other use cases that I've tried. (The actual method I'm adding is more complex and isn't related to the view, but I wanted to do something simple to reproduce the issue.)

I think the issue is that I don't quite understand what exactly $$added is supposed to return, or what additional behavior beside returning the value to be stored $$added is supposed to have. There also doesn't really seem to be an $$added on the prototype or on $FirebaseArray to call as a super to get the default behavior. Can someone point me in the right direction?

UPDATE

For the benefit of others, after reviewing the like that Kato posted, I was able to solve the issue by adding the following, almost all copied directly from the source except for the commented line below.

$$added: function(snap, prevChild) {

        var i = this.$indexFor(snap.name());
          if( i === -1 ) {

            var rec = snap.val();
            if( !angular.isObject(rec) ) {
              rec = { $value: rec };
            }
            rec.$id = snap.name();
            rec.$priority = snap.getPriority();
            $firebaseUtils.applyDefaults(rec, this.$$defaults);

            //This is the line that I added to what I copied from the source
            angular.extend(rec, printMessageObj);


            this._process('child_added', rec, prevChild);
          }
      }
4
  • If you look at the source for $$added, you'll see that it doesn't return anything. It applies the updates internally. Commented Sep 15, 2014 at 17:45
  • Thanks very much for the pointer, @Kato. As I've updated the post above, I was able to solve the issue by mostly copying and pasting from the source and adding one line. I'm a little confused, though, why calling $FirebaseArray.$$added.call(this, snap, prevChild); from the function doesn't work (it says that $$added doesn't exist on $FirebaseArray). This also seems to contradict what's being done in the documentation. Commented Sep 16, 2014 at 0:33
  • I think the internal unwrapping makes this somewhat hard to extend; it would make more sense (to me at least), for this function to call another function before rec is processed with rec as an argument, then pass that to _process unless that function returns false. In $FirebaseArray, that could be a no-op function, but that would be easy to extend to decorate the actual record. Commented Sep 16, 2014 at 0:39
  • Please add your solution as an actual answer and accept it. On StackOverflow it is acceptable to self-answer your own questions like that. Commented Sep 20, 2014 at 21:28

1 Answer 1

2

For the benefit of others, after reviewing the link that Kato posted, I was able to solve the issue by adding the following, almost all copied directly from the source except for the commented line below.

$$added: function(snap, prevChild) {

    var i = this.$indexFor(snap.name());
      if( i === -1 ) {

        var rec = snap.val();
        if( !angular.isObject(rec) ) {
          rec = { $value: rec };
        }
        rec.$id = snap.name();
        rec.$priority = snap.getPriority();
        $firebaseUtils.applyDefaults(rec, this.$$defaults);

        //This is the line that I added to what I copied from the source
        angular.extend(rec, printMessageObj);


        this._process('child_added', rec, prevChild);
      }
  }
Sign up to request clarification or add additional context in comments.

2 Comments

thanks for this explanation, it could help me. what's printMessageObj in this example? there's no declaration to be found here, just the printMessageObjConstructor. thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.