0

I'm using AngularJS (v1.4.5) with Typescript. I'm setting up a sample app to make sure things are working right before I start using Angular in anger (er, that's hard to say).

I've been basing my design a little on this Scott Logic blog post.

I started off with my controller as a function in the app modules(...).controller declaration, and it worked fine.

module Sample {
    var app = angular.module("beerApp", [])
        .controller("beerController", ($scope) => {
            $scope.beers = [
                "Corona",
                "Carlsberg",
                "Cronenberg"
            ];
        });
}

But I decided to try and split out the files and do it properly, however I'm now getting the error in the title. "Error: [ng:areq] Argument 'beerController' is not a function, got undefined

Here's the app I have.

index.cshtml

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Beer list</title>
    <!-- styles:css -->
    <link rel="stylesheet" href="/.build/css/appStyles.css">
    <!-- endinject -->
    <!-- vendors:js -->
    <script src="/.build/vendors/vendors.js"></script>
    <!-- endinject -->
    <!-- app:js -->
    <script src="/.build/spa/app.js"></script>
    <!-- endinject -->
</head>
<body>
    <main ng-app="beerApp">
        <div ng-controller="beerController">
            <p>You can select from any of the following beers:</p>
            <ul>
                <li ng-repeat="beer in beers">
                    {{beer}}
                </li>
            </ul>
        </div>
    </main>
</body>
</html>

app.ts (compiled into app.js by gulp)

module Sample {
    var app = angular.module("beerApp", [])
        .controller('beerController', BeerController);
}

BeerController.ts (also compiled into app.js by gulp)

module Sample {
    export interface IBeerScope extends ng.IScope {
        beers: string[];
    }

    export class BeerController {
        // needed so that uglify doesn't minify the special $scope variable name away
        public static $inject = ['$scope'];

        constructor(private $scope: IBeerScope) {
            $scope.beers = [
                "Corona",
                "Carlsberg",
                "Cronenberg"
            ];
        }
    }
}

In desperation, I also tried commenting out BeerController.ts and copy+pasting it into the bottom of app.ts to see if that remedied it, to see if it was a file inclusion issue, but that didn't seem to help.

The "not a function" thing makes me wonder if it's something up with my Typescript, but I'm not sure. The blog post I was reading seemed to just pass in a type as the second argument to controller, and the angular.d.ts seems to indicate that's okay, even though it doesn't actually state a type can be passed as the 2nd parameter (I'm using the angular typings available on the DefinitelyTyped repository grabbed through the node module Typings).

I know this question has been asked a lot in AngularJS, and I've read a fair few of them to make sure the answer isn't already out there. But I've checked for all the common things and it isn't those. Must be something eluding me.

The questions I've checked already for the answer are:

Any help gratefully appreciated on this.

Community
  • 1
  • 1
Craig Brett
  • 2,123
  • 20
  • 24
  • it kind of looks like the BeerController is not compiled properly and is missing at runtime, what are your tsconfig options? – toskv Mar 06 '16 at 13:23
  • Believe it or not, I'm not using a tsconfig, just cheating and using VS's default build of Typescript files. Is that not a good idea? – Craig Brett Mar 06 '16 at 13:59

1 Answers1

3

Ok, so your problem is the way your files get loaded.

Move your controller registration with Angular down after the definition of your class and it will work just fine.

class BeerController {
   ...
}

angular.module("beerApp", []).controller("beerController", BeerController);

If you have these definitions in seperate files I suggest you move the module definition to single file and only register your controller in the controller file as below.

app.js

angular.module("beerApp", []);

beerController.ts

class BeerController {
   ...
}

angular.module("beerApp").controller("beerController", BeerController);

See this running JSFiddle

Bjørn Sørensen
  • 861
  • 1
  • 5
  • 14
  • You nailed it! Thanks. I wasn't even on the right lines. I actually changed it slightly, so that in app.ts I have: export var beerApp = angular.module("beerApp", []); And I then in the controller file add the controller to the exported beerApp object, to stop myself getting the name wrong. Is that a sensible improvement or am I setting up problems for later with file ordering again? – Craig Brett Mar 06 '16 at 14:15
  • I wouldn't export the app variable. There is no need to keep it in memory. Just fetch it the few times you need it. This does not pollute the global namespace as well. – Bjørn Sørensen Mar 06 '16 at 14:23
  • Fair enough, I'll leave it as was just in case. Thanks again. – Craig Brett Mar 06 '16 at 14:36