1

Beginning to dive into AngularJS so I went to their website, but got stuck on the wire up a backend portion where Angular uses Firebase. The first issue came from the ordering of dependencies:

angular.module('project', ['ngRoute', 'firebase'])

changed to

angular.module('project', ['firebase', 'ngRoute'])

But now it's telling my that $add, in my $scope.save call, is undefined.

Similar $add undefined questions are here and here, but neither seem to apply.

Note: I'm running node's http-server, so I'm assuming it's not a localhost problem.

Scripts

angular.module('project', ['firebase', 'ngRoute'])

.value('fbURL', 'https://unique-url-yay.firebaseio.com/')
.service('fbRef', function(fbURL) {
  return new Firebase(fbURL)
})
.service('fbAuth', function($q, $firebase, $firebaseAuth, fbRef) {
  var auth;
  return function () {
      if (auth) return $q.when(auth);
      var authObj = $firebaseAuth(fbRef);
      if (authObj.$getAuth()) {
        return $q.when(auth = authObj.$getAuth());
      }
      var deferred = $q.defer();
      authObj.$authAnonymously().then(function(authData) {
          auth = authData;
          deferred.resolve(authData);
      });
      return deferred.promise;
  }
})

.service('Projects', function($q, $firebase, fbRef, fbAuth) {
  var self = this;
  this.fetch = function () {
    if (this.projects) return $q.when(this.projects);
    return fbAuth().then(function(auth) {
      var deferred = $q.defer();
      var ref = fbRef.child('projects/' + auth.auth.uid);
      var $projects = $firebase(ref);
      ref.on('value', function(snapshot) {
        if (snapshot.val() === null) {
          $projects.$set(window.projectsArray);
        }
        self.projects = $projects.$asArray();
        deferred.resolve(self.projects);
      });
      return deferred.promise;
    });
  };
})

.config(function($routeProvider) {
  $routeProvider
    .when('/', {
      controller:'ListCtrl',
      templateUrl:'list.html',
      resolve: {
        projects: function (Projects) {
          return Projects.fetch();
        }
      }
    })
    .when('/edit/:projectId', {
      controller:'EditCtrl',
      templateUrl:'detail.html'
    })
    .when('/new', {
      controller:'CreateCtrl',
      templateUrl:'detail.html'
    })
    .otherwise({
      redirectTo:'/'
    });
})

.controller('ListCtrl', function($scope, projects) {
  $scope.projects = projects;
})

.controller('CreateCtrl', function($scope, $location, Projects) {
  $scope.save = function() {
      Projects.projects.$add($scope.project).then(function(data) {
          $location.path('/');
      });
  };
})

.controller('EditCtrl',
  function($scope, $location, $routeParams, Projects) {
    var projectId = $routeParams.projectId,
        projectIndex;

    $scope.projects = Projects.projects;
    projectIndex = $scope.projects.$indexFor(projectId);
    $scope.project = $scope.projects[projectIndex];

    $scope.destroy = function() {
        $scope.projects.$remove($scope.project).then(function(data) {
            $location.path('/');
        });
    };

    $scope.save = function() {
        $scope.projects.$save($scope.project).then(function(data) {
           $location.path('/');
        });
    };
});

Index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Angular</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js">
    </script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-route.min.js">
   </script>
    <script src="https://cdn.firebase.com/js/client/2.0.4/firebase.js"></script>
    <script src="https://cdn.firebase.com/libs/angularfire/0.9.0/angularfire.min.js"></script>
    <script src="scripts.js" type="text/javascript"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body style="padding:20px;">
    <div ng-app="project" class="ng-scope"></div>
      <div ng-view></div>
  </body>
</html>
1
  • whew, i'm sorry you have to work with this Commented Jun 20, 2015 at 1:54

1 Answer 1

2

Alright, a few things are going on here:

Suggestions

  1. AngularFire was updated to 1.1.1 and $firebase is now deprecated. Use $firebaseObject and $firebaseArray instead.

  2. There is no need to do all that stuff in your Projects service and return a promise. $firebaseObject and $firebaseArray return promises.

Example

Check out this PLNKR I made showing a working version of what you're trying to accomplish.

  • It's tied to one of my public Firebase instances.
  • You can create a new piece of data and see it on the home page.

JavaScript:

(function(angular) {
  angular.module('project', ['firebase', 'ngRoute'])
  .value('fbURL', 'https://sb-plnkr.firebaseio.com/so:28942661')

  .service('fbRef', function(fbURL) {
    return new Firebase(fbURL)
  })

  .service('fbAuth', function($q, $firebaseAuth, fbRef) {
    var auth;
    return function () {
      if (auth) return $q.when(auth);
      var authObj = $firebaseAuth(fbRef);
      if (authObj.$getAuth()) {
        return $q.when(auth = authObj.$getAuth());
      }
      var deferred = $q.defer();
      authObj.$authAnonymously().then(function(authData) {
          auth = authData;
          deferred.resolve(authData);
      });
      return deferred.promise;
    }
  })

  .service('Projects', function($q, $firebaseArray, fbRef) {
    this.sync = $firebaseArray(fbRef);
    this.sync.$loaded().then(function(data) {
      var projects = data;
    });
    return this.sync;
  })

  .controller('ListCtrl', function($scope, $location, Projects) {
    Projects.$loaded().then(function(data){
      $scope.projects = data;
    });
  })

  .controller('CreateCtrl', function($scope, $location, Projects) {
    console.log("CreateCtrl");
    $scope.save = function() {
      console.debug("Adding");
      if ($scope.project && $scope.project.content !== '') {
        Projects.$add($scope.project).then(function(ref) {
          console.log("Added ref",ref);
          $location.path('/');
        }).catch(function(errorObject){
          console.error(errorObject);
        });
      } else {
        alert("You have to enter something.");
      }
    };
  })

  .controller('EditCtrl',function($scope, $location, $routeParams, Projects) {
    var projectId = $routeParams.projectId,
        projectIndex;

    $scope.init = function(){
      Projects.$loaded().then(function(data) {
        $scope.projects = data;
        console.info("EditCtrl - Projects.$loaded():",data);
        projectIndex = $scope.projects.$indexFor(projectId);
        $scope.project = $scope.projects[projectIndex];
      });
    }

    $scope.destroy = function() {
      $scope.projects.$remove($scope.project).then(function(data) {
        $location.path('/');
      });
    };

    $scope.save = function() {
      $scope.projects.$save($scope.project).then(function(data) {
       $location.path('/');
      });
    };
  })

  .config(function($routeProvider, $locationProvider) {
    $routeProvider
      .when('/', {
        controller:'ListCtrl',
        templateUrl:'list.html'
      })
      .when('/edit/:projectId', {
        controller:'EditCtrl',
        templateUrl:'detail.html'
      })
      .when('/new', {
        controller:'CreateCtrl',
        templateUrl:'create.html'
      })
      .otherwise({
        redirectTo:'/'
      });

      $locationProvider.html5Mode(true);
  });
})(window.angular);

HTML:

(index.html)

<body style="padding:20px;">
  <div ng-app="project" ng-controller="EditCtrl">
    <a href="new">New</a>
    <div ng-view></div>
  </div>
</body>

(create.html)

<h2>Create</h2>
<button ng-click="save()">Save</button>

(list.html)

<h2>List</h2>
<div ng-repeat="(key,data) in projects">
  <span>$scope.projects[<span ng-bind="key"></span>].content : </span>
  <span ng-bind="data.content"></span>
</div>
<h2>Object Debug</h2>
<pre ng-bind="projects | json"></pre>

Hope that helps!

Sign up to request clarification or add additional context in comments.

5 Comments

Honored to hear that from you @Kato :) Thanks for all the hard work your team has put into 1.0.0 - it's a game changer.
@Kato Do you have any recommendations on where to find up-to-data tutorials? Basically anything that doesn't use outdated APIs.
@NOPENOPENOPE You're most welcome. Thoughts on your last question: I think getting past this roadblock was more about understanding AngularJS in general - promises/services/factories etc. The Firebase team always keeps docs up to date, and those docs have lots of awesome snippets. AngularFire 1.0.0 was just released a couple days ago, so it makes sense that examples on AngularJS site for AngularFire are outdated. It's good habit to check the official docs for whatever you're using, first, before going to examples. firebase.com/docs/web/libraries/angular/guide
For example, on the intro-to-angularfire page, you can see an example for creating a basic factory for an $firebaseArray that is pretty similar to what we did here. You can even run the code sample right in the docs! And there are even more examples on the next page, synchronized-objects.
And last but not least... The synchronized-arrays full example shows a use case of the .$loaded() method. Again, you can run/edit all of these examples right in the Firebase docs. pretty cool Each of the getting started guides have these executable examples that you can tinker with to your liking :) When you are done with the guides, definitely read over the complete AngularFire API documentation as well

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.