5

CONTEXT

I need to load in my AngularJS (v1.4) app some HTML gotten from backend and insert it (the html) into my partial (already loaded). The partial has already some HTML loaded (and completely functional). Right now I'm able to load the HTML and compile it with a directive posted here (Compiling dynamic HTML strings from database). See code below.

PROBLEM

But...when part of the HTML is already loaded (partial loaded and functional) and then I get another HTML content from backend, and the directive is compiling that new one, the entire document (DOM) gets "freezed". I can't type on inputs or do any click on buttons, including those in my previous loaded HTML.

QUESTION

How could I load HTML content, $compile it in "background" or any other way that allows me to continue using the rest of the (already functional) HTML?

It is for me a requisite that the new html content that arrives gets compiled because it contains angular validations and so on that need to be compiled and get inside the "angular world" (be inside the angular digest cycle and so on).

This is the directive I'm using for compiling the html

(function () {

    var dynamic = function($compile) {
        return {
            restrict: 'A',
            replace: true,
            link: function (scope, ele, attrs) {
                scope.$watch(attrs.dynamic, function(html) {
                    if (html) {
                        ele.html(html);
                        $compile(ele.contents())(scope);
                    }

                });
            }
        };
    };

    dynamic.$inject = ['$compile'];

    angular.module('app')
        .directive('dynamic', dynamic);
}());

In the controller I've something like

// this will be filled with asynchronous calls were I get the HTMLs from a service
// in order to keep this example simple I just made a demo, not with the real async calls
$scope.secciones = []

//when the promises are getting resolved "secciones" would be something like (more items can be added after in time)
$scope.secciones = [
    {html: "<div> some html content here (not too small sometimes) </div>"},
    {html: "<div> another html content here (not too small sometimes) </div>"}
]

...and in the view

<!--every time an async call with html is resolved, it's added to secciones, then a new div is generated and compiled-->
<!-- if there was some html previously rendered and the app starts compiling new html the UI gets "freezed"-->
<div ng-repeat="item in secciones">
    <div dynamic="item.html"></div>
</div>

Note: I'm using this approach because each html represents a tab in a tabpanel I have, in which the user actually sees only one html of all of them in "secciones" (the others are hidden, but still there), but I need to compile the others in order to get them ready for the user when he/she click that other tab (another html in secciones).

If there could be any solution to this by upgrading to a newer version of AngularJS(1.x), let's say 1.6, for instance. I'd would be glad to try it out.

5
  • Use $timeout to wrap the compiling of the template. This way you wont block the main thread and your app will be responsive Commented Nov 30, 2016 at 15:24
  • There's also $scope.$evalAsync. Ref: docs.angularjs.org/api/ng/type/$rootScope.Scope Commented Nov 30, 2016 at 15:39
  • Thanks guys. Using the directive I mentioned in my question I did ` scope.$evalAsync( function() { ele.html(html); $compile(ele.contents())(scope); } ); ` and ` $timeout(function () { ele.html(html); $compile(ele.contents())(scope); }) ` The behavior was the same :( Commented Nov 30, 2016 at 18:29
  • @AsielLealCeldeiro maybe you can add the code of your directive to the question i order to find the right solution Commented Dec 1, 2016 at 10:34
  • @Kliment I added the code of the directive I mentioned initially in the question and an example of what I'm doing. Hope it helps to clarify the situation. Any other question, please, feel free to ask. Thank you very much for your help! Commented Dec 1, 2016 at 14:53

2 Answers 2

2
+25

Basically I have done this by getting html from script tag and compile it and append to existing div. You can use following snippet.

Include div in your html

<div id="step-container">

</div>

Controller code

var template = $templateCache.get('basicInfo'); // or your html
$compile($("#step-container").html(template).contents())($scope);
$("#step-container").show();

For demonstration this will be included in html page

<script type="text/ng-template" id="basicInfo"></script>

In this way you can compile you html coming from backend.

Hope it helps.

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

13 Comments

As soon as I give it a try I'll give you some feedback. Thank U very much for your help!
I've been playing around with your solution, but I think I misunderstood what the $templateCache.get method does. I though it would receive some html content (including a string html), but it seem that only htmll content, in a file, for instance it's possible to be passed to that method. I'm passing a string represtation of the html I want to render but the method $templateCache.get returns undefined, Please see this screenshot of my devtools. "data" is the html string and template is the return of the $templateCache.get Please see dropbox.com/s/5k2m3gs56grxoqp/sample.png?dl=0
You dont have to use template cache just pass your html as string . I have commented // or your html // there , that was just for example var template = " <div></div>" OR When you get your html from backend add this to template cache there and then receive from it
thank you so much!, but I've been unable to get this working without affecting the usability of the page. It's a key point on my question that when some (new)html is being compiled by angular, it doesn't block the rest of the page, and it can be "added" to the DOM content "smoothly". I've used another variant, according to docs.angularjs.org/api/ng/service/$templateCache and tried to do a $templateCache.put("myId" ,data); where data is the new html that I want to render and in the html I put <div data-ng-include="myId"></div> but no luck, tihs behaves the same way
The solution actually compiles my html and gets it into the page, but the process still freezes the webpage. Is the $("#step-container").show(); intended to "integrate" the new content only when it is ready? Maybe I'm using it incorrectly. Thanks for your help again!
|
1

You can try use this directive to compile the HTML code. This directive compile the code when to detect any change in variable htmlCode

module.directive('bindHtmlCompile', ['$compile', function ($compile) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                scope.$watch(function () {
                    return scope.$eval(attrs.bindHtmlCompile);
                }, function (value) {
                    element.html(value && value.toString());
                    var compileScope = scope;
                    if (attrs.bindHtmlScope) {
                        compileScope = scope.$eval(attrs.bindHtmlScope);
                    }
                    $compile(element.contents())(compileScope);
                });
            }
        };
    }]);

you can install via bower, DirectiveRepo

USAGE:

<div bind-html-compile="htmlCode"></div>

1 Comment

Thank you @Isma90 for your help. I just tried with your directive, but the final result is the same. When the html is kind of huge, the screen freezes as before.

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.