Requirejs +(多)angular + TypeScript

13

我似乎遇到了一个无法突破的障碍。我正在使用Angular + TypeScript,并希望将其与RequireJS配合使用。有多个定义的Angular应用程序,这些应用程序根据body上的data-app-name属性进行加载。

文件夹结构(简化):

|- Libs
   |- angular
   |- some-plugin
   |- angular-some-plugin // Exposes an angular.module("ngSomePlugin")
   |- require.js
|- Common
   |- Common.ts // Exposes an angular.module("common")
|- App1
   |- Controllers
      |- SomeController.ts
      |- SomeOtherController.ts
   |- Services
      |- SomeService.ts
   |- Main.ts
   |- App.ts
|- App2
   // same as above
|- AppX
   // same as above
|- Index.html
|- Main.ts

Contents:

Index.html:

// All these attributes are set dynamically server-side
<body id="ng-app-wrapper" data-directory="App1" data-app-name="MyApp">
    <script src="Libs/require.js" data-main="Main"></script>
</body>

Main.ts:

console.log("Step 1: Main.js");

requirejs.config({
    paths: {
        "angular": "Libs/angular/angular",
        "common": "Common/common"
    },
    shim: {
        "angular": {
            exports: "angular"
        }
    }
});

require(["angular"], (angular: angular.IAngularStatic) => {
    angular.element(document).ready(function() {

        var $app = angular.element(document.getElementById("ng-app-wrapper"));
        var directory = $app.data("directory");
        var appName = $app.data("app-name");

        requirejs.config({
            paths: {
                "appMain": directory + "/Main"
            }
        });

        require([
            'common',
            'appMain'
        ], function () {
            console.log("Step 5: App1/Main.js loaded");
            console.log("Step 6: Bootstrapping app: " + appName);
            angular.bootstrap($app, [appName]);
        });
    });
});

在App1中的Main.ts:

console.log("Step 2: App1/Main.js");

requirejs.config({
    paths: {
        "app": "App1/App",
        "somePlugin": "Libs/some-plugin/some-plugin", // This is an AMD module
        "ngSomePlugin": "Libs/angular-some-plugin/angular-some-plugin"
    },
    shim: {
        "ngSomePlugin": {
            exports: "ngSomePlugin",
            deps: ["somePlugin"]
        }
    }
});

define([
    "app"
], () => {
    console.log("Step 4: App.js loaded");
});

App1/App.ts:

console.log("Step 3: App.js");

import SomeController = require("App1/Controllers/SomeController");
import SomeOtherController = require("App1/Controllers/SomeOtherController");
import SomeService = require("App1/Services/SomeService");

define([
    "angular",
    "ngSomePlugin"
], (angular: angular.IAngularStatic) => {

    // This isn't called, so obviously executed to late
    console.log("Defining angular module MyApp");

    angular.module("MyApp", ["common", "ngSomePlugin"])
        .controller("someCtrl", SomeController.SomeController)
        .controller("someOtherCtrl", SomeOtherController.SomeOtherController)
        .service("someService", SomeService.SomeService)
        ;
});

然而,出现了一个老问题:Uncaught Error: [$injector:nomod] 模块'MyApp'不可用!您可能拼写错误或忘记加载它。

问题1

我做错了什么?如何确保在启动应用程序之前完成angular.module()调用?下面是控制台日志的输出结果,您可以看到angular尚未定义模块angular.module("MyApp"),所以这显然太晚了:Console log

更新 我可以取消App.ts中的angular调用,因此它不需要任何东西(除了顶部的导入)。然后,如果我将App添加到App1 / Main.ts的shim中,并在那里设置依赖项,似乎可以解决这个问题。这是解决这个问题的好方法吗?

更新2 如果我在App.ts中使用require而不是define,则确实会实例化angular模块,但仍然是在尝试引导它之后。

问题2

是否有任何方法传递自定义配置,例如库所在的目录名称?我尝试了以下内容,但似乎没有起作用(Main.ts):

requirejs.config({
    paths: {
        "appMain": directory + "/Main"
    },
    config: {
        "appMain": {
            libsPath: "Libs/"
        },
        "app": {
            name: appName
        }
    }
});

App1/Main.ts:

define(["module"], (module) => {
    var libsPath = module.config().libsPath;
    requirejs.config({
        paths: {
            "somePlugin": libsPath + "somePlugin/somePlugin"
            // rest of paths
        }
    });

    define([ // or require([])
        "app"
    ], () => {});
});

App.ts:

define([
    "module"
    // others
], (module) => {
    angular.module(module.config().name, []);
});

但是这种方式,逻辑上,angular.bootstrap() 不会等待 App.ts 加载完成。因此,angular 模块尚未定义,导致引导失败。似乎不能像 App1/Main.ts 中那样进行嵌套定义?我该如何配置?


我怀疑require.config()多层调用会按预期工作(附带问题:它们能吗?)但由于data-directory/data-app-name 是在服务器端生成的,建议您也动态生成 require.config() 调用。甚至可以动态地将相应片段直接包含到Index.html中。从那里开始,您需要调整AMD模块中的路径。另外一点:为什么不使用Typescript对AMD的本机支持,即import Foo = require('Foo') 语法? - Nikos Paraskevopoulos
我在angular-app级别上(在App.ts和所有其他angular组件中)进行“import”操作。如果我使用typescript,到处使用import语法是否更好?我认为它不会在非AMD第三方库上起作用? - devqon
是的,多层的require.config()似乎有效 :) - devqon
你能否在已解决的依赖项中添加注释(这样我们就不必回读配置文件),绘制包括所需/需要/期望状态的依赖关系图或将项目发送给我(A)xD。 - EricG
我开始觉得导入可能会阻塞,我没有要求控制器的问题(也跳过了插件)。 - EricG
2个回答

1

问题 1

Alexander Beletsky 写了一篇很棒的文章,讲述了如何将 requireJS 和 Angular 联合使用(以及为什么值得这样做)。在其中,我认为他给出了第一个问题的答案:从另一个模块加载 Angular,然后进行引导调用。这强制 requireJS 在执行任何代码之前加载那些模块的依赖项。

假设这是你的 main.ts

console.log("Step 1: Main.js");

requirejs.config({
    paths: {
        "angular": "Libs/angular/angular",
        "common": "Common/common".
        "mainT1": "t1/main.js"
    },
    shim: {
        "angular": {
            exports: "angular"
        }
    }
});

在最后添加一个调用您的下一个模块的HTML标记。
requirejs(["app"], function(app) {
    app.init();
});

在t1的主函数中,您可以制作实际应用程序并使其一次性加载。

t1/main.ts

define("app-name", ['angular'], function(angular){

    var loader = {};

    loader.load = function(){

         var app = angular.module("app-name",[]);

         return app;
         }

 return loader;

}

最后,假设你有一个暂存文件,在这里它会引导你到名为app.js的文件。在这里,你需要设置顺序来获取一个已经完成了Angular加载顺序的对象。一旦完成,就在init()函数中调用引导程序。

t1/app.js

define("app", function(require){
     var angular = require('angular');
     var appLoader = require('mainT1');

     var app = {}
     app.init = function(){
           var loader = new appLoader();
           loader.load();
           angular.bootstrap(document, ["app-name"]);
     }

}

-1

关于问题1的相关内容:

在App1/App.ts中,函数不应该返回angular模块以便进行注入吗?

例如:

define([
    "angular",
    "ngSomePlugin"
], (angular: angular.IAngularStatic) => {
    return angular.module("MyApp", ["common", "ngSomePlugin"])
        .controller("someCtrl", SomeController.SomeController)
        .controller("someOtherCtrl", SomeOtherController.SomeOtherController)
        .service("someService", SomeService.SomeService)
        ;
});

我并不真正需要这个模块本身,我只需要确保它已经实例化,这样我就可以通过名称启动我的应用程序,即:angular.bootstrap($app, ["MyApp"]) - devqon
它取决于主体的执行,而不是返回内容的值。 - EricG

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