AngularJS + JQuery : How to get dynamic content working in angularjs

JqueryAngularjs

Jquery Problem Overview


I am working on a Ajax app using both jQuery and AngularJS.

When I update content (which contains AngularJS bindings) of a div using jQuery's html function, the AngularJS bindings doesn't work.

Following is code of what I am trying to do:

$(document).ready(function() {
  $("#refreshButton").click(function() {
    $("#dynamicContent").html("<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>")
  });
});

</style><script src="http://docs.angularjs.org/angular-1.0.1.min.js"></script><style>.ng-invalid {
  border: 1px solid red;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="">
  <div id='dynamicContent'>
    <button ng-click="count = count + 1" ng-init="count=0">
        Increment
      </button>
    <span>count: {{count}} </span>
  </div>


  <button id='refreshButton'>
    Refresh
  </button>
</div>

I have dynamic content inside a div with the ID #dynamicContent, and I have a refresh button that would update contents of this div when refresh is clicked. Increment works as expected if I don't refresh the content, but after I refresh, the AngularJS binding stops working.

This may not be valid in AngularJS, but I initially built application with jQuery and started using AngularJS later on so I can't migrate everything to AngularJS. Any help with getting this working in AngularJS is greatly appreciated.

Jquery Solutions


Solution 1 - Jquery

You need to call $compile on the HTML string before inserting it into the DOM so that angular gets a chance to perform the binding.

In your fiddle, it would look something like this.

$("#dynamicContent").html(
  $compile(
    "<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>"
  )(scope)
);

Obviously, $compile must be injected into your controller for this to work.

Read more in the $compile documentation.

Solution 2 - Jquery

Another Solution in Case You Don't Have Control Over Dynamic Content

This works if you didn't load your element through a directive (ie. like in the example in the commented jsfiddles).

Wrap up Your Content

Wrap your content in a div so that you can select it if you are using JQuery. You an also opt to use native javascript to get your element.

<div class="selector">
    <grid-filter columnname="LastNameFirstName" gridname="HomeGrid"></grid-filter>
</div>

Use Angular Injector

You can use the following code to get a reference to $compile if you don't have one.

$(".selector").each(function () {
    var content = $(this);
    angular.element(document).injector().invoke(function($compile) {
        var scope = angular.element(content).scope();
        $compile(content)(scope);
    });
});

Summary

The original post seemed to assume you had a $compile reference handy. It is obviously easy when you have the reference, but I didn't so this was the answer for me.

One Caveat of the previous code

If you are using a asp.net/mvc bundle with minify scenario you will get in trouble when you deploy in release mode. The trouble comes in the form of Uncaught Error: [$injector:unpr] which is caused by the minifier messing with the angular javascript code.

Here is the way to remedy it:

Replace the prevous code snippet with the following overload.

...
angular.element(document).injector().invoke(
[
    "$compile", function($compile) {
        var scope = angular.element(content).scope();
        $compile(content)(scope);
    }
]);
...

This caused a lot of grief for me before I pieced it together.

Solution 3 - Jquery

Addition to @jwize's answer

Because angular.element(document).injector() was giving error injector is not defined So, I have created function that you can run after AJAX call or when DOM is changed using jQuery.

  function compileAngularElement( elSelector) {
        
        var elSelector = (typeof elSelector == 'string') ? elSelector : null ;  
            // The new element to be added
        if (elSelector != null ) {
            var $div = $( elSelector );

                // The parent of the new element
                var $target = $("[ng-app]");

              angular.element($target).injector().invoke(['$compile', function ($compile) {
                        var $scope = angular.element($target).scope();
                        $compile($div)($scope);
                        // Finally, refresh the watch expressions in the new element
                        $scope.$apply();
                    }]);
            }

        }

use it by passing just new element's selector. like this

compileAngularElement( '.user' ) ; 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRajaView Question on Stackoverflow
Solution 1 - JqueryNoah FreitasView Answer on Stackoverflow
Solution 2 - JqueryjwizeView Answer on Stackoverflow
Solution 3 - Jqueryshyammakwana.meView Answer on Stackoverflow