0

Firebase $remove() object from a nested array?

I can't get the Firebase $remove() method to work with nested arrays of objects. I'm making a movies database with an array of movie objects. Removing a movie from the array of movies is working:

  $scope.deleteMovie = function() {
    $scope.movie.$remove().then(function() {
      console.log("Movie deleted.");
    }, function(error) {
      console.log(error);
    });
  };

That uses the $firebaseObject $remove() method which just hooks onto an object and removes it. The movie was identified in the array by its key, passing movie.$id into the URL and then grabbing the key from the URL to identify the movie and putting it in the $scope:

  $scope.movie = $firebaseObject(ref.child($routeParams.id));

Users can add comments to movies. Each comment is an object in an array of comments. The array of comments is a property of the movie object, so it's a second-level nested array of objects. It seems like a better idea to use the $firebaseArray method $remove(recordOrIndex). I'll pass the comment through from the view:

  $scope.deleteComment = function(comment) {
    console.log(comment);
  };

It passes through the comment:

  Object {commentAuthor: "Eva Braun", commentDate: 1462461704268, commentText: "What's not to like?"}

Now I'll use $remove(comment) to delete the comment from the array:

  $scope.deleteComment = function(comment) {
    $scope.movie.movieComments.$remove(comment).then(function() {
      console.log("Comment deleted.");
    }, function(error) {
      console.log(error);
    });
  };

The error message is "TypeError: $scope.movie.movieComments.$remove is not a function".

So $scope.movie.$remove() is a function but $scope.movie.movieComments.$remove is not a function.

I checked if this is an asynchronous problem but that doesn't seem to be the issue.

Firebase doesn't like dot notation for nested arrays of objects. I'll use the child() notation:

$scope.deleteComment = function(comment) {
  $firebaseArray(ref.child($routeParams.id).child('movieComments').remove(comment)).then(function() {
    console.log("Comment deleted.");
  }, function(error) {
    console.log(error);
  });
};

The error message is "Error: Firebase.remove failed: first argument must be a valid function." The documentation says that the first argument must be either a record or an index. The comment passes through without its key, maybe that's the problem?

Let's try using an index instead of a record:

  $scope.deleteComment = function(comment) {
    $firebaseArray(ref.child($routeParams.id).child('movieComments').remove(0)).then(function() {
      console.log("Comment deleted.");
    }, function(error) {
      console.log(error);
    });
  };

Same error message: "Error: Firebase.remove failed: first argument must be a valid function."

Let's try removing the comment from the view:

  <div ng-repeat="comment in movie.movieComments">
    <form>
      <button ng-click="movie.movieComments.$remove(comment)">x</button>
    </form>
  </div>

That does nothing.

Let's try the $firebaseObject version of $remove():

  $scope.deleteComment = function() {
    $firebaseObject(ref.child($routeParams.id).child('movieComments').remove()).then(function() {
      console.log("Comment deleted.");
    }, function(error) {
      console.log(error);
    });
  };

That works great! Except that it removes the entire array of comments, when I only want to remove one comment from the array. Let's add another .child():

  $scope.deleteComment = function(comment) {
    $firebaseObject(ref.child($routeParams.id).child('movieComments').child(comment).remove()).then(function() {
      console.log("Comment deleted.");
    }, function(error) {
      console.log(error);
    });
  };

The error message is "Firebase.child failed: First argument was an invalid path: "undefined". Paths must be non-empty strings and can't contain ".", "#", "$", "[", or "]"".

I'm not identifying the comment correctly. How about using the key:

  $scope.deleteComment = function(comment) {
    console.log(comment);
    $firebaseObject(ref.child($routeParams.id).child('movieComments').child(comment.$id).remove()).then(function() {
      console.log("Comment deleted.");
    }, function(error) {
      console.log("Error, movie not deleted.");
      console.log(error);
    });
  };

Same error message. The key isn't in the comment so the key isn't being passed through when the comment is passed through. I'll pass through the movie object and console log the movieComments array. The array has one object:

  Object {-KH0VeEfwIgT1UOUrFzo: Object}
    -KH0VeEfwIgT1UOUrFzo: Object
      commentAuthor: "Eva Braun"
      commentDate: 1462461704268
      commentText: "What's not to like?"

Now I'll use $keyAt(recordOrIndex) to get the key:

  $scope.deleteComment = function(movie, comment) {
    console.log(movie.movieComments.$keyAt(comment));
  };

"TypeError: movie.movieComments.$keyAt is not a function". Let's try .child() notation:

  $scope.deleteComment = function(movie, comment) {
    console.log(movie.child('movieComments').$keyAt(comment));
  };

"TypeError: movie.child is not a function". Let's try that with an index number:

  $scope.deleteComment = function(movie, comment) {
    console.log(movie.child('movieComments').$keyAt(0));
  };

"TypeError: movie.child is not a function". OK, $keyAt(recordOrIndex) and $remove(recordOrIndex) are both not working. That suggests there's something wrong with my record or index.

Any suggestions how to remove any object from a nested array in Firebase?

2 Answers 2

1

I am not certain with out seeing all of your code, but I have a feeling you are passing in an object with the final .child(comment) when firebase will only accept a string. See this line:

$firebaseObject(ref.child($routeParams.id).child('movieComments').child(comment).remove()).then(function() {

If you found the id or key string representation of what I assume is a comment object you could pass it into the remove function. That is why you are getting the error.

Also you can chain your children like

.child(`${$routeParams.id}/movieComments/comment`)

to keep everything a little cleaner

Sign up to request clarification or add additional context in comments.

Comments

0

I figured it out. Firebase doesn't do dot notation so this doesn't work:

  ng-click="movie.movieComments.$remove(comment)"

I set up an array of comments in the controller:

    $scope.comments = $firebaseArray(ref.child($routeParams.id).child('movieComments'));

Then in the view I set up:

  <div ng-repeat="comment in comments">
    <button ng-click="comments.$remove(comment)"></button>
  </div>

Works perfectly!

There's a mistake in the Firebase documentation. In the "Synchronized Objects" tutorial it gives this example:

  {
    "profiles": {
      "physicsmarie": {
        name: "Marie Curie",
        dob: "November 7, 1867"
      }
    }
  }

That's a JavaScript object. A Firebase object looks like this:

  {
    $id: "-KGSpDSJEtvCHetr5679",
    {
      "profiles": {
        $id: "-KGSpDSJ43er5t3udghoe",
       {
         "physicsmarie": {
         $id: "-KGSpDSJEtvCHNeXWriS",
         {
           name: "Marie Curie",
           dob: "November 7, 1867"
            }
          }
        }
      }
    }
  }

This is why you can't use dot notation with Firebase. Firebase objects are nested double objects, with the key as the first property of the outer object and the inner object as the second property. You have to use .child() notation.

Comments

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.