Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically loading AngularJS modules from within Templates (Views)

Background: Let's suppose for the sake of argument that you have 100,000 views (partials). Let's also suppose you have accompanying view-scoped controllers, and potentially view-scoped services and filters as well. Try to envision an aggregating application that hosts 100,000 disparate small applications.

Issue: When you have "partials" that require accompanying controllers, the typical solution is to do something like this:

$routeProvider.when('/app1', {
        templateUrl: 'partials/view1.html',
        controller: 'controller1'
    });

The controller is typically loaded from index.html via:

<script src="js/directives/Controller1.js"></script>

The problem with this approach is that it doesn't scale. There are solutions out there for dynamically loading controllers, but they still require adding touch points in various config.

Ideal Solution: Ideally - again for very small applications whose numbers are in the 000's, the controller could be loaded dynamically, and from within the partial itself. This would alleviate the need to manage several files and several configuration touch points (not to mention network requests), and keep each partial very well contained.

It would look something like this:

In router:

$routeProvider.when('/apps/:appId', {
        templateUrl: 'partials/app-frame.html',
        controller: 'AppCtrl'
    });

In containing html (app-frame) include the relatively disparate "mini app":

<h1>Currently hosting {{appId}}</h1><hr>
<div class="ng-include: appUrl"></div>

In partial resolved with appUrl, define controller and markup in one:

<script>
  myApp.controller('controller1', ['$scope', function ($scope) {
    $scope.foo = "bar";
  }]);
</script>


<div ng-controller="controller1">
     {{foo}}
</div>

For cases like this, where there are a lot of partials and a 1-1 mapping for controller and view, it can make sense to couple the two for development efficiencies and maintenance. It's a lot cleaner than using several files and additional configuration touch points.

The problem is, this doesn't work. It could be as simple as forcing the script to load prior to applying the directive... but not sure how to do that?

Here are some similar explanations of the problem:

https://groups.google.com/forum/#!topic/angular/H4haaMePJU0

Loading Partial Page With Angular and Compile The Controller

Igor from the AngularJS team says:

I see.. we looked into supporting script tags in jqlite, but what needs to be done to get a cross-browser support involves a lot of black magic. For this reason we decided that for now we are just going to recommend that users use jquery along with angular in this particular case. It doesn't make sense for us to rewrite one third of jquery to get this working in jqlite.

But I don't know what he means by "use jquery" ... JQuery is already loaded into the application from index.html (and prior to angularjs), but it sounds like I need to do something specifically within the partial itself.

like image 274
Robert Christian Avatar asked Nov 05 '13 00:11

Robert Christian


1 Answers

You cannot add new controllers through module('app').controller(name, function() { .. }) after AngularJS bootstrap. In order make it work you should use $controllerProvider.register(name, function() { .. }).

You can override the original controller registering function in following way to be able to add controllers pre and pos bootstrap:

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

app.config(function($controllerProvider) {
    app.controller = function (name, controller) {
        $controllerProvider.register(name, controller);
    };
});
like image 111
otavio.leitao Avatar answered Sep 21 '22 06:09

otavio.leitao