1

I am experimenting with AngularJS 1.3 (I just migrated my app from 1.2). I am playing with the new one-time binding. I have come across this case where it is not working, can someone please explain why? I am dynamically generating an id field by calling a method within the view. As a result, the one-time binding does not seem to be working. See http://plnkr.co/edit/GyfKKb?p=preview.

Specifically, I'm expecting the one-time to not re-render the ng-repeat when a new element is added to the array. But pushing a new element into the array (via button click) also makes the new element appear in the output of the ng-repeat.

index.html

<body ng-controller="MainCtrl">
  <!-- TODO: I would expect addItemC() to do nothing with one time binding -->
  <button ng-click="addItemC()">add</button>

  <div ng-repeat="itemC in ::itemsC">

    <!-- dynamically generate id that is cause breakage -->
    {{::itemC.id() | limitTo:3}}

  </div>      
</div>

app.js

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {

  // generate random string id dynamically (code taken from stackoverlow answer)
  var itemObj = {
    id: function makeid() {
      var text = "";
      var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

      for( var i=0; i < 100; i++ )
          text += possible.charAt(Math.floor(Math.random() * possible.length));

      return text;
    }
  };

  // one time binding does not work
  $scope.itemsC = [
    angular.copy(itemObj),
    angular.copy(itemObj),
    angular.copy(itemObj)
  ];
  $scope.addItemC = function() {
    $scope.itemsC.push(angular.copy(itemObj));
  }
});
5
  • This is a tricky problem. Here's a plunkr demonstrating that if you instantiate the array as empty instead of having a copied value, the array stays one-way bound plnkr.co/edit/jAf3NQOQGLMLMYk30tIa?p=preview Commented Jun 12, 2015 at 17:10
  • @azium Yes I noticed this, which leads me to believe at the empty state the one time binding is applied as the scope variable has 'settled'. So when the array is initiated not empty, it never 'settles' Commented Jun 12, 2015 at 17:17
  • @azium: I think if it is empty, the call to itemC.id() will return nothing since it is undefined. That is why it seems working. See my answer and see if that makes sense. Commented Jun 12, 2015 at 17:20
  • @lookman Ah yes, perfect! There you go OP. Commented Jun 12, 2015 at 17:28
  • @wbeange Is this makes sense? Any comment? Commented Jun 17, 2015 at 19:16

2 Answers 2

2

I think it is because of exceeding number of $digest cycles inside the ng-repeat angular is throwing error. Mostly it is because of the for loop running for 100 iterations for every item.

If you change you logic to generate the id inside the controller and then give the item to ng-repeat to render then it works fine. Take a look at my plunkr

Demo http://plnkr.co/edit/4nFtOy1uGtDyUEWXCVbT?p=preview

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';

  // generate random string id dynamically (code taken from stackoverlow answer)
  function makeid() {
      var text = "";
      var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

      for( var i=0; i < 100; i++ )
          text += possible.charAt(Math.floor(Math.random() * possible.length));

      return text;
    }

  function getItem () {
    return {
      id: makeid()
    };
  }


  var itemObj = {
    id: makeid()
  };


  // one time binding does not work
  $scope.itemsC = [
    getItem(),
    getItem(),
    getItem(),
  ];
  $scope.addItemC = function() {
    $scope.itemsC.push(getItem());
  }

  $scope.removeItem = function(item) {
    var index = $scope.itemsC.indexOf(item);
    $scope.itemsC.splice(index, 1);
  }
});

Inside the markup change itemC.id() to itemC.id since id is now property.

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

5 Comments

So one time binding a method nested inside the ng-repeat is not possible?
I don't understand, you just removed the one-way binding.
@wbeange: I didn't say that. You can very well do that. But the problem here is it is exceeding the number of digest cycles allowed by angular so it is throwing error.
@azium: It removed it by mistake while playing around. I added it back now take a look plnkr.co/edit/4nFtOy1uGtDyUEWXCVbT?p=preview
Ah okay. Can't change my downvote though unless you edit your answer! Silly Stack.
2

I think because you are one time binding the function and calling the function every time. Calling a function inside double curly braces will give whatever the function returns. The function remains the same by keep returning random strings.

So, one time binding actually works here by keep calling the same function. Note that the function does not change. I added new property which is not a function to the object to show you the one time binding actually works.

<div style="font-weight:bold;">itemsC:</div>
<div ng-repeat="itemC in ::itemsC">
  <div ng-click="removeItem(itemC)">
    <!-- dynamically generate id -->
    {{::itemC.name | limitTo:3 }}
  </div>
</div>
<div style="margin-top:10px;">{{itemsC}}</div>

http://plnkr.co/edit/T8KRafey7VJFbcAs4NLZ?p=preview

1 Comment

@wbeange Is this makes sense? Any comment?

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.