3

I'm injecting insecure html into some <div>, like this:

<div class="category-wrapper" ng-bind-html="content"></div>

this html has angularjs "code" ($scope.content is loaded with something like this):

<script type='text/javascript' src='giveus.js'></script>
<div class="giveus-wrapper" ng-controller="GiveUsController">{{variable1}}</div>

Note that this snippet has ng-controller. GiveUsController is lazy loaded at the same time that the embedded html (not in head). There is no error declaring this controller because It has been already tested.

My controller is as easy as:

angular.module("tf").controller('GiveUsController', function ($scope, $http)    
{
     console.debug("GiveUsController loaded");
     $scope.variable1 = "hi!";
}

there is no console debug nor variable1 assignment

It looks like there is no controller binding to that <div>.

I don't know how I can inject html with angular controller and make it work...

Any idea?

scniro
  • 16,844
  • 8
  • 62
  • 106
MrViSiOn
  • 191
  • 2
  • 17
  • 1
    Angular is bootstrapped once so if you try to add components after it has been bootstrapped that probably won't work although I'm not sure how to reload them (if you even can). – Explosion Pills Sep 29 '15 at 16:21
  • To allow for better answers I suggest you create a test within the code editor of StackOverflow or Plunker/JSFiddle – Enkode Sep 29 '15 at 16:25
  • AS @ExplosionPills noticed, your problem can be related with angularJs bootstrapping. You can manually bootstrap, see more here: https://docs.angularjs.org/api/ng/function/angular.bootstrap – Ricardo Pontual Sep 29 '15 at 16:27

3 Answers3

4

You could do what you are wanting with a bit of manual html compilation. Here is an approach that is essentially a directive wrapper for the $compile service. Observe the following example and usage...

 <div class="category-wrapper" ng-html="content"></div>

.controller('ctrl', function($scope) {
    $scope.content = '<div class="giveus-wrapper" ng-controller="GiveUsController">{{variable1}}</div>'
})
.controller('GiveUsController', function($scope) {

    console.log('hello from GiveUsController')
    $scope.variable1 = 'I am variable 1'
})
.directive('ngHtml', ['$compile', function ($compile) {
    return function (scope, elem, attrs) {
        if (attrs.ngHtml) {
            elem.html(scope.$eval(attrs.ngHtml));
            $compile(elem.contents())(scope);
        }
        scope.$watch(attrs.ngHtml, function (newValue, oldValue) {
            if (newValue && newValue !== oldValue) {
                elem.html(newValue);
                $compile(elem.contents())(scope);
            }
        });
    };
}]);

JSFiddle Link - demo

scniro
  • 16,844
  • 8
  • 62
  • 106
  • @MrVision I have edited my answer to closer match your question. Could you check this out to see if this works for you? My suspicion is that JavaScript you are loading. Can you include that on your page and not in this dynamic call? – scniro Sep 29 '15 at 17:22
  • Thank you so much! This is solution for me! Only one thing: I have to replace elem.html(newValue); for elem.html(newValue.toString()); and everything goes well! Thank you! – MrViSiOn Sep 29 '15 at 17:39
1

Angular for itself don't bind the ng-directives that are added into the DOM.

The $sce.compile or $compile helps angular to read which elements are added into the actual DOM, also for use the $compile you must use a directive.

Should be like that:

var m = angular.module(...);

m.directive('directiveName', function factory(injectables) {
  return = {
    priority: 0,
    template: '<div></div>', // or // function(tElement, tAttrs) { ... },
    transclude: false,
    restrict: 'A',
    templateNamespace: 'html',
    scope: false,
    controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
    controllerAs: 'stringIdentifier',
    bindToController: false,
    require: 'siblingDirectiveName', 'optionalDirectiveName', '?^optionalParent'],
    compile: function compile(tElement, tAttrs, transclude) {
      return {
        pre: function preLink(scope, iElement, iAttrs, controller) { ... },
        post: function postLink(scope, iElement, iAttrs, controller) { ... }
      }
    },

  };
});

and where you want

$compileProvider.directive('compile', function($compile) {
      return function(scope, element, attrs) {
        scope.$watch(
          function(scope) {
            return scope.$eval(attrs.compile);
          },
          function(value) {
            element.html(value);
            $compile(element.contents())(scope);
          }
        );
      };
davesnx
  • 374
  • 3
  • 16
0

You have to compile the HTML content, i got this using a directive:

.directive('comunBindHtml', ['$compile', function ($compile) {
        return function(scope, element, attrs) {
          scope.$watch(
            function(scope) {
              // watch the 'compile' expression for changes
              return scope.$eval(attrs.compile);
            },
            function(value) {
              // when the 'compile' expression changes
              // assign it into the current DOM
              element.html(value);

              // compile the new DOM and link it to the current
              // scope.
              // NOTE: we only compile .childNodes so that
              // we don't get into infinite loop compiling ourselves
              $compile(element.contents())(scope);
            }
        );
      };
    }])

Hope it helps :)

Ismael Fuentes
  • 240
  • 1
  • 10
  • just FYI this is essentially the same answer as mine that was posted slightly afterwards – scniro Sep 29 '15 at 16:27
  • I tried all solutions you guys gave to me (thank you!), with no winning. I'm going to write this issue into some fiddle... and edit my post – MrViSiOn Sep 29 '15 at 17:13
  • I don't remember from where, but this answer was copied from another stack post. You should link the sources instead of copy them. – C0ZEN May 02 '17 at 17:12