Compiling dynamic content - AngularJS

I am rewriting this question as I think the original was not too clear.

Basically, I have a 'wrapper' directive where I am trying to dynamically add attributes to one of the transcluded elements. I can make this work, but Angular does not seem to be aware of the new attributes after adding.

If I use $compile , then Angular recognizes them, but by double compiling the translated content, in which case it doubles the number of options in the select tag.

Here is a plunker that shows (with comments) what I'm trying, and the same code follows below for those who can see the code and offer an answer just by looking: (note - my final goal is to check the custom valid-form-group for the required attribute, and if found to apply to the contained select tag)

HTML

 <body ng-controller="MainCtrl"> <form name="validationForm" novalidate> <valid-form-group class="form-group" ng-class="{'has-error': validationForm.validInfo.$error.required}" required> <select ng-model="data.option" ng-options="option.id as option.message for option in selectOptions" name="validInfo" id="validInfo"> <option value="">-- Select a Question --</option> </select> </valid-form-group> </form> </body> 

Js

 var app = angular.module('plunker', []) .controller('MainCtrl', function($scope) { $scope.selectOptions = [ {id: 1, message: 'First option'}, {id: 2, message: 'Second option'}, {id: 3, message: 'Third option'} ]; }) .directive('validFormGroup', function($compile) { return { restrict: 'E', template: '<div><span ng-transclude></span></div>', replace: true, transclude: true, require: '^form', link: function(scope, element, attrs, ctrl) { if (attrs.required !== undefined) { var selectElement = angular.element(element.find('select')); // either of the below produce the same results selectElement.attr('ng-required', true); //selectElement.attr('required', true); // if the below is commented out it wont validate // BUT if it is left in it will recompile and add another 3 options $compile(selectElement)(scope); } } }; }); 

CSS

 .has-error{ border: solid 1px red; } 

Note that the example here uses " required " (or ng-required ) as an added attribute to emphasize the fact that Angular will not recognize it if it is not compiled.

Any help or comments are welcome - a little disappointed that I can't get this to work, so maybe there is something fundamental that I am missing ...

plunker should help in visualizing my problem.

edit - apologies for the delay in responding to replies and comments. As mentioned in a comment or two below, personal problems did not allow me to find the time to investigate.

+7
javascript html angularjs
source share
5 answers

Try this simple directive:

 .directive('validFormGroup', function($compile) { return { restrict: 'A', replace: false, require: '^form', compile: function (element, attr) { if (attr.required !== undefined) { var selectElement = element.find('select'); // either of the below produce the same results selectElement.attr('ng-required', true); //selectElement.attr('required', true); } } }; }); 

And use it as an html attribute:

 <div valid-form-group class="form-group" ng-class="{'has-error': validationForm.validInfo.$error.required}" required> <select ng-model="data.option" ng-options="option.id as option.message for option in selectOptions" name="validInfo" id="validInfo" > <option value="">-- Select a Question --</option> </select> <br/> <br/>Required invalid? {{validationForm.validInfo.$error.required||false}} <br/> <br/> </div> 

Demo

Explanation

  • I don’t use transclude at transclude in this solution, since the purpose of this directive is to simply change the html before compiling with scope, there is no need to translate content that is overly complex.

  • Here I am processing the compile function instead of the link function. compile function is a great place for you to modify the html before referencing the area .

+2
source share

I can only guess what you see is the result of double compilation during the process of initializing directives, so you see double sets of parameters.

You can solve this problem by wrapping the compilation inside $ timeout, which will ensure compilation outside the directive initialization. Here's a working demo and below the directory code:

 .directive('validFormGroup', function($compile, $timeout) { return { restrict: 'E', template: '<div><span ng-transclude></span></div>', replace: true, transclude: true, require: '^form', link: function(scope, element, attrs, ctrl) { if (attrs.required !== undefined) { var selectElement = angular.element(element.find('select')); $timeout(function(){ selectElement.attr('ng-required', true); $compile(selectElement)(scope); }); } } }; }); 

PS You can achieve a similar bootstrap shell function by using an isolated scope in your directive, and then checking to see if your highlighted input / selection element has the required set of attributes. Define a function in an isolated area to check for errors and bind this function to the error group of the ng class of the form. This way you do not need to use $ timeout at all.

+1
source share

I would like to propose here a different approach, which I adapted for dynamic validation. adding validation dynamically to the field reduces the HTML code of the template for each field, I wrote a directive for a similar purpose. see plunker link, e.g. directive ... PLUNKER

I wrote such directives for all types of fields: number, text, select, textarea, bool, datepicker, etc. .... the attached plunker gives you an example for a text field and number.

Angular magic happens using the bottom liner:

  var newElem = angular.element(template); element.replaceWith(newElem); $compile(newElem)(scope); 

all other code is just some logical part of if else.

+1
source share

In this case, you do not need to use the $ compilation. I just changed the code to solve your problem. I try to stay as close as possible to the original version to help you understand.

Javascript (added isRequired scope variable based on attrs.required)

  .directive('validFormGroup', function($compile) { return { restrict: 'E', template: '<div><span ng-transclude></span></div>', replace: true, transclude: true, require: '^form', link: function(scope, element, attrs, ctrl) { if (attrs.required !== undefined) { //added isRequired scope.isRequired = true; var selectElement = angular.element(element.find('select')); // either of the below produce the same results //selectElement.attr('ng-required', true); //selectElement.attr('required', true); // if the below is commented out it wont validate // BUT if it is left in it will recompile and add another 3 options //remove $compile //$compile(selectElement)(scope); } } }; }); 

HTML (added ng-required = isRequired)

 <select ng-model="data.option" ng-options="option.id as option.message for option in selectOptions" name="validInfo" id="validInfo" ng-required="isRequired"> 

You can link to the plunkr version at http://plnkr.co/edit/BGQo05mTNr1H1HjFXSpf?p=preview

I think this will answer your question. If you have a more complex scenario, share it.

Updated: (after reading other comments and answers again) If you need dynamic HTML content, you can use this solution - Compiling dynamic HTML strings from a database

Another way is to remove the ng- * attribute to prevent recompilation. plunkr version at http://plnkr.co/edit/JpfdvISCZ39heuUfdHt3?p=preview

  selectElement.removeAttr('ng-options'); selectElement.removeAttr('ng-model'); 
0
source share

I do not have a complete answer, however, if you give your directive a single scope of the scope: {}, then on a double compilation it will not be able to get the parameters, so it does not work out quietly - I tried this in your plunkr, and I'm in the drop-down list only one set of options appeared.

I said that I don’t have a competing answer - I don’t believe that it is - it seems like a hack, and I think that there are things you need to share with the directive in which you could inherit them yourself through a legacy area so you can maintain functionality, but as I said, it feels hacked and doesn't feel good, because it does not address the fundamental issue of how best to deal with dual compilation.

0
source share

All Articles