Saturday, May 14, 2016

AngularJS with dynamically generated content

This post is about using Angular with dynamically loaded markup (in this particular case it was an entire form).

TL;DR

The problem

Angular can only interact with elements present at its bootstrap time, so it doesn’t know about new generated content from an ajax call for example.


The (real-life) situation is like this, we have a main page that has a button which makes an ajax request when clicked to get the form contents and inject the generated HMTL into a <div> element, no Angular, just jQuery, and yeah, I know it’s not cool, but I’ve mixed Angular and jQuery to solve this problem.
This was a template that must not be changed, and to complicate things further, the toolbar that has the “New entry” button is included with the items grid which - as you guessed- loads with an ajax request after the main page loads (document ready event) ..

The options here are to:
  1. Add the script tags to the form file (template).
  2. Add them to the toolbar that has the “New entry” button.
  3. Add them to the main page.

I’ve chosen the third approach because of the drawbacks of the other two approaches.

The first approach will result in multiple inclusions of the scripts, because every click on the button will inject the HTML of the form template which includes the scripts every time.

The second approach has the same problem, plus, Angular won't see the form as it shows because it loads dynamically, so no advantage here.

The third approach made most sense, it includes the scripts one time when the user clicks the menu entry to open that page, so the only problem here is making angular deal with the newly generated content.


The solution

  • Include the Angular and the app (module) scripts into the main page.
  • Assign the module and the main controller to the wrapper div which also contains a hidden textarea with the ngChange calling the function “recompile”, which we going to define in a moment.
  • After the Ajax request that gets the form contents, insert the generated markup into the textarea and invoke the change event, something like:
    $("#formcontent").val(data).change();

  • At the “mainCtrl” controller, define the “recompile” function:
$scope.recompile = function(){
              $("#the-form-div").html($compile($scope.formcontent)($scope));
              $scope.formcontent = "";
            }



As the Angular documentation mentions about the $compile service:

Compiles an HTML string or DOM into a template and produces a template function, which can then be used to link scope and the template together.

And: The compilation is a process of walking the DOM tree and matching DOM elements to directives.

So basically what’s happening here is the we tell Angular to inspect whatever is inside of the textarea and work its magic on it before showing that to the user.


That’s it, thanks.

No comments:

Post a Comment