0

I need to dynamically add and initialize a set of controllers using AngularJS. I was able to add the controllers using the code below (based on 'UPDATE 2' of this answer) but don't understand how to pass data to the controls dynamically.

My code is below and also available as a JSFiddle. The goal is to have {{ field_name }} and {{ field_type }} populated in the templates once they are added -- right now they are blank. I tried to set these using ng-init but that doesn't seem to work.

This is my first time using AngularJS and I'm coming from a Backbone background so I apologize I am missing something obvious and please feel to correct anything. Adding and initializing the controllers from a dynamic data structure defined on the client is a requirement of the project so I won't be able to render the page content on the server or make additional API calls. Thanks in advance.

Javascript:

DATA_FIELDS = [
    {'field_name': 'name', 'field_type': 'str'},
    {'field_name': 'location', 'field_type': 'str'},
    {'field_name': 'is_active', 'field_type': 'bool'}
]

// Create base module and store providers for later use
providers = {};
editorApp = angular.module('editorApp', [],
    function ($controllerProvider, $compileProvider, $provide) {
        providers = {
            $controllerProvider: $controllerProvider,
            $compileProvider: $compileProvider,
            $provide: $provide
        };
    }
);

// Boostrap base module
angular.bootstrap($('body'), ['editorApp']);

// Store our _invokeQueue length before loading our controllers
// This is just so we don't re-register anything
queueLen = angular.module('editorApp')._invokeQueue.length;

// Define controller for string field
angular.module('editorApp').controller('DisplayFieldStr',
    function ($scope, $rootScope) {
    }
);

// Define controller for boolean field
angular.module('editorApp').controller('DisplayFieldBool',
    function ($scope, $rootScope) {
    }
);

// Register the controls/directives/services we just loaded
var queue = angular.module('editorApp')._invokeQueue;
for(i=queueLen;i<queue.length;i++) {
    var call = queue[i];
    // call is in the form [providerName, providerFunc, providerArguments]
    var provider = providers[call[0]];
    if (provider) {
        provider[call[1]].apply(provider, call[2]);
    }
}

// Append templates to content div and set init attrs  
for (i = 0; i < DATA_FIELDS.length; i += 1) {
    field = DATA_FIELDS[i];
    init_data = 'field_name=\''+field.field_name+'\', ';
    init_data += 'field_type=\''+field.field_type+'\'';
    div = $('.templates .df-' + field.field_type).clone().appendTo('.content');
    div.attr('id', 'df-' + field.field_name);
    controller_name = 'DisplayField' + field.field_type[0].toUpperCase() + field.field_type.substring(1);
    div.attr('ng-controller', controller_name);
    div.attr('ng-init', init_data);
}

// compile the new element
$('body').injector().invoke(function ($compile, $rootScope) {
    for (i = 0; i < DATA_FIELDS.length; i += 1) {
        field = DATA_FIELDS[i];
        $compile($('#df-' + field.field_name))($rootScope);
    }
    $rootScope.$apply();
});

HTML:

<div class='content'></div>
<div class='templates' style='display: none;'>
  <div class='df df-str'>
    <ul>
      <li>Template: df-str</li>
      <li>Field name: {{ field_name }}</li>
      <li>Field type: {{ field_type }}</li>
    </ul>
  </div>
  <div class='df df-bool'>
    <ul>
      <li>Template: df-bool</li>
      <li>Field name: {{ field_name }}</li>
      <li>Field type: {{ field_type }}</li>
    </ul>
  </div>
</div>
Community
  • 1
  • 1
Hakan B.
  • 2,319
  • 23
  • 29

1 Answers1

1

Here is a way you can manipulate the scope of a controller:

//create a new scope from an existing one
var newScope = scope.$new();

//add whatever data you want to
newScope.someProperty = 'some value';

//compile the html with your custom scope
$compile(element.contents())(newScope);

Assuming the compiled html looked like this:

<p>{{someProperty}}</p>

The result would be:

<p>some value</p>

Live demo (click).

m59
  • 43,214
  • 14
  • 119
  • 136
  • Yes, that's what I'm trying to accomplish, thank you. Would it be 'Angularish' to do this using ``$rootScope`` prior to the ``$compile`` calls at the end of my JS? I would really appreciate an example using my code if possible. – Hakan B. Feb 14 '14 at 00:33
  • @HakanB. I included a simple live demo in my answer. I'm not really sure what all your project entails - using your code could be quite difficult for me. Lazy-loading things like this isn't really "Angularish" no matter what you do since the devs didn't have it in mind...it's just fortunately possible. Whatever you do, just follow the usual best practices. The only thing I think is required here is that $compile takes place in a directive, since that is dom manipulation and the other logic should be in a service (like fetching/creating controller functions). – m59 Feb 14 '14 at 00:38
  • OK - thanks very much for your help. I'm not sure if you saw but I included a JSFiddle if that helps with working with my code: http://jsfiddle.net/hakanb/Fy94Y/ Otherwise I'll work with what you gave me. Thanks. – Hakan B. Feb 14 '14 at 00:46
  • @HakanB. I don't understand why all of this is taking place outside of angular...there is probably just too much that you would have to explain. Anyway, you can just add my sample code there just using $rootScope as the scope to create from. – m59 Feb 14 '14 at 01:05
  • After spending more time learning Angular it's clear my approach was completely incorrect and this question makes no sense. Thanks for trying to help anyway. I'm going to try to close the question. – Hakan B. Feb 18 '14 at 04:36
  • @HakanB. Even if that is the case, your question and my answer may help someone in the future. There are lots of questions on SO that are misguided, but the answers help get things on the right track. When someone else with the same problem comes across those posts, they might be able to get some insight to their own problems as well. If you feel that my answer is lacking something that will be useful to future viewers, either let me know or make your own answer detailing everything you think is relevant. – m59 Feb 18 '14 at 05:28