Forgive me for the wall of code that follows, but there's only one small change per block and each change is commented.
I'm trying to use the Angular+Typescript "controller as" template that came with the Sidewaffle template pack. This is what the template looks like out of the box. The only modifications I've made to the template here are comments, whitespace, and renaming 'app1' to 'app':
interface ItestControllerScope extends ng.IScope {
vm: testController;
}
interface ItestController {
greeting: string;
controllerId: string; //This won't work...
changeGreeting: () => void;
}
class testController implements ItestController {
static controllerId: string = "testController"; //...because this is static.
greeting = "Hello";
constructor(private $scope: ItestControllerScope, private $http: ng.IHttpService, private $resource: ng.resource.IResourceService) {
}
changeGreeting() {
this.greeting = "Bye";
}
}
app.controller(testController.controllerId,
['$scope', '$http', '$resource', ($scope, $http, $resource) =>
new testController($scope, $http, $resource)
]);
The first thing to note is that it won't even compile due to the static controllerId
member on the controller class and the controllerId
member that is required by the Icontroller
interface. Since the members of the interface need to be implemented in the instance side of the class's type, this won't work.
That's annoying, but easy to get around, though we lose some of our type checking by doing so:
interface ItestControllerScope extends ng.IScope {
vm: testController;
}
interface ItestController {
greeting: string;
changeGreeting: () => void;
}
class testController implements ItestController {
//we leave the static member on the class and remove the member
//from the interface
static controllerId: string = "testController";
greeting = "Hello";
constructor(private $scope: ItestControllerScope, private $http: ng.IHttpService, private $resource: ng.resource.IResourceService) {
}
changeGreeting() {
this.greeting = "Bye";
}
}
app.controller(testController.controllerId,
['$scope', '$http', '$resource', ($scope, $http, $resource) =>
new testController($scope, $http, $resource)
]);
Now this compiles, but the problem is how the call to app.controller()
gets translated to javascript. Instead of passing the constructor function to app.controller()
directly, it's wrapped in an anonymous function and what we end up with is a constructor within a constructor:
var testController = (function () {
function testController($scope, $http, $resource) {
this.$scope = $scope;
this.$http = $http;
this.$resource = $resource;
this.greeting = "Hello";
}
testController.prototype.changeGreeting = function () {
this.greeting = "Bye";
};
testController.controllerId = "testController";
return testController;
})();
app.controller(testController.controllerId,
['$scope', '$http', '$resource',
//Why won't this work? Why would we want to do this in the first place?
function ($scope, $http, $resource) {
return new testController($scope, $http, $resource);
}
]);
Now when we try to use the "controller as" syntax in a view, Angular can't find the aliased controller- the view binds to an empty object.
The best I can tell, the Typescript template should look like this:
interface ItestControllerScope extends ng.IScope {
vm: testController;
}
interface ItestController {
greeting: string;
changeGreeting: () => void;
}
class testController implements ItestController {
static controllerId: string = "testController";
greeting = "Hello";
constructor(private $scope: ItestControllerScope, private $http: ng.IHttpService, private $resource: ng.resource.IResourceService) {
}
changeGreeting() {
this.greeting = "Bye";
}
}
//Now we're passing the controller constructor directly instead of
//wrapping the constructor call in another constructor
app.controller(testController.controllerId,
['$scope', '$http', '$resource',testController]);
Which compiles into this javascript:
var testController = (function () {
function testController($scope, $http, $resource) {
this.$scope = $scope;
this.$http = $http;
this.$resource = $resource;
this.greeting = "Hello";
}
testController.prototype.changeGreeting = function () {
this.greeting = "Bye";
};
testController.controllerId = "testController";
return testController;
})();
app.controller(testController.controllerId,
['$scope', '$http', '$resource', testController]);
which works fine. So I have two main questions:
controller()
method instead of handing over the constructor directly?My only guess is that both of these problems were not problems in some earlier combination of versions of Typescript and Angular, but I wouldn't know as I'm fairly new to both. I'm using Typescript v1.4 and Angular v1.3.14
Looking through the source code on Github reveals that the template in question has been "updated to be Typescript 1.0 and Angular 1.3 compatible," so with some combination of previous versions of both Angular and Typescript all the stuff in the template must have worked at some time in the past.
The two questions I had are addressed in the updated files, and the changes are similar to those that I made to get things running. The Sidewaffle template pack just hasn't been updated accordingly yet.
Update: As of this writing the latest version of Sidewaffle templates include fixed templates.