web-dev-qa-db-ja.com

AngularJS:コントローラーとコンテンツの遅延読み込み

この単純化されたシナリオでは、index.htm、lazy.htmの2つのファイルがあります。

index.htm:

var myApp = angular.module('myApp', []);
myApp.controller('embed',function($scope){
    $scope.embed = 'Embedded Controller';
});                  
<div ng-controller="embed">{{embed}}</div>    
<div ng-include="'lazy.htm'"></div>

lazy.htm

myApp.controller('lazy',function($scope){
    $scope.lazy = 'Lazy Controller';
});
<div ng-controller="lazy">
    {{lazy}}
</div>

結果はエラーです:「引数 'lazy'は関数ではなく、未定義になりました」

代わりに関数を使用する

lazy.htm

function lazy($scope) {
    $scope.lazy = 'Lazy Controller';
}
<div ng-controller="lazy">
    {{lazy}}
</div>

これは、バージョン1.3ベータ14まで機能します。ベータ15では、グローバルコントローラー機能が削除されました。 https://github.com/angular/angular.js/issues/8296

それでは、lazy.htmの角度付きコンテンツを動的に取得するより良い方法は何ですか?

UPDATE:

この記事( http://ify.io/lazy-loading-in-angularjs )では、別の解決策を見つけました。 $ controllerProviderを使用すると、angularブートストラップの後に新しいコントローラーを登録できます。チャームのように動作します。v1.3.0-beta.18

index.htm:

var myApp = angular.module('myApp', [])
.controller('embed',function($scope){
    $scope.embed = 'Embedded Controller';
})
.config(function($controllerProvider) {
    myApp.cp = $controllerProvider;
});

<div ng-controller="embed">{{embed}}</div>    
<div ng-include="'lazy.htm'"></div>

lazy.htm

myApp.cp.register('lazy',function($scope){
    $scope.lazy = 'Lazy Controller';
});
<div ng-controller="lazy">
    {{lazy}}
</div>

更新2:

動作する他の2つの選択肢は次のとおりです。

lazy.htm

_app = $('[ng-app]').scope();    
_app.lazy = function($scope) {
    $scope.lazy = 'Lazy Controller';
};

OR

var $rootScope = $('[ng-app]').injector().get('$rootScope');        
$rootScope.lazy = function($scope) {
    $scope.lazy = 'Lazy Controller';
}; 

しかし、これらの最後の2つの例を本番環境で使用しないでください。

Jqueryを使用して$ routeProviderを解決することもできます

app.js

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

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

/*Creating a more synthesized form of service of $ controllerProvider.register*/
app.registerCtrl = $controllerProvider.register;

function loadScript(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("head").appendChild(script);
  return result.promise();
}

function loader(arrayName){

    return {
      load: function($q){
                var deferred = $q.defer(),
                map = arrayName.map(function(name) {
                    return loadScript('js/controllers/'+name+".js");
                });

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

                return deferred.promise;
        }
    };
}

$routeProvider  
    .when('/', {
        templateUrl: 'views/foo.html',
        resolve: loader(['foo'])
    })
    .when('/bar',{
        templateUrl: 'views/bar.html',
        controller: 'BarCtrl',
        resolve: loader(['bar'])
    })
    .otherwise({
        redirectTo: document.location.pathname
    });
}]);

/views/foo.html

<section ng-controller='FooCtrl'>
    {{text}}
</section>

js/controllers/foo.js

/*Here we use the synthesized version of $controllerProvider.register 
to register the controller in view*/
app.registerCtrl('FooCtrl',function($scope){
    $scope.text = 'Test';
});

/views/bar.html

<section>
    {{text2}}
</section>

js/controllers/bar.js

app.registerCtrl('BarCtrl',function($scope){
    $scope.text2 = 'Test';
});
15
André Betiolo

最初はAndréBetioloの答えを利用しました。ただし、ajaxの読み込みが非ブロッキングであるため、スクリプトが読み込まれる前にビューがコントローラーを要求することがあるため、常に機能するとは限りません。

解決策として、すべてのスクリプトが正常にロードされるまで関数が戻らないように強制しました。これは一種のハックですが、解決が完了する前にロードが成功することを確認します。また、複数のコントローラーをロードすることもできます。

app.js

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

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

    /*Creating a more synthesized form of service of $ controllerProvider.register*/
    app.registerCtrl = $controllerProvider.register;

    //jquery to dynamically include controllers as needed
    function controllers(controllers){
        var numLoaded = 0;
        for (i = 0; i < controllers.length; i++) {
            $.ajaxSetup({async:false});
            $.getScript('js/controllers/' + controllers[i] + '.js').success(function(){
                numLoaded++;
                if (numLoaded == controllers.length) {
                    return true; //only return after all scripts are loaded, this is blocking, and will fail if all scripts aren't loaded.
                }
            });
        }
    }

    $routeProvider
        .when('/', {
            templateUrl: 'views/foo.html',
            resolve: {
                load: function () {
                    controllers(['foo'])
                }
            }
        })
        .when('/bar',{
            templateUrl: 'views/bar.html',
            controller: 'BarCtrl',
            resolve: {
                load: function () {
                    controllers(['bar','foo']) //you can load multiple controller files
                }
            }
        })
        .otherwise({
            redirectTo: document.location.pathname
        });
}]);

/views/foo.html

<section ng-controller='FooCtrl'>
    {{text}}
</section>

/views/bar.html

<section ng-controller='BarCtrl'>
    {{text2}}
</section>
<section ng-controller='FooCtrl'>
    {{text}}
</section>

/controllers/bar.js

app.registerCtrl('BarCtrl',function($scope){
    $scope.text2 = 'Test';
});
3
thecrazyrussian

//// JConfigファイル--------

window.angularApp.config(function ($routeProvider,$controllerProvider,$compileProvider,$provide, azMessages) {

$routeProvider.when('/login', {
             resolve: {
                 load: ['$q', '$rootScope', function ($q, $rootScope) {
                     var deferred = $q.defer();
                     require([

                         //load required Js file here

                ], function () {
                    $rootScope.$apply(function () {
                        deferred.resolve();
                    });
                });
                     return deferred.promise;
                 } ]
             }
         });


  $routeProvider.otherwise({ redirectTo: '/login' });

    window.angularApp.components = {
        controller: $controllerProvider.register,
        service: $provide.service,
        directive: $compileProvider.directive
    }

//コントローラーの宣言

angularApp.components.controller('DiscussionController',[function(){

}]);
3
chirag

純粋なAngularJS遅延ロードを使用できます。

「LazyService」を作成します。

var ng = angular.module('app');

ng.factory('lazyService', [ '$http', function($http) {
    var jsPath = 'js/${ name }.js';
    var promisesCache = {};

    return {
        loadScript: function(name) {
            var path = jsPath.replace('${ name }', name);
            var promise = promisesCache[name];
            if (!promise) {
                promise = $http.get(path);
                promisesCache[name] = promise;

                return promise.then(function(result) {
                    eval(result.data);
                    console.info('Loaded: ' + path);
                });
            }

            return promise;
        }
    }
}]);

次に、構成を定義します。

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

ng.config([ '$routeProvider', '$controllerProvider', '$provide', function($routeProvider, $controllerProvider, $provide) {
    // Lazy loading
    ng.lazy = {
        controller: $controllerProvider.register,
        //directive: $compileProvider.directive,
        //filter: $filterProvider.register,
        factory: $provide.factory,
        service: $provide.service
    }

    $routeProvider
    .when('/', {
        templateUrl: 'view/home.html'
    })
    .when('/vendor', {
        templateUrl: 'view/vendor.html',
        resolve: {
            svc: [ 'lazyService', function(lazyService) {
                return lazyService.loadScript('services/vendor');
            }],
            ctrl: [ 'lazyService', function(lazyService) {
                return lazyService.loadScript('controllers/vendor');
            }]
        }
    });
. . .

「js/services/vendor.js」で、次のようにサービスを作成します。

var ng = angular.module('app');
ng.lazy.service('vendorService', [ function() {
. . .

「js/controllers/vendor.js」で、次のようにコントローラーを作成します。

var ng = angular.module('app');
ng.lazy.controller('vendorController', [ function() {
. . .

「resolve」プロパティは、ルートがロードされる前にどのプロミスを解決するかを定義します。

2
Skarllot

ディレクティブを使用してコントローラーをロードすることもできます!

ここの例:

https://Gist.github.com/raphaelluchini/53d08ed1331e47aa6a87

1
raphaelluchini

あなたが求めていることを行う最善の方法は、代わりにディレクティブを使用し、適切なタイミングでバインドされるようにコントローラーとテンプレートを結び付けることです。現在、2番目の例で示したようにグローバル関数を宣言しない限り、適切なタイミングでlazy.htmでバインドは行われません。

1
Fourth

理想的には-Angularは、新しいバージョンではより頻繁に適用される可能性があるため、HTMLとJSを強制的に分離します。

RequireJSの使用が必要な場合があります http://solutionoptimist.com/2013/09/30/requirejs-angularjs-dependency-injection/

トリックのために試すことができます

ng-controller-controller="'lazy'"

または

HTMLで

ng-controller-controller = "myObject.controller"

どこかに注入する

$scope.myObject.controller = $controller('lazy', {$scope: $scope})
1
bhantol

これを試してください plugin for Angular JS。オンデマンドでコントローラースクリプトを遅延ロードするのに役立ちます。

1
Nirus

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

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