Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase.push failed: first argument contains an invalid key ($$hashKey)

I recently started learning AngularJS+Firebase. I'm trying to write in my firebase an object like this:

{
    title: "Personal Information",
    say: [
        [{ "eng": "What's", "ukr": "Що є" }, { "eng": "your", "ukr": "твоє" }, { "eng": "surname?", "ukr": "прізвище?" }],
        [{ "eng": "Smith", "ukr": "Сміт" }],
        [{ "eng": "What's", "ukr": "Що є" }, { "eng": "your", "ukr": "твоє" }, { "eng": "first", "ukr": "перше" }, { "eng": "name?", "ukr": "ім'я?(не фамілія)" }]
    ]
}

with line:

lessondata.add($scope.topic);

where 'lessondata' is service created with angularFireCollection() and $scope.topic - object bound to my UI. But got the following error: Firebase.push failed: first argument contains an invalid key ($$hashKey) in property 'say.0.0'. Keys must be non-empty strings and can't contain ".", "#", "$", "/", "[", or "]"


As I understood Firebase do not allow to use 0 as a key even if it's a key in an attached array for which zero key is natural. So should I change my object structure in some hardcoded instance or I miss something? Thanks in advance!

like image 541
user2587800 Avatar asked Jul 16 '13 15:07

user2587800


5 Answers

EDIT: As Anant points out in the comments, in the latest stable version of Angular (1.0.7 atm), you can use angular.copy(obj) to remove $$hashkey attributes.

Like Michael said, the '$' in '$$hashKey' is the issue. Angular creates the $$hashKey properties behind the scenes (see more here: https://groups.google.com/forum/#!topic/angular/pI0IgNHKjxw). I've gotten around this issue by doing something like myRef.push(angular.fromJson(angular.toJson(myAngularObject))).

like image 193
bennlich Avatar answered Nov 19 '22 19:11

bennlich


The issue is the $ in "$$hashKey", not the 0. 0 is allowed.

like image 6
Michael Lehenbauer Avatar answered Nov 19 '22 19:11

Michael Lehenbauer


I wanted to throw another answer in here that is much simpler, just use track by in your repeat. It will get rid of the $$hashKey attribute that is causing so much grief.

<div ng-repeat="item in items track by $index">{{item.name}}</div>
like image 4
stackSean Avatar answered Nov 19 '22 18:11

stackSean


I'm a bit late to this but I thought I would like to add my two cents as I was shaking my head to all the other answers. Hopefully this can help you to avoid this issue all together.

Use the angularFire library intended to handle angular data and use it's methods.

while yes you can use the pure javascript library methods to .push() .add() .update(), .set() ect.

So if you want to avoid any clashes when firebase communicates with angular javascript you need to be using the appropriate .$foo() methods (i.e. .$save()). In your case just add the $ to your .add() (make it .$add())

so use lessondata.$add($scope.topic);



differences when saving with firebase's vs angularfire's


AngularFire's $save() method is implemented using Firebase's set() method.

Firebase's push() operation corresponds to AngularFire's $add() method

Typically you should be using set()/$save() if you either have an object that already exists in the database or if you are working with objects that have a natural key. more info on that here: https://stackoverflow.com/a/35959496/4642530



Things to note with AngularFire


For a callback:

and if you want a callback to know if your data saved correctly you can do something like this:

var list = $firebaseArray(ref);
list.$add({ foo: "bar" }).then(function(ref) {
  var id = ref.key();
  console.log("added record with id " + id);
  list.$indexFor(id); // returns location in the array
});

I was surprised this wasn't mentioned earlier in any of the other answers, but take a look at these docs https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebasearray-addnewdata

Cheers.

like image 1
garrettmac Avatar answered Nov 19 '22 19:11

garrettmac


The best way to get rid of the $$hasKeys is to use "track by" in ng-repeats as in the following (as mentioned in the answer above)

<div ng-repeat="(key, value) in myObj track by $index"> ... </div>

but you can also set track as par of ng-model-options

ng-model-options="{ trackBy: '$value.someKeyOnYourObject' }"

on a form control. This way also improves performance of your angular app.

Another way is to remove the $$hashKey is using

angular.copy(someObj);

If all else fails, you could also use lodash to remove the keys that start with the "$".

_.omitBy(yourObject, function(value, key){return _.startsWith(key, '$');});
like image 1
Chase Oliphant Avatar answered Nov 19 '22 19:11

Chase Oliphant