3

A similar question was asked here, but the one accepted answer doesn't really answer the question.

Using AngularFire, is it possible to to create relational-style databases? Or access the UniqueIDs?

When adding nested items to Firebase via AngularFire, each item is actually set under another index numbered numerically.

enter image description here

Because of this, I'll need to reference the user's products with the following relative url:

users/:id/products

My question is, once I have created a user (or anything for that matter), how do I get the index value?

// Get the users
var ref = new Firebase('https://myapp.firebaseio.com/users')
var promise = angularFire(ref, $scope, 'users', {})
promise.then(function() {
  // Can I get the ID from here somehow?
})

// Users saves a product
id = null // ???
var product = {
   name: 'new product',
   desc: 'this is a new product'
}
var ref = new Firebase('https://myapp.firebaseio.com/users/' + id + '/products')
var promise = angularFire(ref, $scope, 'products', {})
promise.then(function() {
  $scope.products.push(product)
})

Update

To clarify, this isn't a question about user authentication. I already have that taken care of. Sorry for the confusion.

I've just run into a brick wall when I start making things "under" other things in Firebase. Doesn't matter if it's users or giraffes.

If I make "Stores," each Store has "Products" (let's say.)

I'd like to be able to retrieve them with

stores/{storeId}/products

But the storeId would ideally be the index ID that is created from AngularFire (See the picture I have attached). The trouble is, AngularFire just creates this ID without letting me know about it.

If I had some success function like

success: function(response) { 
  $scope.store.id = response.indexId
}

That would make the most sense, but it doesn't appear AngularFire is prepare this very needed functionality. Prove me wrong, please. :)

Community
  • 1
  • 1
Adam Grant
  • 12,477
  • 10
  • 58
  • 65
  • fyi, the last argument of your angularFire() call, {}, is now deprecated. You can accomplish a similar thing by doing `$scope.users = {}` before initing the angularFire. – bennlich Oct 19 '13 at 15:29
  • @bennlich am I wrong in thinking you will always get an object back no matter how you initialize before `angularFire` returns? And if so, isn't the suggestion that you can use `.push()` on an implicit `angularFire` binding in the [docs](http://angularfire.com/documentation.html) inaccurate? I know this is slightly off-topic but I think still relevant. – hiattp Oct 20 '13 at 15:54
  • @hiattp Why would .push() be wrong? – Adam Grant Oct 20 '13 at 17:33
  • Well, JavaScript objects don't have a push method, just arrays. – hiattp Oct 20 '13 at 18:02
  • @hiattp See this fiddle: http://jsfiddle.net/4zGvz/2/. You get an object back if it starts life as an object, and an array back if it starts life as an array. Otherwise it waits to see what kind of data already exists at the reference. – bennlich Oct 21 '13 at 03:37
  • @bennlich wow that's super helpful thanks. I suppose I've been working with pre-existing/pre-typed data so I assumed they were always objects. – hiattp Oct 21 '13 at 04:15

3 Answers3

4

There definitely seems to be a way to do everything you'll want. Your first question was:

var promise = angularFire(ref, $scope, 'users', {})
promise.then(function() {
  // Can I get the ID from here somehow?
})

Once the promise returns for this call, your $scope.users will be an object of users whose keys are the id values of the users you've created. So to access the ids of those users:

var promise = angularFire(ref, $scope, 'users')
promise.then(function() {
  for(var userId in $scope.users){
    console.log("User Id: " + userId);
  }
});

This doesn't seem tremendously helpful to what you are ultimately trying to achieve, but at least you can see how to return the ids for all users from AngularFire.

Since you want to create products under users I think @bennlich was trying to get at the fact that the user's id should be available from some other variable, like a user object if you are using angularFireAuth. But whenever you do have the id, there are multiple ways to create objects under that user. Give that you have the user's id, you'll have the following ref:

var userProductRef = new Firebase('https://myapp.firebaseio.com/users/' + userId + '/products');

So one way to create a product using AngularFire would be creating an explicit data binding with angularFireCollection:

$scope.userProducts = angularFireCollection(userProductRef);
// create a new product
var newProductRef = $scope.userProducts.add({name: 'new product', desc: 'this is a new product'});
console.log("New product id: " + newProductRef.name());

If you wanted to use the implicit data binding, you wouldn't need to use AngularFire directly for the object creation at all, as the data sync is "implied." In this case you have to keep in mind that AngularFire is just an extension/augmentation of the vanilla Firebase API, and is not intended to be a substitute. So you might have something like this, using the Firebase .push method to create the id:

// sync $scope.userProducts with Firebase
var promise = angularFire(userProductRef, $scope, 'userProducts');
// create a new product when promise fulfilled
promise.then(function(){
  var newProductId = userProductRef.push(); // here is your id
  // this will sync to Firebase automatically because of the implied angularFire binding
  $scope.userProducts[newProductId] = {name: 'new product', desc: 'this is a new product'};
});

These methods use AngularFire for object creation, but as mentioned I think it helps not to think of AngularFire as a replacement for the Firebase API, it just makes common Angular use cases much easier. Depending on how your view and CRUD actions are structured, it may or may not make since to use AngularFire for Creation, even if it is useful for Read/Update/etc actions. And as a final note, while you can do this type of relational data structuring in Firebase using AngularFire, it's likely to cause difficulties later. You should strongly consider restructuring (de-normalizing) your data to optimize for Firebase's key/value store design.

hiattp
  • 2,326
  • 1
  • 20
  • 23
  • The only little thing I would clarify in this answer is that in the last example you don't /have/ to use push() to generate the id (though it's certainly not a bad idea to do so). Also I'm not sure how important it is that you wait for the promise to be fulfilled in the last example. Any idea about this? – bennlich Oct 20 '13 at 15:05
  • Without using your own id or push(), can you create the new product and still retrieve the id? I think I see what you mean, but the goal seemed to be using Firebase-generated ids, maybe I misunderstood. I needed to wait for the promise in this case only because I didn't set `$scope.userProducts = {}`, so its actually undefined until the promise fulfills. – hiattp Oct 20 '13 at 15:48
  • Granted, it would be cleaner if you predefined and didn't wait. I was trying to be consistent with the OP's original code so it would look more familiar. – hiattp Oct 20 '13 at 16:02
  • Wow. Thank you for the very helpful response. I'll try this out and return to this post. – Adam Grant Oct 20 '13 at 16:45
  • I really just want a good clean solution, whatever it is. The docs act as if I'm going to write a rule for each and every row. It is of such little help to read the Firebase Docs, frankly. – Adam Grant Oct 20 '13 at 17:15
  • "...the user's id should be available from some other variable, like a user object if you are using angularFireAuth" Should, but can't. I have to do this myself. Firebase could easily provide this essential functionality. – Adam Grant Oct 20 '13 at 17:17
  • Sorry to drag this out, but I feel we still haven't arrived at one simple, recommended way to store things under things. I have a continuation here: http://stackoverflow.com/questions/19481149/basic-user-authentication-with-records-in-angularfire Hopefully there is an end to this labyrinth. – Adam Grant Oct 20 '13 at 18:28
  • @hiattp [Firebase community manager here] You are awesome at answering Firebase-tagged questions on SO! Could you email me (sara@firebase)? I'd like to send you some Firebase swag! – Sara Oct 24 '13 at 23:08
3

In the new version of AngularFire, you can just iterate through the AngularFire object as though it were a normal javascript object.

$scope.users = {};
angularFire(ref, $scope, 'users').then(function() {
    for (var id in $scope.users) {
        if ($scope.users.hasOwnProperty(id) {
            ...
        }
    }
});

I don't think this is really what you want to do though, as you'd be loading all user data into every client. You should probably have the client provide a username or some other kind of id, maybe with Firebase Simple Login.

Finally, are you familiar with your browser's developer tools? They can be extremely useful for peeking inside of objects you don't know what to do with, like an AngularFire, to see how they're structured internally.

bennlich
  • 1,207
  • 10
  • 21
  • You're right, I'm just using users as an example. I'm well versed with developer tools and the front end in general, but the bottleneck is getting Firebase to send back the data I need without having to do a lot of JSON manipulation in the front end. – Adam Grant Oct 19 '13 at 17:15
  • Hmm, okay. Maybe it would help to know what you'd like to fetch then? Storing products under a user id makes sense, you just need the user to authenticate so that you have an id to fetch with. – bennlich Oct 19 '13 at 21:19
  • I haven't voted this up or marked it yet because I don't see how this does the following: (1) Create a record (user, product, item, whatever), (2) Get the index id as it is created in Firebase from AngularFire. – Adam Grant Oct 19 '13 at 22:09
  • I've clarified in an update above. Thanks, @bennlich. – Adam Grant Oct 19 '13 at 22:14
  • Check out hiattp's answer. – bennlich Oct 20 '13 at 15:03
1

I have the best and quickest answer for all such kind of problems guys!!!

var chru;
    var ref = new Firebase("https://myapp.firebaseio.com/users/");
    var sync = $firebase(ref);
    var users= syncSelect.$asArray();
    $scope.users= users;
    users.$loaded().then(function() {
        chru = users.length;
    });

    $scope.specificIdOnSelect = function() {
        var jj;
        for (jj = 0; jj < chru; jj++) {
            var keyer = users.$keyAt(jj);
            var rec = users.$getRecord(keyer);

            if ($scope.userSelected== rec.name) {
                alert("Unique Key of " + $scope.users+ " is " + users.$keyAt(jj));
            }
        }
    };

    $scope.allIds = function() {
        var jj;
        for (jj = 0; jj < chru; jj++) {
            var keyer = users.$keyAt(jj);
            var rec = users.$getRecord(keyer);
            alert("Unique Key of " + $scope.users+ " is " + users.$keyAt(jj));
        }
    };

Try it and then please let me know if this helps!!!