0

In part of my application, I have a user editing a single page of information (via an HTML editor: Content Tools). I have started to modify that functional code so that the page they are editing is structured with a dynamic template, rather than hard coded.

Currently I am storing the edited/editable data in a database table. I also am storing the template definition in a database table. The template I am using is simply the HTML code that was previously hard coded, so I know the template is valid.

At this point I am correctly receiving back all the data in a JSON wrapper from the server just fine. What is not working is rendering the content--the page remains blank. I'm sure the issue is my lack of understanding for AngularJS and how it executes.

[UPDATED: Code and following execution description has been updated after reworking with input from @Wes H and @31PIY. No longer a $templateCache issue, apparently.] Specifically, my HTML is:

<body ng-app>
    <div ng-controller="PageEditController">
        <div ng-bind-html="renderHtml(htmlTemplate)"></div>
    </div>
</body>

[NOTE: renderHtml is my wrapper for $sce.TrustAsHtml() to make sure the HTML is not stripped by Angular]

And the relevant part of my javascript is:

        $http({
            method: 'POST',
            url: 'https://my-api-access-point',
            data: parms
        }).then(function successCallback(response) {
                var resp = response.data['Items'];
                var data = {};
                if (resp)
                    data = resp[0];
                console.log("jsLoadScreen Data: ", data);
                $scope.models.fields = data;
                console.log('replacing template: ', $scope.models.fields['template']);
                $scope.htmlTemplate = $scope.models.fields['template'];

And the console output is:

jsLoadScreen Data: {UID:"1"
 left:"<h1>↵    One↵</h1>"
 right:"<h2>↵    Two↵</h2>"
 screenId:"1"
 template:"        <div class="row">↵            <div class="column">↵                <div data-editable data-name="left" ng-bind-html="renderHtml(models.fields.left)">↵                </div>↵            </div>↵            <div class="vl"></div>↵            <div class="column">↵                <div data-editable data-name="right" ng-bind-html="renderHtml(models.fields.right)">↵                </div>↵            </div>↵        </div>"

replacing template:         <div class="row">
            <div class="column">
                <div data-editable data-name="left" ng-bind-html="renderHtml(models.fields.left)">
                </div>
            </div>
            <div class="vl"></div>
            <div class="column">
                <div data-editable data-name="right" ng-bind-html="renderHtml(models.fields.right)">
                </div>
            </div>
        </div>

I have verified that the template code IS loading on the page (via "view source"). However, no javascript or AngularJS tag in that template code is executed once it loads.

I have tried several steps to make this code execute, including adding another .then() to my promise to touch some variables that are in the template to force a refresh, as well as various ill fated attempts to cause delays in the process in case there was a race condition. No luck.

Am I in such strange waters for AngularJS that I should just give up and combine the template and data on the server side and make this a single AJAX operation and not try to do the two step: (1) AJAX insert new HTML template, once inserted then (2) AJAX insert new data into the new HTML template? Or is there something I should do to the newly inserted template (or while inserting the template) to register it as something for javascript to execute?

The closest descriptions I have seen on SO are: How $templateCache works? and Load HTML template from file into a variable in AngularJs but I can't quite wrap my mind around what they say and what I am unsuccessfully trying to do.

UPDATE: After working through the previous responses below, I am closeER. With the info from @Wes H and @31piy I now have verified the HTML is loaded on the page correctly (as seen by "view source" in the developer tools on Chrome). HOWEVER, the various Angular commands (ng-bind-html(renderHtml...)) and 3rd party tool triggers (data-editable) don't appear to be interpreted by Angular, so I have the HTML but no javascript activity related to it. My guess is the inserted HTML template somehow is put in the DOM but not actually recognized and executed by Angular (or javascript). Thoughts?

Doug
  • 235
  • 4
  • 11

4 Answers4

1

Assuming that all the relevant configuration is done in your AngularJS app, in my opinion, you don't need to use $templateCache at all. Why? You want the HTML template from the DB to be set in your HTML (if I understood it correctly).

To do that, you can simply use ng-bind-html which binds the HTML content to an HTML element in your template. Here is an example:

$http.post('https://my-api-access-point', parms)
  .then(function successCallback(response) {
    // ... Your code
    // Assign the template to a scope variable here
    $scope.htmlTemplate = $scope.models.fields['template'];
    // ... Your code
  });

Now, you have a variable htmlTemplate in the scope, which can be bound directly to the HTML template like this:

<div ng-bind-html="htmlTemplate"></div>

This way, the contents of htmlTemplate will appear under the div element. You need to ensure that you've correctly configured ngSanitize module in your app.

31piy
  • 23,323
  • 6
  • 47
  • 67
  • Good info here, and working in conjunction with @Wes H I was able to get much closer. I have updated my above post with more information. – Doug Jan 02 '18 at 19:51
1

$templateCache - As the first link you listed describes, it serves as a caching utility for any template URL requests like templateUrl, ng-include, etc.

In addition to this, you can use $templateCache as key-value cache storage. When you $templateCache.put('KEY', 'HTML string'), any template URL request will check against the stored keys before making a network request. In your case, you are manually adding a store to the key 'edit.html', so it will always retrieve your value. The key can be any unique string though, doesn't have to be a file name.

What you're trying to do should work, according to the documentation. However, the typical way to include a template (cached or not) in AngularJs is to use ngInclude.

If you replace the <script></script> block with:

<div ng-include="'edit.html'"></div>

The HTML and any bindings from $scope.models.fields['template'] will be rendered. Note the extra quotes around the key.

The other potential tricky part is making sure the promise that's being returned by $http is resolved before you try to access the cached template in ng-include. That's where 31piy's suggestion of using ng-bind-html, which watches $scope and would update when the promise resolves, might be simpler.

Wes H
  • 11
  • 1
  • Good info here, but that ng-include does create an infinite loop as it appears to re-trigger on the reload. I don't know the details why, but @31piy 's ng-bind-html does appear to get me closER. I have updated my above post with more information. – Doug Jan 02 '18 at 19:50
0

To start, disclaimer ... I do not know AngularJS at all, so take this with a grain of salt!

That being said, after reading this, I had a look at the documentation: https://docs.angularjs.org/api/ng/service/$templateCache

It looks like you've created the cache declaratively by consuming the $templateCache service directly. Now, you need to render it through one of the following methods:

myApp.component('myComponent', {
   templateUrl: 'edit.html'
});

or

$templateCache.get('edit.html')

Additionally, since you're consuming the service directly, there's no need for the script tag. According to the documentation, you can take one approach or the other.

Hope that helps!

user1786981
  • 116
  • 6
0

In addition to the restructures mentioned by @31piy & @Wes H, it turns out the final step is to use $compile with a directive. Solution found here: Compiling dynamic HTML strings from database

Doug
  • 235
  • 4
  • 11