Is it possible to compile the angular template into the final html string?
Is it possible to compile this html template line:
"<p>List of products from {{supplier.name}}</p> <p ng-repeat="ref in refs">{{ref}}</p>" directly to the html line, for example:
"<p>List of products from Some Supplier</p> <p>a0120</p> <p>a0241</p> <p>z1242</p> <p>z3412</p>" or at least a less clean version:
"<p class="ng-scope ng-binding">List of product from Duval</p> <!-- ngRepeat: ref in refs track by $index --> <p ng-repeat="ref in refs track by $index" class="ng-scope ng-binding">a0120</p> <p ng-repeat="ref in refs track by $index" class="ng-scope ng-binding">a0241</p> <p ng-repeat="ref in refs track by $index" class="ng-scope ng-binding">z1242</p> <p ng-repeat="ref in refs track by $index" class="ng-scope ng-binding">z3412</p>" I tried using $ compile (templateStr) ($ scope), but the returned dom elements are not fully processed. However, I was not able to compile it with the page element using the following directive, and, checking this element, I see that it has the last html I'm looking for:
app.directive('compile', function($compile) { return{ restrict: 'A', scope: { compile: '=compile', data: '=ngData' }, link: function(scope, element, attrs) { scope.$watch('data', function(value) { for (var k in scope.data) scope[k] = scope.data[k]; } ) scope.$watch('compile', function(value) { element.html(value); var a = $compile(element.contents())(scope); } ) } } }) Is there any way to get this latest html directly from the template? Thanks
PS: What I'm trying to achieve here is to edit the template directly in CKEditor (in text mode, not in the source) and only end up goint in source mode to add some "ng-repeat" attributes. Using template engines such as Handlebars requires placeholders outside the html elements and is automatically removed by CKEditor, since it deals only with html.
POSSIBLE SOLUTION (Hacky): One of the possible ways is to use the compilation directive on a hidden element and read the contents of the element after downloading to the controller:
$scope.$on('$viewContentLoaded', $scope.onLoaded); $timeout(function() { var el =$("#text div")[0] cleanAngularStuff(el) $scope.currMailTemplate.processed = el.innerHTML }); The cleanAngularStuff function is designed to clean additional angular directives and classes.
I will post it here if someone wants to use it or improve it.
Best way to do this without adding an element to the page?
What you need to do is access the compiled element after the $ digest loop.
So, in the $ digest loop, you can do:
templateString = '<some-template-code/>'; ... var compiled = $compile(templateString)(scope); // scope.$digest // only call this if not within a $digest cycle // you can do a $timeout to let the previous digest cycle complete $timeout(function(){ var theHtml = compiled[0].outerHTML; console.log('the html with the variables', theHtml); }); If you are not already in the digest loop, you must manually call scope.$digest() . You can see the embedded sample below.
(function(){ "use strict"; var app = angular.module('soApp', []); app.directive('showCompiledTemplate',[ '$compile','$timeout', function($compile , $timeout) { return { restrict: 'E', template: '<div class="compiled-template">' + '<div><textarea cols="40" rows="15" readonly></textarea></div>' + '<div class="output"></div>' + '</div>', scope: { data: '=', template: '=' }, link: function(scope,elem,attrs) { var textarea = elem.find('textarea')[0]; var output = elem.children().children().eq(1); var updateOutput = function(tpl) { var compiled = $compile(tpl)(scope); $timeout(function(){ var theHtml = compiled[0].outerHTML; textarea.value = theHtml; output.html(theHtml); }); }; scope.$watch("template",function(tpl){ updateOutput(tpl); }); scope.$watch("data",function(){ updateOutput(scope.template); },true); } }; } ]); app.controller('MainCtrl', function() { this.data = { name: 'John', list: ['one duck','two ducks','three ducks'] }; //this.template = "<div>hi</div>"; var template = ''; template += '<div>\n'; template += ' <p>{{data.name}}</p>\n'; template += ' <ul>\n'; template += ' <li ng-repeat="item in data.list">{{item}}</li>\n'; template += ' </ul>\n'; template += '</div>\n'; this.template = template; }); })(); .form-field { padding-bottom: 10px; } .form-field span { width: 70px; display: inline-block; } .compiled-template { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; } .compiled-template textarea { background-color: #eee; margin-right: 10px; } .compiled-template .output { border: 1px solid #ccc; padding: 10px; } <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script> <div ng-app="soApp"> <div ng-controller="MainCtrl as main"> <div class="form-field"> <span class="form-label">Name:</span> <input type="text" ng-model="main.data.name" /> <br/> </div> <div class="form-field"> <span class="form-label">Template:</span> <textarea ng-model="main.template" cols="40" rows="8"></textarea> <br/> </div> <div> <show-compiled-template data="main.data" template="main.template" /> <div> </div> </div> This can be done by providing a template for your directive as shown below.
app.directive('compile', function($compile) { return{ restrict: 'A', template: '<p>List of products from {{supplier.name}}</p> <p ng-repeat="ref in refs">{{ref}}</p>' scope: { refs: '=' supplier: '=' }, link: function(scope, element, attrs) { # Your code goes here } } })