Adam Youngers
Adam Youngers

Reputation: 6749

Saving structured data in firebase with angular

I understand the concepts and best practices of saving structured data in firebase, but I'm not clear on how to go about actually saving the data to multiple locations and providing the cross references needed.

{
  articles: {
    -KBX5TurV9uJTeiR26-N: {
       title: 'post title 1',
       body: 'post body goes here',
       imagesRef: {
          -KBX5XOYASP7h2ZPOKmg: true
       }
    },
    -KCe7cy6QC29WRYap0D1: {
       title: 'post title 2',
       body: 'post body goes here',
       imagesRef: {
          -KBX5XOYASP7h2ZPOKmg: true
       }
    }
  }
  images: {
    -KBX5XOYASP7h2ZPOKmg: {
       image: 'data:image/gif;base64,R0lGODlhZABkAOYAAPj4...',
       articlesRef: {
          -KBX5TurV9uJTeiR26-N: true,
          -KCe7cy6QC29WRYap0D1: true
       }
    }
  }
}

The above schema is an example of what I am going for. On submit of a form consisting of a title, body and image field, I need to post the title and body to the articles object, then post image to the images object, then update the imagesRef and articlesRef objects with cross references. What is the best way to go about this in Angular. I also have angularfire as part of the project.

I'm new to angular and firebase but I think I would need to do two posts one to articles and one to images to save them and generate the unique ids. I would then need to some how wait for both to succeed, grab the unique ids and do and update on both objects to save the cross references. Here is what I have so far, but not sure how to take it further...

     $scope.AddPost = function(){

        var ref = new Firebase(FIREBASE_URI);
        var newArticleRef = ref.child('articles').push();
        var newArticleKey = newArticleRef.key();

        // Create the data we want to update
        var addNewPost = {};
        addNewPost["articles/" + newArticleKey] = {
            title:   $scope.article.title,
            post:    $scope.article.post
        };

        if ($scope.image) {

            var newImageRef = ref.child('images').push();
            var newImageKey = newImageRef.key();

            // Add image...
            addNewPost['images/' + newImageKey] = {
                image: $scope.image
            };

            //Add article ref...
            addNewPost['images/' + newImageKey + '/articles/' + newArticleKey] = true;

            //Add cross ref to article...
            addNewPost['articles/' + newArticleKey + '/image/' + newImageKey] = true;

        }


        // Do a deep-path update
        ref.update(addNewPost, function(error) {
                if (error) {
                    console.log("Error:", error);
                }
        });

    });

UPDATE: I updated the code sample to make use of multi-location updates and the fan-out approach, but I'm getting an error on save...

Error: Firebase.update failed: First argument contains a path /images/-KCpx-Pj9oMM-EWN9irS that is ancestor of another path /images/-KCpx-Pj9oMM-EWN9irS/articles/-KCpx-Pj9oMM-EWN9irR
at Error (native)
at ig (http://localhost:8000/app/assets/js/plugins.js:430:390)
at jg (http://localhost:8000/app/assets/js/plugins.js:431:383)
at X.update (http://localhost:8000/app/assets/js/plugins.js:564:369)
at r.$scope.AddPost (http://localhost:8000/app/assets/js/app.js:171:17)
at fn (eval at <anonymous> (http://localhost:8000/app/assets/js/plugins.js:216:110), <anonymous>:4:212)
at e (http://localhost:8000/app/assets/js/plugins.js:257:177)
at r.$eval (http://localhost:8000/app/assets/js/plugins.js:133:446)
at r.$apply (http://localhost:8000/app/assets/js/plugins.js:134:175)
at r.scopePrototype.$apply (chrome-extension://ighdmehidhipcmcojjgiloacoafjmpfk/dist/hint.js:1427:22)

WARNING: I tried the following. It created a single object and did not trigger a JS error, but it wiped out my DB leaving only the newly created object. Didn't lose much as I'm just running some tests right now, but thought that I should warn others. Do Not Do this...

        addNewPost.Articles = {};
        addNewPost.Articles[newArticleKey] = {};
        addNewPost.Articles[newArticleKey].title = $scope.article.title;
        addNewPost.Articles[newArticleKey].post = $scope.article.post;

        var newImageRef = ref.child('images').push();
        var newImageKey = newImageRef.key();

        addNewPost.images = {};
        addNewPost.images[newImageKey] = {};
        addNewPost.images[newImageKey].image = $scope.image;
        addNewPost.images[newImageKey].articles = {};
        addNewPost.images[newImageKey].articles[newArticleKey] = true;

        addNewPost.Articles[newArticleKey].image = {};
        addNewPost.Articles[newArticleKey].image[newImageKey] = true;

Upvotes: 4

Views: 1000

Answers (1)

Adam Youngers
Adam Youngers

Reputation: 6749

Not sure its the best answer, but I found the following worked for me. It makes use of Frank van Puffelen's suggestion to use Multi-location updates. The following code was setup to support adding a new image, using an existing image or no image at all. I figure the same could apply to any meta data be in categories, tags, or users.

    .controller('AddPostController', ['$scope','$firebaseArray','FIREBASE_URI',
    function($scope,$firebaseArray,FIREBASE_URI) {

        var ref = new Firebase(FIREBASE_URI);

        $scope.AddPost = function(){

            var newArticleRef = ref.child('articles').push();
            var newArticleKey = newArticleRef.key();

            var newImageRef = ref.child('images').push();
            var newImageKey = newImageRef.key();

            // Create the data we want to update
            var addNewPost = {};

            // Add new article...
            addNewPost["articles/" + newArticleKey] = {
                title:   $scope.article.title,
                post:    $scope.article.post,
                emailId: user,
                '.priority': user
            };

            if ($scope.image) {

                // Add new image reference to new article...
                addNewPost["articles/" + newArticleKey].image = {};
                addNewPost["articles/" + newArticleKey].image[newImageKey] = true;

                // Add new image...
                addNewPost['images/' + newImageKey] = {
                    image: $scope.image,
                    emailId: user,
                    '.priority': user
                };

                // Add article reference to new image...
                addNewPost['images/' + newImageKey].articles = {};
                addNewPost['images/' + newImageKey].articles[newArticleKey] = true;

            } else if ($scope.article.image) {

                // Add existing image reference to article...
                addNewPost["articles/" + newArticleKey].image = {};
                addNewPost["articles/" + newArticleKey].image[$scope.article.image] = true;

                // Add new article reference to existing image...
                addNewPost['images/' + $scope.article.image + '/articles/' + newArticleKey] = true;

            }

            // Do a deep-path update
            ref.update(addNewPost, function(error) {
                if (error) {
                    console.log("Error:", error);
                }
            });

        };

    }]);

Upvotes: 2

Related Questions