Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase $id from second level Object

So I have this for example in firebase

clients {
   //clients with unique keys {
       invoices: {
         // Invoices with unique Keys
       }
   }
}

I'm returning all this with one ref like so:

.controller('singleClientController', function($scope, $firebaseObject, fbUrl, $routeParams) {

    var id = $routeParams.id;

    var singleRef = new Firebase(fbUrl+'/clients/'+id);
    var client = this;

    client.details = $firebaseObject(singleRef);

})

so in my html, I'm trying to return the $id for both the client and the invoice. I am able to get the client no problem with {{client.details.$id}} but when I try to do the same thing with the invoice id {{invoice.$id}} I don't get anything.

The invoices are displayed through a foreach like so:

<tr ng-repeat="invoice in client.details.invoices">
     <td>
         <a href="#/invoices/details/{{invoice.$id}}/{{client.details.$id}}">
            {{invoice.settings.number}}
         </a>
     </td>
     ...
</tr>

Is it because the invoices are inside the client? If so, how would you return the id for the invoices? It's driving me nuts! Please help!

For a better understanding of my firebase set-up here is a screenshot of what I'm talking about.

enter image description here

like image 406
moevans Avatar asked Feb 10 '23 03:02

moevans


1 Answers

tl;dr - In Firebase it is ideal to have a flat data structure.

JSBin Example

In your case you have invoices nested under clients.

{
   "clients": {
      "1": {
         "name": "Alison",
         "invoices": {
            "0001": {
              "amount": 500
              "paid": true
            }
         }
      }
   }
}

This feels natural because the invoices are nicely grouped underneath the proper client. However, this can lead to bad performance in syncing data with Firebase. Firebase reads all data underneath a node.

This means every time you read /clients/1 you also get the invoices downloaded over the network. Even if you just need the client's name, you'll also get the the invoices.

The solution is to flatten your data structure.

{
   "clients": {
      "1": {
        "name": "Alison"
      }
   },
   "clientInvoices": {
     "1": {
        "0001": {
          "amount": 500
          "paid": true
        }
     }
   }
}

The important part to grasp here is the shared key.

In this example the key is 1. This is for simplicity. In reality you'll likely use a .push() id.

By using this pattern you'll still be able to retrieve all of the invoices for the client by simply knowing their key. This also decouples the client from the invoices.

As an added benefit your controller code and ng-repeat will be much easier.

In your case you should switch from a $firebaseObject to a $firebaseArray for the invoices. We can even create a helper factory that gets the invoices by a client's id.

.factory('invoices', function(fbUrl, $firebaseArray) {
   return function(clientId) {
      var ref = new Firebase(fbUrl).child('clientInvoices').child(clientId);
      return $firebaseArray(ref);
   }
})

// While we're at it, lets create a helper factory for retrieving a
// client by their id
.factory('clients', function(fbUrl, $firebaseObject) {
    return function(clientId) {
       var ref = new Firebase(fbUrl).child('clients').child(clientId);
       return $firebaseObject(ref);
    }
})

Now inject the helper factories into your controller and use the $routeParams.id to retrieve the client's invoices:

.controller('singleClientController', function($scope, $routeParams, invoices, clients) {

    $scope.client = clients($routeParams.id);
    $scope.clientInvoices = invoices($routeParams.id);

})

Now binding it to your template is a breeze:

<tr ng-repeat="invoice in clientInvoices">
     <td>
         <a href="#/invoices/details/{{invoice.$id}}/{{client.$id}}">
            {{invoice.settings.number}}
         </a>
     </td>
     ...
</tr>
like image 83
David East Avatar answered Feb 13 '23 06:02

David East