web-dev-qa-db-ja.com

モジュールを動的に挿入します(必要な場合のみ)

Require.jsをAngular.jsと組み合わせて使用​​しています。

たとえば、FirstControllerには Angular UI Codemirror が必要です。それは少なくとも135 kbです。

require([
  "angular",
  "angular.ui.codemirror" // requires codemirror itself
], function(angular) {
  angular.module("app", [ ..., "ui.codemirror" ]).controller("FirstController", [ ... ]);
});

Angular幸せにするために、ページがロードされるたびにディレクティブとCodemirror libを含める必要はありません。
だからこそ、ルートがヒットしたときにのみコントローラーをロードするのです ここで行われていることのように

ただし、次のようなものが必要な場合

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.codemirrorモジュール(または他のモジュール)もappモジュールにありますか?
外部依存関係のコードの変更を伴う場合を除き、これを達成するためのハック的な方法であるかどうかは気にしません。

それが便利な場合:私はAngular 1.2.0。

44
gustavohenke

私は今、しばらくの間requirejs + Angularをミックスしようとしています。スコープがインラインコードやフィドルには大きすぎるため、これまでの努力でGithubに小さなプロジェクト( angular-require-lazy )を公開しました。このプロジェクトは、次の点を示しています。

  • AngularJSモジュールは遅延ロードされます。
  • ディレクティブも遅延ロードできます。
  • 「モジュール」検出とメタデータメカニズムがあります(他のペットプロジェクトを参照してください: require-lazy
  • アプリケーションは自動的にバンドルに分割されます(つまり、r.jsを使用したビルドが機能します)

方法は次のとおりです。

  • プロバイダー(_$controllerProvider_、_$compileProvider_)は、config関数からキャプチャされます(最初に angularjs-requirejs-lazy-controllers )。
  • ブートストラップ後、angularは、遅延ロードされたモジュールを処理できる独自のラッパーに置き換えられます。
  • インジェクターがキャプチャされ、約束として提供されます。
  • AMDモジュールは、Angularモジュールに変換できます。

この実装はあなたのニーズを満たします:lazy-load Angular modules(少なくとも私が使用しているng-grid))、間違いなくハックです:)、外部ライブラリを変更しません。

コメント/意見は大歓迎です。


(編集)このソリューションと他のソリューションの違いは、動的なrequire()呼び出しを行わないため、r.js(およびrequire-lazyプロジェクト)でビルドできることです。それ以外の点では、アイデアはさまざまなソリューションにほぼ収束しています。

皆さんお元気で!

33

注意:Nikos Paraskevopoulosのソリューションを使用してください。信頼性が高く(使用しています)、さらに多くの例があります。


さて、私はついにこれを簡単な助けでこれを達成する方法を見つけました answer

私の質問で言ったように、これは非常にハックな方法になりました。アプリモジュールのコンテキストで、依存モジュールの_invokeQueue配列内の各関数を適用します。

これは次のようなものです(moduleExtender関数にもっと注意を払ってください):

define([ "angular" ], function( angular ) {
    // Returns a angular module, searching for its name, if it's a string
    function get( name ) {
        if ( typeof name === "string" ) {
            return angular.module( name );
        }

        return name;
    };

    var moduleExtender = function( sourceModule ) {
        var modules = Array.prototype.slice.call( arguments );

        // Take sourceModule out of the array
        modules.shift();

        // Parse the source module
        sourceModule = get( sourceModule );
        if ( !sourceModule._amdDecorated ) {
            throw new Error( "Can't extend a module which hasn't been decorated." );
        }

        // Merge all modules into the source module
        modules.forEach(function( module ) {
            module = get( module );
            module._invokeQueue.reverse().forEach(function( call ) {
                // call is in format [ provider, function, args ]
                var provider = sourceModule._lazyProviders[ call[ 0 ] ];

                // Same as for example $controllerProvider.register("Ctrl", function() { ... })
                provider && provider[ call[ 1 ] ].apply( provider, call[ 2 ] );
            });
        });
    };

    var moduleDecorator = function( module ) {
        module = get( module );
        module.extend = moduleExtender.bind( null, module );

        // Add config to decorate with lazy providers
        module.config([
            "$compileProvider",
            "$controllerProvider",
            "$filterProvider",
            "$provide",
            function( $compileProvider, $controllerProvider, $filterProvider, $provide ) {
                module._lazyProviders = {
                    $compileProvider: $compileProvider,
                    $controllerProvider: $controllerProvider,
                    $filterProvider: $filterProvider,
                    $provide: $provide
                };

                module.lazy = {
                    // ...controller, directive, etc, all functions to define something in angular are here, just like the project mentioned in the question
                };
                module._amdDecorated = true;
            }
        ]);
    };

    // Tadaaa, all done!
    return {
        decorate: moduleDecorator
    };
});

これが完了したら、たとえば、これを行うために必要なだけです:

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

これの鍵は、appモジュールが依存するモジュールも遅延読み込みモジュールである必要があることです。これは、angularが$ injectorサービスに使用するプロバイダーおよびインスタンスのキャッシュがプライベートであり、初期化の完了後に新しいモジュールを登録するメソッドを公開しないためです。

したがって、これを行うための「ハッキング」方法は、遅延ロードするモジュールのそれぞれを編集して、遅延ロードモジュールオブジェクトを要求することです(リンクした例では、モジュールは「appModules.js」ファイルにあります)。次に、コントローラー、ディレクティブ、ファクトリーなどの各呼び出しを編集して、app.lazy.{same call}代わりに。

その後、アプリルートがどのように遅延ロードされるかを調べることで、リンクしたサンプルプロジェクトを引き続き追跡できます(「appRoutes.js」ファイルにこれを行う方法が示されています)。

これが役立つかどうかはわかりませんが、幸運を祈ります。

4
Clark Pan

これを行うディレクティブがあります:

https://github.com/AndyGrom/loadOnDemand

例:

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

既存の遅延ロード技術の問題は、私がやりたいことを自分でやることです。

たとえば、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ライセンス): https://github.com/lessmarkup/less-markup/blob/master/LessMarkup/UserInterface/Scripts/Providers/lazyload.js

0
Oleg Gordeev

サンプルコードをお送りします。それは私のためにうまく機能しています。これを確認してください:

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

そして、HTMLページで:

<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>
0
s_kumar