Loading an Angularjs Controller Dynamically

Loading an AngularJS controller dynamically

I've found a possible solution where I don't need to know about the controller before bootstrapping:

// Make module Foo and store $controllerProvider in a global
var controllerProvider = null;
angular.module('Foo', [], function($controllerProvider) {
controllerProvider = $controllerProvider;
});
// Bootstrap Foo
angular.bootstrap($('body'), ['Foo']);

// .. time passes ..

// Load javascript file with Ctrl controller
angular.module('Foo').controller('Ctrl', function($scope, $rootScope) {
$scope.msg = "It works! rootScope is " + $rootScope.$id +
", should be " + $('body').scope().$id;
});
// Load html file with content that uses Ctrl controller
$('<div id="ctrl" ng-controller="Ctrl" ng-bind="msg">').appendTo('body');

// Register Ctrl controller manually
// If you can reference the controller function directly, just run:
// $controllerProvider.register(controllerName, controllerFunction);
// Note: I haven't found a way to get $controllerProvider at this stage
// 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) {
controllerProvider.register(controllerName, call[2][1]);
}
}
}
registerController("Foo", "Ctrl");
// compile the new element
$('body').injector().invoke(function($compile, $rootScope) {
$compile($('#ctrl'))($rootScope);
$rootScope.$apply();
});

Fiddle. Only problem is that you need to store the $controllerProvider and use it in a place where it really shouldn't be used (after the bootstrap). Also there doesn't seem to be an easy way to get at a function used to define a controller until it is registered, so I need to loop through the module's _invokeQueue, which is undocumented.

UPDATE: To register directives and services, instead of $controllerProvider.register simply use $compileProvider.directive and $provide.factory respectively. Again, you'll need to save references to these in your initial module config.

UDPATE 2: Here's a fiddle which automatically registers all controllers/directives/services loaded without having to specify them individually.

Angularjs add controller dynamically

This blog describes how to fiddle with angular to force it to load additional controllers after it has been bootstrapped:

http://www.bennadel.com/blog/2553-loading-angularjs-components-after-your-application-has-been-bootstrapped.htm

Of course, this is completely unsupported, and could be broken by changes to angular at any time.

But, here is an updated version of your code using this method:

var app = angular.module('app', [])
app.config(
function($controllerProvider, $provide, $compileProvider) {
// Since the "shorthand" methods for component
// definitions are no longer valid, we can just
// override them to use the providers for post-
// bootstrap loading.
console.log("Config method executed.");
// Let's keep the older references.
app._controller = app.controller;
app._service = app.service;
app._factory = app.factory;
app._value = app.value;
app._directive = app.directive;
app.controller = function(name, constructor) {
console.log("controller...");
console.log(name);
console.dir(constructor);
$controllerProvider.register(name, constructor);
return (this);
};
// Provider-based service.
app.service = function(name, constructor) {
$provide.service(name, constructor);
return (this);
};
// Provider-based factory.
app.factory = function(name, factory) {
$provide.factory(name, factory);
return (this);
};
// Provider-based value.
app.value = function(name, value) {
$provide.value(name, value);
return (this);
};
// Provider-based directive.
app.directive = function(name, factory) {
$compileProvider.directive(name, factory);
return (this);
};
});
app.controller('AppCtrl', function($scope, $http, $compile) {
$scope.someData = {};
$scope.loadTemplate = function() {
$http.get("template.html")
.then(function(r) {
// load in the html, including the script, which will be executed
$(".dynamic-content").html(
r.data
);
// compile the loaded html into an actual template and put it back where it was
$(".dynamic-content").html($compile($(".dynamic-content").html())($scope));
})
}
});

Note that I used jQuery to place the HTML into the DOM, causing the script to execute, then to grab the HTML back out of the DOM so it could be compiled, after which I placed it back in the DOM again.

Also, there is an undefined variable in your template.html, so I updated it to look like this:

<script>
console.log('begin')
angular.module('app')
.controller('TempCtrl', function ($scope) {
$scope.total = 0;
console.log('inside')
$scope.update = function () {
$scope.total += 1;
};
});
console.log('end')
</script>

<div ng-controller="TempCtrl">
<h2>About</h2>
<h3>{{total}}</h3>
<p>Testing the total</p>
<button ng-click="update()">Update</button>
</div>

Here is a working plunk: http://plnkr.co/edit/cB5N05

Loading angularjs dynamically and generating controllers and scope

Angularjs guide page would help you.

http://docs.angularjs.org/guide/bootstrap

There is a description how to do the manual bootstrapping.

AngularJS Dynamically Load Controller and Template based on Route

I was able to get it working by not including the controller in the routing at all. Instead I put the ng-controller attribute in the view I was loading.

This worked!


angular.module('common.auto-loading-routes', []).config(function($routeProvider){

function getTemplate(routeParams){
var route = (routeParams.current) ? routeParams.current.params.route : routeParams.route;
var directory = route.split('/');
directory = directory.filter(function(entry) { return entry.trim() != ''; });
var page = directory[directory.length - 1];
directory.splice(-1,1);
directory = directory.join('/');
return directory + '/' + page +'.view.html';
}

$routeProvider.when('/:route*', {
templateUrl: function($routeParams) { //works
return getTemplate($routeParams);
},
resolve: {
check: function($route, $http, $location){
return $http.get(getTemplate($route)).then(function(response){
if (response.status !== 404){
return true;
} else {
$location.path('404');
}
});
}
}
}).otherwise('/404');
});

In the HTML of that view, I just put

ng-controller="home.controller"
(or whatever controller is appropriate)

Load controller dynamically based on route group

I managed to solve it inspired by @calebboyd, http://ify.io/lazy-loading-in-angularjs/ and http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx

Using http://dustindiaz.com/scriptjs

app.js

app.config(function($controllerProvider, $compileProvider, $filterProvider, $provide) {
app.register = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
});

Then i register the "load controller by group" route.

$routeProvider.when('/:plugin', {

templateUrl: function(rd) {
return 'plugin/' + rd.plugin + '/index.html';
},

resolve: {
load: function($q, $route, $rootScope) {

var deferred = $q.defer();

var dependencies = [
'plugin/' + $route.current.params.plugin + '/controller.js'
];

$script(dependencies, function () {
$rootScope.$apply(function() {
deferred.resolve();
});
});

return deferred.promise;
}
}
});

controller.js

app.register.controller('MyPluginCtrl', function ($scope) {
...
});

index.html

<div ng-controller="MyPluginCtrl">
...
</div>


Related Topics



Leave a reply



Submit