0

I'm creating a login system for Angular application and got stuck on unknown provider error. I checked everything for 5 times and I don't see what's wrong.

I'm using Webpack as a build system.

The error text is as follows:

angular.js:68 Uncaught Error: [$injector:modulerr] Failed to instantiate module Universe due to:
Error: [$injector:modulerr] Failed to instantiate module layout due to:
Error: [$injector:unpr] Unknown provider: Self

Under suspition are 4 javascript files, constituting 3 modules:

  • app.js - contains Universe module
  • layout.module.js - contains layout module
  • auth.module.js - contains auth module
  • self.service.js - contains SelfResource factory for Self service in auth

Resource factory Self, representing information about logged-in user, is causing the error.

app.js

'use strict';

require("expose?$!expose?jQuery!jquery");
require("metisMenu/dist/metisMenu");
require("iCheck/icheck");
require("expose?_!lodash"); // was underscore

var angular = require("angular");
require("angular-animate");
require("angular-messages");
require("angular-resource");
require("angular-sanitize");
require("angular-cookies");
require("angular-ui-router");
require("bootstrap/dist/css/bootstrap.css");
require("font-awesome/css/font-awesome.css");
require("angular-bootstrap");

require("../assets/styles/style.scss");
require("../assets/fonts/pe-icon-7-stroke/css/pe-icon-7-stroke.css");
require("../assets/fonts/pe-icon-7-stroke/css/helper.css");

// Import all html files to put them in $templateCache
// If you need to use lazy loading, you will probably need
// to remove these two lines and explicitly require htmls
const templates = require.context(__dirname, true, /\.html$/);
templates.keys().forEach(templates);

var LayoutModule = require("layout/layout.module");
var HomeModule = require("home/home.module");
var AccountModule = require("account/account.module");
var WorkflowModule =  require("workflow/workflow/workflow.module");
var TaskModule = require("workflow/task/task.module");
var ToolModule = require("workflow/tool/tool.module");

var NavigationModule = require("../components/navigation/navigation.module");
var PanelModule = require("../components/panel/panel.module");
var UniverseDirectives = require("../components/directives");
var AuthModule = require("../components/auth/auth.module");

angular.module("Universe", [
    "ngAnimate",
    "ngMessages",
    "ngResource",
    "ngSanitize",
    "ngCookies",
    "ui.router",
    "ui.bootstrap",

    LayoutModule.name,
    HomeModule.name,
    AccountModule.name,
    WorkflowModule.name,
    TaskModule.name,
    ToolModule.name,

    NavigationModule.name,
    PanelModule.name,
    UniverseDirectives.name,
    AuthModule.name
])
.config(function($urlRouterProvider, $locationProvider, $stateProvider) {
    // $urlRouterProvider.otherwise('/');

    // $locationProvider.html5Mode(true);

    $stateProvider
      .state('test', {
        url: "/test",
        template: "This is a test"
      });
})
.run(function($rootScope, Auth) {
    $rootScope.Auth = Auth;
});

layout.js

import angular from "angular";

// @ngInject
function routes($stateProvider, Self) {
    $stateProvider.state("layout", {
        url: "",
        abstract: true,
        templateUrl: "/app/layout/layout.html",
        resolve: {
            self: function() {
                var self = Self({});
                return self;
            }
        }
    });
}

//export default 
module.exports = angular.module("layout", ["auth"]).config(routes);

auth.module.js

'use strict';

var AuthService = require("./auth.service");
var UserResource = require("./user.service");
var SelfResource = require("./self.service");
var AuthInterceptor = require("./interceptor.service");
var UtilModule = require("../util/util.module");

module.exports = angular.module('auth', [
  'ui.router',
  UtilModule.name
])
  .factory('Auth', AuthService)
  .factory('User', UserResource)
  .factory('Self', SelfResource)
  .factory('AuthInterceptor', AuthInterceptor)
  .config(function($httpProvider) {
    $httpProvider.interceptors.push('AuthInterceptor');
  })
  .run(function($rootScope, $state, Auth) {
      // This is router decorator, it redirects users, lacking priviledges, to login page

      // Redirect to login if route requires auth and the user is not logged in, or doesn't have required role
      $rootScope.$on('$stateChangeStart', function(event, next) {    
          if (!next.authenticate) {
            return;
          }

          if (typeof next.authenticate === 'string') {
              Auth.hasRole(next.authenticate, _.noop).then(has => {
                  if(has) {
                      return;
                  }

                  event.preventDefault();
                  return Auth.isLoggedIn(_.noop).then(is => {
                       $state.go(is ? 'main' : 'login');
                  });
              });
          }
          else {
              Auth.isLoggedIn(_.noop).then(is => {
                  if(is) {
                      return;
                  }

                  event.preventDefault();
                  $state.go('main');
              });
          }
      });    
  });    

self.service.js

'use strict';

// @ngInject
function SelfResource($resource) {
    return $resource('/api/self/',
        {},
        {
            get: {
                method: 'GET'
            }
        },
        {
            stripTrailingSlashed: false
        }
    );
}

module.exports = SelfResource;

So, everything should work: I imported SelfResource in auth.module.js and declared it as a factory. I also declared auth module, defined in auth.module.js, as a dependency for layout module.

Webpack compiles without errors, all the modules are present in the output bundle. Why error?


UPDATE: I added the following lines to the beginning of layout.module.js:

var authModule = require("../../components/auth/auth.module");
console.log(authModule);

that gave me the contents of auth module:

Object {_invokeQueue: Array[4], _configBlocks: Array[1], _runBlocks: Array[1], requires: Array[2], name: "auth"}

Its _invokeQueue contains the Self factory. So, I guess it's a strange problem on the receiving side.

Boris Burkov
  • 13,420
  • 17
  • 74
  • 109
  • You can not inject services/factories to config. Only providers. Just inject service to resolve: http://stackoverflow.com/questions/15937267/inject-service-in-app-config – Petr Averyanov Mar 28 '16 at 15:56
  • @PetrAveryanov Thank you again, Petr, you helped me out. – Boris Burkov Mar 28 '16 at 16:07
  • @PetrAveryanov on a second thought, what a lame design decision. `$resource` and `resolve()` were meant to be used with each other and now I have to invent a crutch to use them together. And in fact, it is possible to use them together, but artificially prohibited. – Boris Burkov Mar 28 '16 at 16:38

1 Answers1

0

you have a Self undefined here? If you mean SelfResource, make sure it's imported and injected correct.

import angular from "angular";

// @ngInject
function routes($stateProvider, Self) {
    $stateProvider.state("layout", {
        url: "",
        abstract: true,
        templateUrl: "/app/layout/layout.html",
        resolve: {
            self: function() {
                var self = Self({});
                return self;
            }
        }
    });
}

//export default 
module.exports = angular.module("layout", ["auth"]).config(routes);
z.a.
  • 2,549
  • 3
  • 12
  • 16
  • Yes, Angular can't find it in `layout` modules, but it is at least properly exported from `self.service.js`. I guess, I called `.factory()` correctly. So, source of error is unclear. – Boris Burkov Mar 28 '16 at 15:42