0

I'm currently working on this AngularJS Tutorial: Learn to Build Modern Web Apps with Angular and Rails https://thinkster.io/angular-rails/ which I think is a great resource showing how to build Rails Web Apps with AngularJS.

So far, I've completed over two thirds of the tutorial successfully, but now I'm facing some issues with factory services. When I hit the post button to create new post, I get the following error:

enter image description here

Error message

angular.js?body=1:11608 TypeError: undefined is not a function
    at Scope.$scope.addPost (http://0.0.0.0:3000/assets/home/mainCtrl.js?body=1:23:24)

It points to the code below in mainCtrl.js file:

                posts.create({
                    title: $scope.title,
                    link: $scope.link,
                });

the entire mainCtrl.js file:

angular.module('flapperNews')
.controller('MainCtrl', ['$scope', 'posts', function ($scope, posts) {

        $scope.test = 'Hello world!';

        $scope.posts = posts.posts;

        $scope.posts.push({
            title: $scope.title,
            link: $scope.link,
            upvotes: 0,
            comments: [
              { author: 'Joe', body: 'Cool post!', upvotes: 0 },
              { author: 'Bob', body: 'Great idea but everything is wrong!', upvotes: 0 }
            ]
        });

        $scope.addPost = function () {

            if (!$scope.title || $scope.title === '') { return; }
                 posts.create({
                    title: $scope.title,
                    link: $scope.link,
                });
            $scope.title = '';
            $scope.link = '';
        };

        $scope.incrementUpvotes = function(post) {
            posts.upvote(post);
        };
}]);

In the above controller, if the addPost function is replaced with the one that was used in the tutorial before the factory create method was introduced, then it works fine.

Working code:

$scope.addPost = function(){
  if(!$scope.title || $scope.title === '') { return; }
  $scope.posts.push({
    title: $scope.title,
    link: $scope.link,
    upvotes: 0
  });
  $scope.title = '';
  $scope.link = '';
};

So somehow the factory's posts.create method is causing the issue (although posts.posts is accesible).

Below is post.js file which o.create method is causing the current issue

angular.module('flapperNews').factory('posts', ['$http',
    function($http){

        var o = {
            posts: []
        };
        return o; 

        o.getAll = function() {
            return $http.get('/posts.json').success(function(data){
                angular.copy(data, o.posts);
            });
        };

        o.create = function(post) {
            console.log("o.create");
            return $http.post('/posts.json', post).success(function(data){
                o.posts.push(data);
            });
        };

        o.upvote = function(post) {
          return $http.put('/posts/' + post.id + '/upvote.json')
            .success(function(data){
              post.upvotes += 1;
            });
        };

        resolve: {
            postPromise: ['posts', function(posts){
                return posts.getAll();
            }]
        }

    }
]);

app.js file

angular.module('flapperNews', ['ui.router', 'templates'])
.config([
  '$stateProvider',
  '$urlRouterProvider',
  function($stateProvider, $urlRouterProvider) {
    $stateProvider
      .state('home', {
        url: '/home',
        templateUrl: 'home/_home.html',
        controller: 'MainCtrl'
      })
      .state('posts', {
        url: '/posts/{id}',
        templateUrl: 'posts/_posts.html',
        controller: 'PostsCtrl'
      })
    $urlRouterProvider.otherwise('home')
}]);

If someone knows what the underlying issue here, then please give advice. Many Thanks :-)

My rails version 4.0.2 and I'm using Linux Ubuntu 12.04

Some thoughts, if I can't get the factory methods working, then I might put those methods directly in controllers to resolve the issue, hopefully :-)

2 Answers 2

1

You returned the object too soon, the other functions didn't register (since they are function expressions, and not function declarations, they didn't get hoisted). Move your return o; to the end and it will work:

app.factory('posts', ['$http',
    function($http){

        var o = {
            posts: []
        };


        o.getAll = function() {
            return $http.get('/posts.json').success(function(data){
                angular.copy(data, o.posts);
            });
        };

        o.create = function(post) {
            alert('In create!');
            console.log("o.create");
            return $http.post('/posts.json', post).success(function(data){
                o.posts.push(data);
            });
        };

        o.upvote = function(post) {
          return $http.put('/posts/' + post.id + '/upvote.json')
            .success(function(data){
              post.upvotes += 1;
            });
        };

        resolve: {
            postPromise: ['posts', function(posts){
                return posts.getAll();
            }]
        }
        return o;

    }
])

See this working Fiddle.

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

1 Comment

Thanks million :-) for sorting out my problem. Now it works! + I'll definitely read more about JavaScript hoisting from the link you provided - thanks again.
1

Your resolve statement should go in the home state in your app.js. As the tutorial says, "By using the resolve property in this way, we are ensuring that anytime our home state is entered, we will automatically query all posts from our backend before the state actually finishes loading."

App.js:

angular.module('flapperNews', ['ui.router', 'templates'])

.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {

  $stateProvider
  .state('home', {
      url: '/home',
      templateUrl: 'home/_home.html',
      controller: 'MainCtrl',
      resolve: {
        postPromise: ['posts', function(posts){
          return posts.getAll();
        }]
      }
    })

  .state('posts', {
      url: '/posts/{id}',
      templateUrl: 'posts/_posts.html',
      controller: 'PostsCtrl'
 })

   $urlRouterProvider.otherwise('home');
}]);

Comments

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.