使用RequireJS与TypeScript和AngularJS

12

我试图在使用RequireJS的同时,用TypeScript定义Angular模块时遇到了困难。 花费了几天时间寻找解决方案,但一无所获。我很困惑,请帮帮我!

我不是使用Visual Studio,而是WebStorm。然而这并不应该有什么影响。 我使用的是TS 9.1、AngularJS 1.0.7、RequireJS 2.1.8。 我也尝试过使用DefinitelyTyped的angular.d.td,但它没有帮助。

请问有没有一个简单的例子,可以通过使用“DefinitelyTyped/angularjs/angular.d.ts”引用,在web页面中调用'angular.module'来定义自身的TS angular app module(用于app init),以及另一个具有控制器的TS angular模块(module),并使用RequireJS进行加载,并引用控制器?

求救啊……我被TypeScript、RequireJS、AngularJS模块地狱困扰着。


就个人而言,我不再看到在应用程序内部使用requirejs的任何好处。我很快会制作一个关于我的当前工作流程的视频教程。但是如果您了解grunt,可以查看:https://github.com/basarat/video-angular-typescript,其中使用了https://npmjs.org/package/grunt-ts。 - basarat
我不使用Angular,但我使用带有自己版本的RequireJS的Dojo和TypeScript。尝试了几次才弄清楚。一些细节有点棘手,特别是因为TypeScript默认使用CommonJS风格的模块,这与RequireJS不兼容。您可以查看我的https://github.com/schungx/Dojo-TypeScript以获取一些灵感。我有一种手册详细介绍如何在Dojo中使用TypeScript,您应该从中获得一些想法。 - Stephen Chung
2个回答

22

我能够将这三种技术结合在一起使用。我向我的Visual Studio项目中添加了angular.d.ts和来自DefinitelyTyped的其他文件,但我还需要使用declare module语句添加模块声明。这是因为DefinitelyTyped中的Angular定义是针对不使用AMD/requirejs的情况编写的。也许最好是不使用AMD(使用<script>标签加载)来使用jquery和angular,并仅将AMD用于应用程序模块,但无论如何,这是从我的项目中提取的示例:

index.html

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

requireMain.ts

这是用于requirejs的主文件。虽然它是TypeScript文件,但不使用import语法,而是使用通常的requirejs语法。它还声明了angular模块。

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' },
    });
});

非常好。谢谢! - Daniel Shin
谢谢!我真的很喜欢你的解决方案结构,但是在application.js中出现了这个错误 - Juan Stoppa
你有一个包含整个解决方案的git仓库吗?我可以看一下吗? - Juan Stoppa
很抱歉,@Juancho,我没有使用这些技术的公共项目。 - Vojta
这是一个比较老的回答,我认为今天应该可以直接使用Angular定义(在requireMain.ts中不需要“declare module”语句)。 - Vojta

3
我所知道的情况下,这种情况并没有任何阻碍,我们正在内部使用一个大型应用程序。在公开场合,我有这个小样本:https://github.com/basarat/TypeScriptDeepDive,它使用了所有三个。
为了帮助您进行调试,我相信您遇到了RequireJS + Angular的一些非TS问题。缩小问题范围的最简单方法是定义以下变量:
declare var angular:any;
declare var define:any; 
declare var require:any; 

这将使您能够像使用JavaScript一样使用TypeScript,至少对于requirejs/angularjs而言。

然后,您可以解决问题,并引入Angular/RequireJS的类型定义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接