2

I'm trying to wrap my head around how to use Typescript and Angularjs together. I've read several blog posts and both projects' documentation but it isn't sticking.

I have an Angular controller that takes two dependencies, $scope and a service that I wrote called greetingService. I've defined a main app Angular module and attached the controller and service to it. I have log statements in my Typescript constructors so that I can inspect the dependencies in the browser. In the constructors of both my controller and my custom service, the dependencies are injected correctly. However, in the controller, when I try to use a dependency, it is undefined. I think this code snippet demonstrates the issue better than I can explain it.

declare var angular;

module App.Services {
    export class GreetingService {

        constructor(private $http) {
            console.log("In greeting service constructor");
            console.log($http);
        }

        getGreetingsPromise() {
            return this.$http.get('/greetings.json');
        }
    }
}

module App.Controllers {
    export class GreetingController {

        static $inject = ['$scope', 'greetingService'];
        constructor(public $scope, public greetingService: App.Services.GreetingService) {
            this.$scope.greeting = "This will be a greeting.";
            this.$scope.greet = this.greet;

            console.log(greetingService); // this is defined and looks right
        }

        greet(language: string) {
            console.log("Greeting...");
            console.log(this.$scope); // this is undefined
            console.log(this.greetingService); // this is undefined
            var greetingPromise = this.greetingService.getGreetingsPromise();
            greetingPromise.then(data => this.$scope.greeting = data[language]);
        }
    }
}


var mainApp = angular.module('mainApp', []);
mainApp.controller('greetingController', ['$scope', 'greetingService', App.Controllers.GreetingController]);
mainApp.service('greetingService', App.Services.GreetingService);

And here is the Angular template (that correctly displays "This will be a greeting" that is initialized in the constructor).

<div class="jumbotron">
    <div ng-controller="greetingController">
        <p class="lead">{{greeting}}</p>
        <button ng-click="greet('english')">Greet me</button>
    </div>
</div>

1 Answer 1

3

This line:

this.$scope.greet = this.greet

is the problem. You are just copying the function reference to a scope property and when it is called with scope this won't be the controller instance, it is determined by the caller (in your case, this should be the scope itself inside the greet function when it is called from the template) except for the bound functions. A quick fix would be to use function.bind. i.e this.$scope.greet = this.greet.bind(this) with which you create a bound function, function reference bound to the controller instance. You could also use an arrow function and assign it as reference, this will force TS to convert any this to _this cached variable:

   greet = (language: string) => {
        console.log("Greeting...");
        console.log(this.$scope); // this is undefined
        console.log(this.greetingService); // this is undefined
        var greetingPromise = this.greetingService.getGreetingsPromise();
        greetingPromise.then(data => this.$scope.greeting = data[language]);
    }

Ideally if you use controller As (provided your angular supports it, if not highly recommended to upgrade) syntax rather than using scope directly you would not need do all these.

i.e:

export class GreetingController {
    greeting :string;

    static $inject = ['greetingService'];

    constructor(public greetingService: App.Services.GreetingService) {
        this.greeting = "This will be a greeting.";
    }

    greet(language: string) {
        var greetingPromise = this.greetingService.getGreetingsPromise();
        greetingPromise.then(data => this.greeting = data[language]);
    }
}

and

<div class="jumbotron">
    <div ng-controller="greetingController as vm">
        <p class="lead">{{vm.greeting}}</p>
        <button ng-click="vm.greet('english')">Greet me</button>
    </div>
</div>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.