4
votes

I recently switched from angularfire 0.6 to to 0.8.0. I am having problems to save an item of a list, that contains an array itself.

My Objects account look like this:

{
  "-JQruasomekeys0nrXxH" : {
    "created" : "2014-03-23T22:00:10.176Z",
    "durations" : [ {
      "$$hashKey": "00L", // this is generated by ng-repeat on arrays
      "end" : "2014-07-15T22:00:00.000Z",
      "start" : "2014-07-09T22:00:00.000Z"
    } ],
    "email" : "[email protected]",
  }
}

The durations are an array of time periods with start and end, which are resembled by a ng-repeat of two input fields in HTML.

<tr ng-repeat="duration in account.durations">
  <td>
     <input ng-model="duration.start" datepicker>
  </td>
  <td>
     <input ng-model="duration.end" datepicker>
  </td>
</tr>

Before saving the account to firebase, I am doing angular.copy($scope.account) in the Controller, to get rid of the angular $$hash values. This worked in angularfire 0.6.0.

In angularfire 0.8.0, I am still getting the error:

Error: Firebase.set failed: First argument contains an invalid key ($$hashKey) in property 'durations.0'. Keys must be non-empty strings and can't contain ".", "#", "$", "/", "[", or "]

How is this meant to be solved by angularfire, when working with objects, that have arrays within themselves? Durations won't be the only array I will have within the account. So is there a more professional solution, or do i have to angular.copy every single array object before saving to firebase via angularfire?

Thanks in advance for any hint.

Update (11. Aug 14) After doing a bit more research, the Problem is not that using angular.copy() does not work any more. actually it does. But it is very unhandy to update/modify an existing dataset with the angularfire method $save() (https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebaseobject-save) Because I can't seem to find a proper way to $save() an item after doing angular.copy().

Update 2 (12 Aug 14) I found a workaround for the moment, but actually a workaround is what I wanted to avoid in the first place:

// loop for every duration, that should clean the object from "$$hashKey" attributes
angular.forEach($scope.account.durations, function (value, index) {
  var cleanValue = {};

  // checks, if the date is a JSON string already
  if (typeof value.start === 'string') {
    cleanValue.start = value.start;
  } else {
    cleanValue.start = value.start.toJSON();
  }

  // checks, if the date is a JSON string already
  if (typeof value.end === 'string') {
    cleanValue.end = value.end;
  } else {
    cleanValue.end = value.end.toJSON();
  }

  // overwrite the array object at index to get loose of $$hashKey
  $scope.account.durations[index] = cleanValue;
});

I think that this workaround takes the advantages off the document based principle in Firebase, as I have to know the exact attributes of an object before storing it via AngularFire.

Update 3 (13. AUg 14) I added a jsfiddle to show the problem: jsfiddle.net/1fem6byt

If you add lines to the array, and then try and save the object to the firebase, the $$hashKey error (given above) appears in the console. There are ways to workaround this problem, but I am looking for an easier, or cleaner solution with angularfire, if possible. I might not do adding of lines correctly – or do I miss something?

1
Which version of angularjs are you using with angularfire 0.8.0?runTarm
I don't see any $$hashKey in your data or any of the code setting up or saving this list. Where is that coming from? It's going to be hard to suggest a fix if the problem set is incomplete.Kato
I'm using angular 1.2.21. The $$hashKey is generated by ng-repeat in angular. Actually during creating of the account using the method $add() (firebase.com/docs/web/libraries/angular/…) angular.copy() works. But I can't use angular.copy() when using $save() when updating the data (firebase.com/docs/web/libraries/angular/…)Rias
Your code is still incomplete. What are we copying (and why exactly?). And where is $asObject() being used? Try creating a small sample of code that reproduces your problem. This will produce much more intelligent answers and faster fixes.Kato
@Rias, this structure still doesn't make any sense. If you want an array of durations fetch those. Don't fetch the parent (account/) as an object and then try and use it's child as a collection. The embedded array is not going to work since angular attaches invalid properties to it. I'll look into some ideas for a future release. However, AngularFire is a bindings library--it binds objects and collections--not a local copy of your Firebase data tree. Just get the collection directly.Kato

1 Answers

2
votes

Of course the best way to solve it in firebase like @Kato mentioned and have the real time binding to data from array into view you should use an extra firebase synchronized object through the $asArray instance.

Like in this solution: http://jsfiddle.net/RaVbaker/sb00kor8/1/ But you will loose then possibility to save data "on demand" with button like Save to Firebase. They will be saved instantly after change in firebase array object occurs. But it will also give you flexibility and synchronization of data across pages.

But other and much easier in your case is to change the way angular tracks uniqueness of objects in ng-repeat directive. It can be done by setting track by $index in the end of ng-repeat statement.

Changing ng-repeat="duration in account.durations" into this:

 ng-repeat="duration in account.durations track by $index"

Your jsfiddle updated with this solution: http://jsfiddle.net/RaVbaker/sb00kor8/2/

More info about track by for ngRepeat you can find obviously in official documentation.

Hope I helped.