Angularjs 仅在需要时动态注入模块

Angularjs 仅在需要时动态注入模块,angularjs,requirejs,Angularjs,Requirejs,我将Require.js与Angular.js结合使用 一些控制器需要巨大的外部依赖项,而其他控制器则不需要,例如,FirstController需要。这至少是额外的135 kb: require([ "angular", "angular.ui.codemirror" // requires codemirror itself ], function(angular) { angular.module("app", [ ..., "ui.codemirror" ]).control

我将Require.js与Angular.js结合使用

一些控制器需要巨大的外部依赖项,而其他控制器则不需要,例如,
FirstController
需要。这至少是额外的135 kb:

require([
  "angular",
  "angular.ui.codemirror" // requires codemirror itself
], function(angular) {
  angular.module("app", [ ..., "ui.codemirror" ]).controller("FirstController", [ ... ]);
});
我不希望每次加载页面时都必须包含指令和Codemirror库,只是为了让Angular满意。
这就是为什么我现在只在点击路线时加载控制器

然而,当我需要像

define([
  "app",
  "angular.ui.codemirror"
], function(app) {
  // ui-codemirror directive MUST be available to the view of this controller as of now
  app.lazy.controller("FirstController", [
    "$scope",
    function($scope) {
      // ...
    }
  ]);
});
我如何告诉Angular将
ui.codemirr
模块(或任何其他模块)也注入到应用程序模块中?
我不在乎这是否是一种黑客方式,除非它涉及修改外部依赖项的代码。


如果有用的话:我正在运行Angular 1.2.0。

关键是,你的
应用程序
模块所依赖的任何模块也需要是延迟加载模块。这是因为angular用于其$injector服务的提供程序和实例缓存是私有的,并且在初始化完成后,它们不公开注册新模块的方法

因此,“黑客”的方法是编辑您希望延迟加载的每个模块,以要求延迟加载模块对象(在您链接的示例中,该模块位于文件“appModules.js”中),然后编辑每个控制器、指令、工厂等调用,改为使用
app.lazy.{same call}

之后,您可以通过查看应用程序路由是如何延迟加载的(appRoutes.js文件显示了如何执行此操作),继续关注您链接到的示例项目


不太确定这是否有用,但祝你好运。

注意:使用Nikos Paraskevopoulos的解决方案,因为它更可靠(我正在使用它),并且有更多的例子。


好的,我终于找到了如何通过简单的帮助实现这一点

正如我在我的问题中所说,这已经成为一种非常令人讨厌的方式。它包括在应用程序模块的上下文中应用依赖模块的
\u invokeQueue
数组中的每个函数

它是这样的(请在ModuleXtender功能中多加注意):

完成后,例如,我只需要执行以下操作:

app.extend( "ui.codemirror" ); // ui.codemirror module will now be available in my application
app.controller( "FirstController", [ ..., function() { });

一段时间以来,我一直在尝试混合requirejs+Angular。到目前为止,我在Github()中发布了一个小项目,因为范围对于内联代码或fiddle来说太大了。该项目展示了以下几点:

  • AngularJS模块是延迟加载的
  • 指令也可以延迟加载
  • 有一个“模块”发现和元数据机制(请参阅我的另一个宠物项目:)
  • 应用程序自动拆分为捆绑包(即使用r.js works构建)
如何做到:

  • 提供程序(例如,
    $controllerProvider
    $compileProvider
    )是从
    config
    函数(我第一次看到的技术)中捕获的
  • 引导后,
    angular
    被我们自己的包装器所取代,该包装器可以处理延迟加载的模块
  • 喷油器被捕获并作为承诺提供
  • AMD模块可以转换为角度模块
这个实现满足了您的需求:它可以延迟加载角度模块(至少是我使用的ng网格),绝对是黑客的:),并且不修改外部库

欢迎发表评论/意见


(编辑)此解决方案与其他解决方案的区别在于它不进行动态
require()
调用,因此可以使用r.js(以及我的require lazy项目)构建。除此之外,这些想法在不同的解决方案中或多或少是趋同的


祝大家好运

现有的延迟加载技术的问题在于,它们可以完成我想自己做的事情

例如,使用requirejs,我只想调用:

require(['tinymce', function() {
   // here I would like to just have tinymce module loaded and working
});
然而,它不是这样工作的。为什么?据我所知,AngularJS只是将模块标记为“将在将来加载”,例如,如果我稍等片刻,它就会工作——我将能够使用它。所以在上面的函数中,我想调用一些函数,比如loadPendingModules()

在我的项目中,我创建了简单的提供者(“lazyLoad”),它只做这件事,其他什么都不做,所以现在,如果我需要完全加载某个模块,我可以执行以下操作:

myApp.controller('myController', ['$scope', 'lazyLoad', function($scope, lazyLoad) {

    // ........

    $scope.onMyButtonClicked = function() {

        require(['tinymce', function() {
            lazyLoad.loadModules();

            // and here I can work with the modules as they are completely loaded
        }]);
    };

    // ........

});
以下是指向源文件(MPL许可证)的链接:

有一个指令可以做到这一点:

例如:

<div load-on-demand="'module_name'"></div>

我将向您发送示例代码。这对我来说很好。因此,请检查以下内容:

var myapp = angular.module('myapp', ['ngRoute']);

/* Module Creation */
var app = angular.module('app', ['ngRoute']);

app.config(['$routeProvider', '$controllerProvider', function ($routeProvider, $controllerProvider) {

app.register = {
    controller: $controllerProvider.register,
    //directive: $compileProvider.directive,
    //filter: $filterProvider.register,
    //factory: $provide.factory,
    //service: $provide.service
};


//    so I keep a reference from when I ran my module config
function registerController(moduleName, controllerName) {
    // Here I cannot get the controller function directly so I
    // need to loop through the module's _invokeQueue to get it
    var queue = angular.module(moduleName)._invokeQueue;
    for (var i = 0; i < queue.length; i++) {
        var call = queue[i];
        if (call[0] == "$controllerProvider" &&
           call[1] == "register" &&
           call[2][0] == controllerName) {
            app.register.controller(controllerName, call[2][1]);
        }
    }
}


var tt = {
    loadScript:
function (path) {
    var result = $.Deferred(),
    script = document.createElement("script");
    script.async = "async";
    script.type = "text/javascript";
    script.src = path;
    script.onload = script.onreadystatechange = function (_, isAbort) {
        if (!script.readyState || /loaded|complete/.test(script.readyState)) {
            if (isAbort)
                result.reject();
            else {
                result.resolve();
            }
        }
    };
    script.onerror = function () { result.reject(); };
    document.querySelector(".shubham").appendChild(script);
    return result.promise();
}
}

function stripScripts(s) {
    var div = document.querySelector(".shubham");
    div.innerHTML = s;
    var scripts = div.getElementsByTagName('script');
    var i = scripts.length;
    while (i--) {
        scripts[i].parentNode.removeChild(scripts[i]);
    }
    return div.innerHTML;
}


function loader(arrayName) {
    return {
        load: function ($q) {
            stripScripts(''); // This Function Remove javascript from Local
            var deferred = $q.defer(),
            map = arrayName.map(function (obj) {
                return tt.loadScript(obj.path)
                .then(function () {
                    registerController(obj.module, obj.controller);
                })
            });

            $q.all(map).then(function (r) {
                deferred.resolve();
            });
            return deferred.promise;
        }
    };
};



$routeProvider
    .when('/first', {
        templateUrl: '/Views/foo.html',
        resolve: loader([{ controller: 'FirstController', path: '/MyScripts/FirstController.js', module: 'app' },
            { controller: 'SecondController', path: '/MyScripts/SecondController.js', module: 'app' }])
    })

    .when('/second', {
        templateUrl: '/Views/bar.html',
        resolve: loader([{ controller: 'SecondController', path: '/MyScripts/SecondController.js', module: 'app' },
        { controller: 'A', path: '/MyScripts/anotherModuleController.js', module: 'myapp' }])
    })
    .otherwise({
        redirectTo: document.location.pathname
        });
}])
var myapp=angular.module('myapp',['ngRoute']);
/*模块创建*/
var app=angular.module('app',['ngRoute']);
app.config(['$routeProvider','$controllerProvider',函数($routeProvider,$controllerProvider){
app.register={
控制器:$controllerProvider.register,
//指令:$compileProvider.directive,
//过滤器:$filterProvider.register,
//工厂:$provide.factory,
//服务:$provide.service
};
//因此,我在运行模块配置时保留了一个引用
功能寄存器控制器(模块名称、控制器名称){
//在这里,我无法直接获取控制器函数,因此
//需要循环通过模块的_invokeQueue来获取它
var queue=angular.module(moduleName)。\u invokeQueue;
对于(变量i=0;ivar myapp = angular.module('myapp', ['ngRoute']);

/* Module Creation */
var app = angular.module('app', ['ngRoute']);

app.config(['$routeProvider', '$controllerProvider', function ($routeProvider, $controllerProvider) {

app.register = {
    controller: $controllerProvider.register,
    //directive: $compileProvider.directive,
    //filter: $filterProvider.register,
    //factory: $provide.factory,
    //service: $provide.service
};


//    so I keep a reference from when I ran my module config
function registerController(moduleName, controllerName) {
    // Here I cannot get the controller function directly so I
    // need to loop through the module's _invokeQueue to get it
    var queue = angular.module(moduleName)._invokeQueue;
    for (var i = 0; i < queue.length; i++) {
        var call = queue[i];
        if (call[0] == "$controllerProvider" &&
           call[1] == "register" &&
           call[2][0] == controllerName) {
            app.register.controller(controllerName, call[2][1]);
        }
    }
}


var tt = {
    loadScript:
function (path) {
    var result = $.Deferred(),
    script = document.createElement("script");
    script.async = "async";
    script.type = "text/javascript";
    script.src = path;
    script.onload = script.onreadystatechange = function (_, isAbort) {
        if (!script.readyState || /loaded|complete/.test(script.readyState)) {
            if (isAbort)
                result.reject();
            else {
                result.resolve();
            }
        }
    };
    script.onerror = function () { result.reject(); };
    document.querySelector(".shubham").appendChild(script);
    return result.promise();
}
}

function stripScripts(s) {
    var div = document.querySelector(".shubham");
    div.innerHTML = s;
    var scripts = div.getElementsByTagName('script');
    var i = scripts.length;
    while (i--) {
        scripts[i].parentNode.removeChild(scripts[i]);
    }
    return div.innerHTML;
}


function loader(arrayName) {
    return {
        load: function ($q) {
            stripScripts(''); // This Function Remove javascript from Local
            var deferred = $q.defer(),
            map = arrayName.map(function (obj) {
                return tt.loadScript(obj.path)
                .then(function () {
                    registerController(obj.module, obj.controller);
                })
            });

            $q.all(map).then(function (r) {
                deferred.resolve();
            });
            return deferred.promise;
        }
    };
};



$routeProvider
    .when('/first', {
        templateUrl: '/Views/foo.html',
        resolve: loader([{ controller: 'FirstController', path: '/MyScripts/FirstController.js', module: 'app' },
            { controller: 'SecondController', path: '/MyScripts/SecondController.js', module: 'app' }])
    })

    .when('/second', {
        templateUrl: '/Views/bar.html',
        resolve: loader([{ controller: 'SecondController', path: '/MyScripts/SecondController.js', module: 'app' },
        { controller: 'A', path: '/MyScripts/anotherModuleController.js', module: 'myapp' }])
    })
    .otherwise({
        redirectTo: document.location.pathname
        });
}])
<body ng-app="app">

<div class="container example">
    <!--ng-controller="testController"-->

    <h3>Hello</h3>

    <table>
        <tr>
            <td><a href="#/first">First Page </a></td>
            <td><a href="#/second">Second Page</a></td>
        </tr>
    </table>




        <div id="ng-view" class="wrapper_inside" ng-view>
        </div>
    <div class="shubham">
    </div>
</div>