8

I have a web page with a module defined (myModule) where I'm boostraping angularjs using

angular.bootstrap(element,[myModule.name]);

After the click of a button, I add dynamic html and compile using

$compile('<my-element data="data"></my-element>',$scope.$new());

The directive is added using

myModule.directive('myElement',function(){});

The problem is when I add the directive before calling bootstrap, $compile ends up correctly processing my directive. However, if the directive is added after calling bootstrap, $compile does not do anything to my html. It just adds the class ng-scope to it and the directive/tag is not processed.

In my case, not all the directives will be loaded before bootstrap is called. In the case where I load directives after calling bootstrap, how do I get to use it within the page?

Thanks.

Edit: Just to clarify. All directives are loaded dynamically. Those I load before bootstrapping work fine. Those I load after bootstrapping fails. When I swap the directives loaded, I can the same result so it is not the directives but appears to be that after bootstrapping, newly added directives does not seem to take effect.

ritcoder
  • 3,274
  • 10
  • 42
  • 62

2 Answers2

8

The thing with registering lazy controller or directives is that you have to get hold of $controllerProvider and $compileProvider respectively.

It can be done only during configuration phase so you have to keep the references until you load the controllers/directives.

Lately I was working on lazy loading of the controller, today I've added support for directives, check out my code here:

https://github.com/matys84pl/angularjs-requirejs-lazy-controllers/

particularly this module lazy-directives.js

NOTE: I use RequireJS in my project, however applying my solution to yepnope should be quite easy.

matys84pl
  • 8,344
  • 1
  • 18
  • 18
  • Hi matys84pl, that is exactly what I needed. My directives are now loading quite nicely. Yet to try the controllers but that is not my target for now. One thing though that I noticed was that the first request for directives complete before angular calls config (before I call .bootstrap) and in that instance I use the module to add the directives as in (me.providers.compile || me.module).directive(k, v). Thanks. – ritcoder Nov 26 '12 at 15:46
  • +1 on the exactly what I needed... Already using requireJS but new to Angular and needed to figure out how to navigate the tricky bootstrapping sequences to lazy-load. This is so perfect for what I'm doing that I feel downright guilty cribbing your homework! – uooq Dec 04 '12 at 13:47
2

This Fiddle show-cases how to dynamically load/register and use:

  • Angular controllers (using $controllerProvider)
  • Angular directives (using $compileProvider)
  • Angular partial templates (using $templateCache)

Code: Setup

// Initialize app.lazyController and app.lazyDirective.
// We will later use them to create controller and directives dynamically.
var app = angular.module('app', []);
app.config(function($controllerProvider, $compileProvider) {
        // see page 12 of:
        //    http://www.slideshare.net/nirkaufman/angularjs-lazy-loading-techniques
        app.lazyController = $controllerProvider.register;

        // see: http://jsfiddle.net/8Bf8m/33/
        app.lazyDirective = $compileProvider.directive;
    });

// set of partials
var partials = [];

// store scope & templateCache, so we can dynamically insert partials
var scope, templateCache;

// define main controller
function MainCtrl($scope, $templateCache) {
    $scope.partials = partials;

    scope = $scope;
    templateCache = $templateCache;
}

Code: Example

var maxPartials = 3;
var i = 0;
var timer = setInterval(function() {
    var i = partials.length;

    app.lazyDirective('clickMe', function () { return {
        link : function (scope, element) {
            element.bind('click', function () {
                alert('Clicked: ' + element.text());
            });
        },
    };});

    // define controller
    var ctrlName = 'partial' + i + 'Ctrl';
    app.lazyController(ctrlName, function($scope) {
        $scope.text = 'hi ' + i;
    });

    // add partial template that I have available in string form
    var newPartial = {
        name: 'template' + i,
        content: '<div ng-controller="' + ctrlName + '" class="a' + i + '">' +
        '<input type="text" ng-model="text"></input>'+
        '{{text}} <br/>' + 
        '<button click-me="">Click Me!</button>' +
        '</div> <br/> <br/>'
    };
    partials[i] = newPartial;

    // add template and notify angular of the content change
    templateCache.put(partials[i].name, partials[i].content);
    scope.$apply();

    // stop timer
    if (partials.length >= maxPartials)  clearInterval(timer);
}, 1000);
Domi
  • 22,151
  • 15
  • 92
  • 122
  • 1
    The code actually works. The first several lines is the most important part. – blackmiaool Mar 13 '17 at 05:48
  • It's got the stuff you need plus an example. I guess I should have made it simpler and should explain the core part a bit better :) – Domi Mar 15 '17 at 14:59