3

It looks like this questions already asked few times but there is no correct answer to it.

my case: i'm loading a template (with html and script) init to div using ajax (not using angular routing temple for some reason).

index.html (main)

<!DOCTYPE html>
<html ng-app="app" ng-controller="AppCtrl">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Web</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
    </head>
    <body>
        <div class="container body-content">
            <div class="dynamic-content" >
                <!-- Loading content Here -->
            </div>
            <button ng-click="loadTemplate()">Load Template</button>
        </div>

        <script>
            angular.module('app', [])
            .controller('AppCtrl', function ($scope) {
                $scope.someData = {};
                $scope.loadTemplate = function() {
                    ....
                    //AJAX to get the templet.html
                    //and load it into .dynamic-content
                    //then applying scope();                  
                }
            });
        </script>
    </body>
</html>

template.html (template)

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

<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>

when i click the button Load Template it loads the template.html file in to container but i'm getting error

Error: [ng:areq] Argument 'TempCtrl' is not a function, got undefined

though its being added to app controllers.

How can i add controllers dynamically and get it work with dynamic html nodes

DEMO HERE https://plnkr.co/edit/EAa9Md36hDzpQ1BgIQKg?p=preview

10
  • 1
    So you're loading ajax that attempts to add a new controller to your existing module, and the "begin" and "end" are showing up in your console, indicating that the script is being executed? Commented Feb 17, 2016 at 20:12
  • Have you tried just putting the script before the HTML portion of the template? Commented Feb 17, 2016 at 20:14
  • Yes the console is showing begin and end but not inside Commented Feb 17, 2016 at 20:14
  • @Dave Yes i tried like that, but same error Commented Feb 17, 2016 at 20:15
  • 1
    Actually this might do the trick: bennadel.com/blog/… Commented Feb 17, 2016 at 20:41

3 Answers 3

6

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

Sign up to request clarification or add additional context in comments.

1 Comment

Perfect. this is exactly what i want.. Thanks Dave.
2

Updated Dave's example with angular components support

app.config(
  function ($controllerProvider, $provide, $compileProvider) {

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

    // 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._component = app.component;

    // Provider-based controller.
    app.controller = function (name, 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 );
    };
    // Provider-based component.
    app.component = function (name, options) {
        $compileProvider.component(name, options);
        return ( this );
    };

});

Comments

-1

Try loading the templates controller in the index.html, instead of in the template.html, that way it is already existing when the markup is looking for it. In index.html script portion:

  angular.modules('app', [])
        .controller('AppCtrl', function ($scope) {
            $scope.someData = {};
            $scope.loadTemplate = function() {
                ....
                //AJAX to get the templet.html
                //and load it into .dynamic-content
                //then applying scope();                  
            }
        }).controller('TempCtrl', function ($scope) {
    $scope.total = 0;
    console.log('inside')
    $scope.update = function () {
        total += total;
    };
});

EDIT: This is when having multiple JS files that reference the app module would be handy, that way each file is owning a controller.

4 Comments

my question is how to add dynamic controllers not how to get this example work..
I'm just curious now, what is the benefit you want to receive from this?
like i mentioned.. i have 50+ template files with each template has its own controller(s). so cant put them all in one file and load at once
anyhow i found a solution and updated my plunker plnkr.co/edit/EAa9Md36hDzpQ1BgIQKg?p=preview

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.