AngularJS - Unable to change directive template dynamically in communication function

Beware

The decision made will break the directive with replace: true : HTML will not be replaced, the special CSS selector will no longer work, etc.


I want my directive to dynamically change its template, observing the string received as an attribute from the parent controller, so I used $compile from this answer along with $observe from this small part of an interesting tutorial , but alas, it does not work as shown in this plunkr .

About error

If jQuery is enabled before AngularJS in scripts, calling replaceWith causes me the following error:

 TypeError: Cannot read property 'ownerDocument' of undefined 

But , if I remove jQuery, forcing AngularJS to use its jqLite, the same part generates this error, making things clearer for a general jQuery agnostic like me:

 TypeError: Failed to execute 'replaceChild' on 'Node': parameter 1 is not of type 'Node'. 

Even if it is clear to me that I am not passing a valid Node-type object to replaceWith , I don’t know how to handle this situation, as I expected $compile to do the job.

The only thing I know is what console.log(tplContent) looks like console.log(tplContent) I right promise?):

 Object { config: Object data: "<script type="text/ng-template" id="templateId.html"> ↡ <p>TEMPLATE A</p> ↡</script>" headers: function (d) ng339: 10 status: 200 statusText: "OK" } 

while console.log($compile(tplContent)(scope)) returns an array with the same object as the first and only element:

 [Object] 0: { config: Object data: "<script type="text/ng-template" id="templateId.html"> ↡ <p>TEMPLATE A</p> ↡</script>" headers: function (d) ng339: 10 status: 200 statusText: "OK" }, length: 1 

I really want to avoid using one of the following two backups, do you have any idea what I'm doing wrong here?


Failures aka don't tell me about it

I know that I could split the directive into two directives and ng-if them as follows:

 (function() { 'use-strict'; angular.module('app') .directive('dynamicTemplateA', dynamicTemplate); DynTplCtrl.$inject = ['$http', '$templateCache', '$compile', '$parse']; function dynamicTemplate($http, $templateCache, $compile, $parse) { var directive = { restrict: 'E', templateUrl: 'template-a.html', scope: {}, bindToController: { tpl: '@', i: '=' }, controller: DynTplCtrl, controllerAs: 'dyntplctrl', link: linkFunc } return directive; function linkFunc(scope, el, attrs, ctrl) {} } DynTplCtrl.$inject = []; function DynTplCtrl() {} })() (function() { 'use-strict'; angular.module('app') .directive('dynamicTemplateB', dynamicTemplate); DynTplCtrl.$inject = ['$http', '$templateCache', '$compile', '$parse']; function dynamicTemplate($http, $templateCache, $compile, $parse) { var directive = { restrict: 'E', templateUrl: 'template-b.html', scope: {}, bindToController: { tpl: '@', i: '=' }, controller: DynTplCtrl, controllerAs: 'dyntplctrl', link: linkFunc } return directive; function linkFunc(scope, el, attrs, ctrl) {} } DynTplCtrl.$inject = []; function DynTplCtrl() {} })() 

and then in controller.html :

 <div ng-repeat="i in [1,2,3]"> <dynamic-template-a ng-if="mainctrl.tpl === 'a'" tpl="{{mainctrl.tpl}}" i="i"></dynamic-template-a> <dynamic-template-b ng-if="mainctrl.tpl === 'b'" tpl="{{mainctrl.tpl}}" i="i"></dynamic-template-b> </div> 

I also know that I can use ng-include like this :

 (function() { 'use-strict'; angular.module('app') .directive('dynamicTemplateA', dynamicTemplate); DynTplCtrl.$inject = ['$http', '$templateCache', '$compile', '$parse']; function dynamicTemplate($http, $templateCache, $compile, $parse) { var directive = { restrict: 'E', template: '<div ng-include="dyntplctrl.getTemplateUrl()"></div>', scope: {}, bindToController: { tpl: '@', i: '=' }, controller: DynTplCtrl, controllerAs: 'dyntplctrl', link: linkFunc } return directive; function linkFunc(scope, el, attrs, ctrl) {} } DynTplCtrl.$inject = []; function DynTplCtrl() { var vm = this; vm.getTemplateUrl = _getTemplateUrl; function _getTemplateUrl() { return 'template-' + vm.tpl + '.html'; } } })() 
+5
source share
2 answers

Loans to this issue .

You need to change your code a bit when replacing the template:

 el.html(tplContent.data); $compile(el.contents())(scope); 

This will replace the contents of the element (although you need to handle sanitation here), and then compile the template in the directive area.

In addition, for testing, I removed the <script> tags from template-a.html and template-b.html .

Here is a forked plunker that has the changes mentioned above.

+2
source

You do not need to put the HTML tag in the script tag. Just save plain HTML in your files like

template-a.html

 <p>TEMPLATE A</p> 

And change your code a bit to achieve what you want.

  function(tplContent) { var content = $compile(tplContent.data)(scope); if(el[0].childNodes.length){ el[0].removeChild(el[0].childNodes[0]); } el.append(content); } 
0
source

Source: https://habr.com/ru/post/1266370/


All Articles