【问题标题】:Requirejs + (multi)angular + typescriptRequirejs +(多)角度+打字稿
【发布时间】:2016-02-03 15:40:56
【问题描述】:

我似乎碰到了一堵我无法打破的墙。我正在使用 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

内容:

索引.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)
        ;
});

然而,这似乎打破了旧的错误: 未捕获的错误:[$injector:nomod] 模块“MyApp”不可用!您要么拼错了模块名称,要么忘记加载它。

问题 1

我在这里做错了什么?在引导我的应用之前,如何确保 angular.module() 调用已完成?

这是console.logs的输出,在这里你可以看到angular还没有定义模块angular.module("MyApp"),所以很明显这是很晚才完成的:

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

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

问题 2

有没有办法传递自定义配置,例如 Libs 所在的目录名称?我尝试了以下似乎不起作用的方法(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 被加载。因此,角度模块尚未定义,并且无法引导。似乎您不能像在 App1/Main.ts 中那样进行嵌套定义?我应该如何配置?

【问题讨论】:

  • 我怀疑require.config() 的多层调用能否按预期工作。 (附带问题:是吗?)但是由于data-directory/data-app-name 是在服务器端生成的,我建议您也动态生成require.config() 调用。您甚至可能希望在服务器端将适当的片段直接动态地包含到 Index.html 中。从那里,您将需要调整 AMD 模块中的路径。还有一个旁注:你为什么不使用 Typescript 对 AMD 的原生支持,即 import Foo = require('Foo') 语法?
  • 我在 angular-app 级别(在 App.ts 和所有其他 angular 组件中)做 import 的东西。如果我使用打字稿,在任何地方都使用导入语法会更好吗?我认为它不适用于非 AMD 第三方库?
  • 是的,require.config() 的多层似乎工作:)
  • 您能否将 cmets 添加到已解决的依赖项中(这样我们就不必回读配置),绘制包含预期/需要/期望状态的依赖关系图或通过电子邮件将项目发送给我(A)xD
  • 我开始觉得导入可能会被阻塞,我没有遇到需要控制器的问题(也跳过了插件)。

标签: angular typescript requirejs amd


【解决方案1】:

问题 1

Alexander Beletsky 写了一篇great article 将 requireJS 和 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"
        }
    }
});

在最后添加对下一个模块的调用

requirejs(["app"], function(app) {
    app.init();
});

在 t1 的 main 中,您制作了实际的应用程序并一次加载它。

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"]);
     }

}

【讨论】:

    【解决方案2】:

    与问题1相关:

    在 App1/App.ts 中,函数不应该返回要注入的角度模块吗?

    例如。

    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"])
    • 它依赖于body的执行,而不是返回内容的值..
    猜你喜欢
    • 2013-08-16
    • 2014-06-26
    • 2014-01-23
    • 1970-01-01
    • 1970-01-01
    • 2015-06-23
    • 2015-09-29
    • 2018-11-11
    • 1970-01-01
    相关资源
    最近更新 更多