Search code examples
javascriptangularjsangularjs-directiveangularjs-ng-repeat

How can I get my nested ng-repeat form inputs to stop updating each other?


My input fields are updating each other!

There's clearly some pass-by-reference shenanigans here, but I can't figure it out.

I have a custom directive that I'm setting up with ng-repeat:

<my-dir ng-repeat="obj in mio" obj="obj" other="otro" unique-name="_{{$index}}"></my-dir>

This directive contains a form whose inputs are also ng-repeating directives. (Each instance of the form needs the same set of inputs.)

Things I have done:

  • Given each my-dir form and each input element within it a unique id
  • Tried to use a composite track by in the nested ng-repeat (i.e. track by uniqueName + input.varname), but couldn't seem to pass in variables from the parent scope
  • Wrapped the nested ng-repeat in an ng-if to see if that created a convenient isolate scope

My input fields are still updating each other.

Additional code

in my-dir.html:

<form name="{{uniqueName}}">
  <parameter-directive
    ng-repeat="input in otro.inputs"
    type="input.vartype"
    name="input.varname"
    id-key="{{uniqueName}}"
    ng-model="input">
  </parameter-directive>
</form>

and in parameter-directive.html:

<label for="{{name}}" class="pull-left">{{name}}</label>
<input
  ng-required="{{defaultValue == null}}"
  id="{{idKey + '_' + name}}"
  ng-model="ngModel.val"
  ng-attr-type="{{inputType}}"
>

(Happy to also include code from the directive js, but this question is already quite long.)


Solution

  • Turns out the issue had nothing to do with the directives lacking unique ids.

    The issue was that the parameter-directive's ng-model was bound to the same object from the parent directive (in the example above, the object passed in with other="otro") and passed by reference.

    The otro object also happens to be loaded from an API, so I ended up adding a broadcast event:

    in my-dir.directive.js:

    $scope.getOtro = function getOtro() {
      $http.get(URL)
        .then((resp) => {
          $scope.otro = resp.data;
          $scope.$broadcast('otro-loaded', $scope.otro.inputs);
          }
      };
    

    in parameter-directive.directive.js:

    $scope.$on('otro-loaded', (_, inputs) => {
        $scope.inputs = angular.copy(inputs);
    });
    

    I will not claim that this is a particularly elegant way to solve this problem, but it did the trick.