Search code examples
angularjsangular-resource

Struggling to resolve resource for route in AngularJS


I'm currently working on a web app that uses Django REST Framework for the back end and AngularJS for the front end. I'm still fairly new to Angular and I'm struggling to get the list of projects to resolve before the page finishes rendering. Whenever the page loads, the Chromeconsole reports the following error:

Error: [$injector:unpr] Unknown provider: projectsProvider <- projects <- HomeCtrl
http://errors.angularjs.org/1.4.2/$injector/unpr?p0=projectsProvider%20%3C-%20projects%20%3C-%20HomeCtrl
    at REGEX_STRING_REGEXP (http://127.0.0.1:8000/static/bower_components/angular/angular.js:68:12)
    at http://127.0.0.1:8000/static/bower_components/angular/angular.js:4264:19
    at Object.getService [as get] (http://127.0.0.1:8000/static/bower_components/angular/angular.js:4411:39)
    at http://127.0.0.1:8000/static/bower_components/angular/angular.js:4269:45
    at getService (http://127.0.0.1:8000/static/bower_components/angular/angular.js:4411:39)
    at Object.invoke (http://127.0.0.1:8000/static/bower_components/angular/angular.js:4443:13)
    at ident.$get.extend.instance (http://127.0.0.1:8000/static/bower_components/angular/angular.js:9001:34)
    at nodeLinkFn (http://127.0.0.1:8000/static/bower_components/angular/angular.js:8111:36)
    at compositeLinkFn (http://127.0.0.1:8000/static/bower_components/angular/angular.js:7543:13)
    at publicLinkFn (http://127.0.0.1:8000/static/bower_components/angular/angular.js:7418:30) <div ng-view="" class="ng-scope">

Here's the routes:

var app = angular.module('projectile', [
    'ngRoute',
    'btford.markdown',
    'projectile.controllers',
    'projectile.services'
])

.config(['$httpProvider', function($httpProvider) {
  $httpProvider.defaults.xsrfCookieName = 'csrftoken';
  $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
}])

.config(function ($routeProvider) {
  $routeProvider
    .when('/', {
      templateUrl: '/static/templates/home.html',
      resolve: {
        projects: function (MultiProjectLoader) {
          return MultiProjectLoader();
        }
      },
      controller: 'HomeCtrl'
    })
    .otherwise({
      redirectTo: '/'
    });
});

Here's the controllers:

angular.module('projectile.controllers', [
        'projectile.directives',
        'projectile.services',
        'ngRoute',
        'btford.markdown'
])

.controller('HomeCtrl', function ($scope, projects, Issue) {
  // Get open issues
  $scope.issues = Issue.query({ open: true });

  // Get projects
  $scope.projects = projects;
});

Here's the services:

angular.module('projectile.services', ['ngResource'])

.factory('Project', function ($resource) {
  return $resource('/api/projects/:projectId');
})

.factory('MultiProjectLoader', function (Project, $q) {
  return function() {
    var delay = $q.defer();
    Project.query(function(projects) {
      delay.resolve(projects);
    }, function() {
      delay.reject('Unable to fetch projects');
    });
    return delay.promise;
  };
});

What's gone wrong? And how do I resolve it? If I use debugger inside the controller, projects is defined and contains the correct data, so I'm rather stumped.

I can just pass through Project and run Project.query() to get the result, but that means the page gets updated after it's rendered. I have a spinner directive in place and I want to wait for the request to finish before rendering the page, which I understand resolve is for.


Solution

  • As your factory directly returning a promise, you should not call the function MultiProjectLoader factory. Instead you should only return promise from the resolve function.

    projects: function (MultiProjectLoader) {
        //removed function bracket
        return MultiProjectLoader; //returned the promise directive from resolve.
    }
    

    But as singleton is considered you shouldn't define factory this way. You should return object from factory and that will have various method which will used for different purpose.

    Factory

    .factory('MultiProjectLoader', function(Project, $q) {
        return {
            projectQuery: function() {
                return roject.query().$promise.then(function(projects) {
                    return projects;
                }, function(error) {
                    return error;
                });
            };
        }
    });
    

    Resolve

    projects: function (MultiProjectLoader) {
        return MultiProjectLoader.projectQuery(); //returned the promise
    }