thatandrey
thatandrey

Reputation: 287

ng model inside ng repeat is conflicting with scope model somehow, isn't updating

I'm not sure exactly what the issue is, but something weird is going on. I have a contenteditable directive that lets me assign an ng-model to a div (which I got from the some angular documentation), which I'm using to save the changes made in the div.

In my html, I have: (I'm linking the textarea to the div so I can confirm the binding is working)

<button ng-click="addPage()">Add Page</button>

<div ng-repeat-start="item in book" ng-model="item.v" contenteditable></div>
<textarea ng-model="item.v" ng-repeat-end></textarea>

The problem is that the ng-repeat produces an empty div/textarea even though I specified a base model in my controller:

  $scope.book = [{v: "asd", t: "test"}];
  $scope.addPage = function(){
     $scope.book.push({v: "", t: "test"});
  };

Shouldn't the div be loaded with the value "asd"? The curious thing is when I use the console log to see $scope.book, It has the correct value for the t property, but not thev property. When I change the html code from item.v to item.t, then the reverse happens in the console log, and it's the "t" property that gets overwritten.

It's almost as if $scope.book is being overwritten by the ng-repeat. After the page has loaded, I can add as many of these divs as I want, type in them, and then when I check the console log, there are being saved to $scope.book correctly. But something seems to be happening initially.. Any help?


Solution As pointed out in the comments, the problem was with the content editable directive, which you can see in action over here. Specifically, I don't know why exactly and I'd love to find out, but the error occurs here:

        read(); // initialize

        // Write data to the model
        function read() {
          var html = element.html();
          // When we clear the content editable the browser leaves a <br> behind
          // If strip-br attribute is provided then we strip this out
          if ( attrs.stripBr && html == '<br>' ) {
            html = '';
          }
          ngModel.$setViewValue(html);
        }

$setViewValue is overwriting the ngModel somehow, so all I had to do (as suggested by New Dev) was comment out the read() function and it works great now.

Upvotes: 1

Views: 454

Answers (1)

New Dev
New Dev

Reputation: 49580

Input directives - both built-in and custom - require: "ngModel". That is to say that they use the ngModel pipeline to marshal data between the Model and the View through a pipe of $formatters, $parsers and $validators.

An input control (doesn't have to be <input>) would call $setViewValue when it detects that its "View" state has changed (typically a DOM interaction has happened, but could be for whatever reason - could be based on $interval).

An input control would also define ngModel.$render to render its DOM in response to a change coming from the Modal side.

And typically, the Model takes precedence over the View, because ultimately, the View should be driven by the Model (i.e. ViewModel).

This doesn't happen with your contenteditable. In fact, it calls read() to "initialize" based on its View data. Since it is empty, it sets (via `$setViewValue - something one would do when a user has interacted with the control) the ngModel-bound value to be empty.

Removing the call to read() fixes the issue.

Upvotes: 2

Related Questions