3

Am using the following template to configure a AngularJS/Typescript web app and receiving the following error.

The following error is appearing when I run the application:
0x800a139e - JavaScript runtime error: [$injector:modulerr] Failed to instantiate module app due to:

Error: [$injector:nomod] Module 'app' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.

I have checked this thread and ensured that I do indeed have a reference to angular-route.js

app.ts - Easier version to view + index.html

  /// <reference path="./typings/angularjs/angular.d.ts" />
'use strict';

// Create and register modules
var modules = ['app.controllers','app.directives', 'app.filters', 'app.services'];
modules.forEach((module) => angular.module(module, []));
angular.module('app', modules);

// Url routing
angular.module('app').config(['$routeProvider',
    function routes($routeProvider: ng.IRouteProvider) {
        $routeProvider
            .when('/', {
                templateUrl: 'views/MyView.html',
                controller: 'app.controllers.MyController'
            })
            .otherwise({
                redirectTo: '/'
            });
    }
]);

module app {
    export module controllers {}
    export module directives {}
    export module filters {}
    export module services {}

    export interface IController {}
    export interface IDirective {
        restrict: string;
        link($scope: ng.IScope, element: JQuery, attrs: ng.IAttributes): any;
    }
    export interface IFilter {
        filter (input: any, ...args: any[]): any;
    }
    export interface IService {}

    /**
     * Register new controller.
     *
     * @param className
     * @param services
     */
    export function registerController (className: string, services = []) {
        var controller = 'app.controllers.' + className;
        services.push(app.controllers[className]);
        angular.module('app.controllers').controller(controller, services);
    }

    /**
     * Register new filter.
     *
     * @param className
     * @param services
     */
    export function registerFilter (className: string, services = []) {
        var filter = className.toLowerCase();
        services.push(() => (new app.filters[className]()).filter);
        angular.module('app.filters').filter(filter, services);
    }

    /**
     * Register new directive.
     *
     * @param className
     * @param services
     */
    export function registerDirective (className: string, services = []) {
        var directive = className[0].toLowerCase() + className.slice(1);
        services.push(() => new app.directives[className]());
        angular.module('app.directives').directive(directive, services);
    }

    /**
     * Register new service.
     *
     * @param className
     * @param services
     */
    export function registerService (className: string, services = []) {
        var service = className[0].toLowerCase() + className.slice(1);
        services.push(() => new app.services[className]());
        angular.module('app.services').factory(service, services);
    }
}

Am a bit of a TS/Angular newbie so any help would be greatly appreciated.

Thanks

Potential Duplicate: AngularJS + TypeScript: cannot inject $routeProvider

Community
  • 1
  • 1
freschx
  • 94
  • 1
  • 1
  • 11

2 Answers2

14

You need to make a few changes to that template to get it to work.

Full source here

First ensure you have the correct references. Including a reference to angular-route.d.ts

/// <reference path="./typings/angularjs/angular.d.ts" />
/// <reference path="./typings/angularjs/angular-route.d.ts" />

'use strict';

// Create and register modules
var modules = ['app.controllers','app.directives', 'app.filters', 'app.services'];
modules.forEach((module) => angular.module(module, []));

To use the $routeProvider you must have included the ngRoute module in your modules, but because we don't want to register it with angular, because it already exists there, we need to push the module afterwards like this:

// *** Push ngRoute or $routeProvider won't work ***
modules.push("ngRoute");

angular.module('app', modules);

Then you need to fix the type of $routeProvider from ng.IRouteProvider to ng.route.IRouteProvider as that definition is wrong.

// Url routing
angular.module('app').config(['$routeProvider',
    function routes($routeProvider: ng.route.IRouteProvider) { // *** $routeProvider is typed with ng.route.IRouteProvider ***
        $routeProvider
            .when('/', {
                templateUrl: 'views/MyView.html',
                controller: 'app.controllers.MyController'
            })
            .otherwise({
                redirectTo: '/'
            });
    }
]);

Next TypeScript won't accept empty modules. So having export module controllers {} etc is pointless, because TSC won't include them, and they will be undefined giving errors. Unless you were to define at least one controller, directive, filter and service in your app. But this is solved by simply including null;

module app {
    export module controllers { null; }
    export module directives { null; }
    export module filters { null; }
    export module services { null; }

    export interface IController {}
    export interface IDirective {
        restrict: string;
        link($scope: ng.IScope, element: JQuery, attrs: ng.IAttributes): any;
    }
    export interface IFilter {
        filter (input: any, ...args: any[]): any;
    }
    export interface IService {}

    /**
     * Register new controller.
     *
     * @param className
     * @param services
     */
    export function registerController (className: string, services = []) {
        var controller = 'app.controllers.' + className;
        services.push(app.controllers[className]);
        angular.module('app.controllers').controller(controller, services);
    }

    /**
     * Register new filter.
     *
     * @param className
     * @param services
     */
    export function registerFilter (className: string, services = []) {
        var filter = className.toLowerCase();
        services.push(() => (new app.filters[className]()).filter);
        angular.module('app.filters').filter(filter, services);
    }

    /**
     * Register new directive.
     *
     * @param className
     * @param services
     */
    export function registerDirective (className: string, services = []) {
        var directive = className[0].toLowerCase() + className.slice(1);
        services.push(() => new app.directives[className]());
        angular.module('app.directives').directive(directive, services);
    }

    /**
     * Register new service.
     *
     * @param className
     * @param services
     */
    export function registerService (className: string, services = []) {
        var service = className[0].toLowerCase() + className.slice(1);
        services.push(() => new app.services[className]());
        angular.module('app.services').factory(service, services);
    }
}

That should now work and you can register your controllers, filters, services and directives. Such as:

module app.controllers
{
    export class testCtrl implements IController
    {
        constructor()
        {
            console.log("Test Controller");
        }
    }
}

app.registerController('testCtrl');

When referencing a controller, or service to use the full name. i.e:

<div ng-controller="app.controllers.testCtrl"></div>

Remember in your html to reference angular-route.js after angular.js. i.e:

<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular-route.js"></script>
Scott
  • 21,211
  • 8
  • 65
  • 72
  • Scott your answer managed to solve my main issue so again a big thanks, however I'm now getting a secondary error saying it cannot load the module ngLocale. (http://oi62.tinypic.com/ojkon.jpg) Where (and how) does one go about loading this module in app.ts? I've checked the doco here: http://docs.angularjs.org/error/$injector/nomod but can't quite get my head around it. – freschx Feb 18 '14 at 10:30
  • @freschx You can add further modules to your template by using `modules.push("moduleName");` after the current `modules.push("ngRoute");` line. So in this case `modules.push("ngLocale");`. Hope that helps :) – Scott Feb 19 '14 at 17:11
  • @Scott is there a way to put `module app.controllers` into a different file? I want to split my controller typescript files into a different directory. I tried to split it out, but it is not registering any controllers then. – Agrejus Dec 07 '17 at 22:00
-2

I found my sollution here a link:

It happens when I tried to include a module dependency that did not exist. This happens due to a number of reasons such as removing a script for index.html for a module but leaving in the module dependency or even misspelling a module dependency. (Turns out my issue was the former.) So this was easy to fix once you knew that.

Matt
  • 74,352
  • 26
  • 153
  • 180
Prasath Siva
  • 502
  • 1
  • 4
  • 7