Using RequireJS with TypeScript and AngularJS

2019-02-04 16:09发布

问题:

I'm about to rip my brains out trying to define an Angular module in TypeScript while using RequireJS. Have looked all over for a solution while spending days trying to figure it all out to no avail. Please help, I'm so confused!

I'm not using Visual Studio, but rather WebStorm. This shouldn't really matter however. I'm using TS 9.1, AngularJS 1.0.7, RequireJS 2.1.8. I've also tried using DefinitelyTyped's angular.d.td, but it's not helping.

Does anyone have a simple example of a TS angular app module for app init, with another TS angular module that has a controller, being loaded with RequireJS, referencing "DefinitelyTyped/angularjs/angular.d.ts", and calling 'angular.module' to define themselves, and then referencing the controller in a web page?

Please, help.... I'm burning in TypeScript, RequireJS, AngularJS module hell.

回答1:

I was able to use these 3 technologies together. I added angular.d.ts and other files from DefinitelyTyped to my Visual Studio project but I also needed to add module declarations using declare module statements. That's because angular definitions from DefinitelyTyped are written for usage without AMD/requirejs. Maybe it is better to use jquery and angular without AMD (load it using <script> tag) and use AMD only for application modules but anyway here is example extracted from my project:

index.html

<script src="webjars/requirejs/2.1.11/require.js" data-main="js/requireMain"></script>

requireMain.ts

Main file for requirejs. It is TypeScript file but doesn't use import syntax and rather uses usual requirejs syntax. It also declares angular modules.

require.config({
    baseUrl: '../js',
    paths: {
        'jquery': '../webjars/jquery/1.11.0/jquery',
        'angular': '../webjars/angularjs/1.2.16/angular',
        'angular-route': '../webjars/angularjs/1.2.16/angular-route',
        'angular-resource': '../webjars/angularjs/1.2.16/angular-resource',
        'angular-ui-bootstrap': '../webjars/angular-ui-bootstrap/0.10.0/ui-bootstrap-tpls',
    },
    shim: {
        'jquery': { exports: 'jquery' },
        'angular': { exports: 'angular', dep: ['jquery'] },
        'angular-route': { exports: 'angular-route', deps: ['angular'] },
        'angular-resource': { exports: 'angular-resource', deps: ['angular'] },
        'angular-ui-bootstrap': { exports: 'angular-ui-bootstrap', deps: ['angular'] },
    },
});

// TypeScript declarations useful for importing angular modules
declare module 'angular' {
    var angular: ng.IAngularStatic;
    export = angular;
}
declare module 'angular-route' {
}
declare module 'angular-resource' {
}
declare module 'angular-ui-bootstrap' {
}

require(['jquery', 'angular', 'angular-route', 'angular-resource', 'angular-ui-bootstrap', 'bootstrap', 
    'application', 'routes'],
    function ($: JQueryStatic, angular: ng.IAngularStatic, angularRoute, angularResource, angularUiBootstrap,
        application, routes) {
        $(function () {
            angular.bootstrap(document, ['application']);
        });
    });

application.ts

import angular = require('angular');
import angularRoute = require('angular-route');
import angularResource = require('angular-resource');
import angularUiBootstrap = require('angular-ui-bootstrap');

var application = angular.module('application', ['ngRoute', 'ngResource', 'ui.bootstrap']);
export = application

routes.ts

import application = require('application');
import myModule = require('myModule');

application.config(function ($routeProvider) {
    $routeProvider.
        when('/myPage', { controller: myModule.MyPageCtrl, templateUrl: 'partials/myPage.html' }).
        otherwise({ redirectTo: '/myPage' });
});

myModule.ts

import application = require('application');
import angularUiBootstrap = require('angular-ui-bootstrap');
import myService = require('myService');

export interface MyPageCtrlScope {
    someData: string;
    someAction: () => void;
}

export class MyPageCtrl {

    constructor(public $scope: MyPageCtrlScope, private PersonService: myService.PersonResourceClass, private $modal: ng.ui.bootstrap.IModalService) {

        PersonService.additionalAction({}).$promise.then(
            (person) => {
                this.$scope.someData = person.email;
            });

        $scope.someAction = this.someAction.bind(this);
    }

    someAction() {
        this.$modal.open({
            templateUrl: 'dialog.html'
        }).result.then(
            () => {
                this.$scope.someData = 'something else';
            });
    }

}

myService.ts

import application = require('application');
import angularResource = require('angular-resource');

export interface Person {
    id?: number;
    email?: string;
}

export interface PersonResource extends Person, ng.resource.IResource<PersonResource> {
}

export interface PersonResourceClass extends ng.resource.IResourceClass<PersonResource> {
    additionalAction(person: Person): PersonResource;
}

application.factory('PersonService', function ($resource: ng.resource.IResourceService, apiUrl: string): PersonResourceClass {
    return <PersonResourceClass> $resource(apiUrl + '/person/:id', { id: "@id" }, {
        'additionalAction': { method: 'POST', url: apiUrl + '/person/:id/additionalAction' },
    });
});


回答2:

There are no stoppers in this scenario that I am aware of, we are using in a large app internally. Publicly I have this small sample : https://github.com/basarat/TypeScriptDeepDive that uses all three.

To help you debug I am sure there is some non TS issue you are having with RequireJS + Angular. The simplest way to narrow it down is to define the following variables:

declare var angular:any;
declare var define:any; 
declare var require:any; 

This will allow you to use straight up TypeScript as though it was javascript, at least for requirejs/angularjs.

Then you can sort out your issues and pull in the type definitions for angular / requirejs.