1

I wrote a simple directive to be able to dynamically set the value of ng-model, as described in this other Stack Overflow question: AngularJS - bind ng-model to a variable which name is stored inside another variable. This works great, except when I use it on an element that also has an ng-repeat. It gets rendered too many times and the DOM does not look correct.

Directive:

angular.module( 'dynamicModel-directive', [] )
.directive( 'dynamicModel', function( $compile ) {
    return {
        link: function( scope, element, attr ){
            element[0].removeAttribute( 'dynamic-model' );
            element[0].setAttribute( 'ng-model', scope.$eval( attr.dynamicModel ) );
            $compile( element[0] )( scope );
        }
    };
});

HTML (simplified by rendering an input box instead of a button group):

<div ng-if="field.type == 'buttonGroup'">
     <input type="text" ng-repeat="option in field.options"
           dynamic-model="field.name" class="form-control">
</div>

The above code results in four input boxes being displayed, even though there are only two items in field.options.

Here is what the resulting DOM looks like:

<div ng-if="field.type == 'buttonGroup'" class="ng-scope">
<!-- ngRepeat: option in field.options -->
<!-- ngRepeat: option in field.options -->
<input type="text" ng-repeat="option in field.options" class="form-control ng-scope ng-pristine ng-valid" ng-model="application.insured.gender">
<!-- end ngRepeat: option in field.options -->
<input type="text" ng-repeat="option in field.options" class="form-control ng-scope ng-pristine ng-valid" ng-model="application.insured.gender">
<!-- end ngRepeat: option in field.options -->
<!-- end ngRepeat: option in field.options -->
<!-- ngRepeat: option in field.options -->
<input type="text" ng-repeat="option in field.options" class="form-control ng-scope ng-pristine ng-valid" ng-model="application.insured.gender">
<!-- end ngRepeat: option in field.options -->
<input type="text" ng-repeat="option in field.options" class="form-control ng-scope ng-pristine ng-valid" ng-model="application.insured.gender">
<!-- end ngRepeat: option in field.options -->
<!-- end ngRepeat: option in field.options -->

When I comment out the $compile line in the directive it displays the appropriate number of times so I think it is related to that, but I am not sure how to fix it. Any ideas? Thanks!

2
  • Why would you want 2 inputs to have the same ng-model? Is it a radio input? Commented Aug 12, 2014 at 21:22
  • It's a button group that acts as radio buttons using the btn-radio AngularUI directive Commented Aug 12, 2014 at 21:44

1 Answer 1

4

That is because the ng-repeat is got compiled multiple times.

You could set the terminal flag and raise the priority to be higher than ng-repeat to ensure that the ng-repeat will be compiled only once.

.directive('dynamicModel', function ($compile) {
  return {
    terminal: true, // prevent ng-repeat from compiled twice
    priority: 1001, // must higher than ng-repeat
    link: function (scope, element, attrs) {
      attrs.$set('ngModel', scope.$eval(attrs.dynamicModel));
      attrs.$set('dynamicModel', null);
      $compile(element)(scope);
    }
  };
});

PS. There is the attrs.$set().

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

1 Comment

Here is a working example that I was working on before runTarm beat me to it: codepen.io/Chevex/pen/EJsLi

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.