Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angularjs - ngRepeat with ngInit - ngRepeat doesn't refresh rendered value

Tags:

angularjs

I have array which is displayed using ngRepeater but with this directive I'm using ngInit directive which execute function which should return object to be displayed. Everything works perfectly but when I added "New" button where I add new value to array then function "SetPreview" is executed only once I think function should be executed depending from the amount of array value. How can I do that?

UI:

<body ng-app>

  <ul ng-controller="PhoneListCtrl">
      <button ng-click="add()">Add</button>
    <li ng-repeat="phone in phones" ng-init="displayedQuestion=SetPreview(phone);">
      {{displayedQuestion.name}}
      <p>{{displayedQuestion.snippet}}</p>
    </li>
  </ul>
</body>

Controller:

function PhoneListCtrl($scope) {
  $scope.phones = [
    {"name": "Nexus S",
     "snippet": "Fast just got faster with Nexus S."},
    {"name": "Motorola XOOM™ with Wi-Fi",
     "snippet": "The Next, Next Generation tablet."},
    {"name": "MOTOROLA XOOM™",
     "snippet": "The Next, Next Generation tablet."}
  ];

    $scope.add = function(){
        this.phones.push({name:'1', snippet:'n'});
    };        
    $scope.SetPreview = function(phone)
    {
        //here logic which can take object from diffrent location
        console.log(phone);
        return phone;
    };
}

Sample is here - jsfiddle

Edit:
Here is more complicated sample: -> Now collection of Phones is empty, when you click Add button new item is added and is set as editable(you can change value in text field) and when ngRender is executed SetPreview function returns editable object(it’s work like preview). Now try click Add button again and as you can see the editable value of first item is still presented to user, but I want to refresh entire ng-repeater.

like image 725
Adam Łepkowski Avatar asked Mar 12 '13 06:03

Adam Łepkowski


3 Answers

You are running into an Angular performance feature.

Essentially Angular can see that the element in the array ('A' for example) is the same object reference, so it doesn't call ng-init again. This is efficient. Even if you concatenated an old list into a new list, Angular would see that it it the same reference.

If instead you create a new object with the same values as the old object, it has a different reference and Angular re-inits it: Bad example that does what you are looking for: http://jsfiddle.net/fqnKt/37/

$scope.add = function(item) {
    var newItems = [];
    angular.forEach($scope.items, function(obj){
        this.push({val:obj.val});
    },newItems)

    newItems.push({val:item})
    $scope.items = newItems;
};

I don't recommend the approach taken in the fiddle, but rather you should find a different method than ng-init to trigger your code.

like image 54
checketts Avatar answered Nov 16 '22 12:11

checketts


I've found out that you can just replace ng-init with angular expression {{}} in a hidden block:

<body ng-app>

<ul ng-controller="PhoneListCtrl">
    <button ng-click="add()">Add</button>
    <li ng-repeat="phone in phones">
      <span style="display: none">{{displayedQuestion=SetPreview(phone)}}</span>
      {{displayedQuestion.name}}
      <p>{{displayedQuestion.snippet}}</p>
    </li>
  </ul>
</body>
like image 32
Undefitied Avatar answered Nov 16 '22 11:11

Undefitied


If you have an ngInit in an element, and an ngRepeat in a descendant, the ngInit will only run once.

To fix that, add an ngRepeat to the element with the ngInit, with an expression that repeats once and depends upon the same array (and other dependencies).

For example:

ng-repeat="_ in [ products ]" ng-init="total = 0"
like image 3
Aaron Queenan Avatar answered Nov 16 '22 12:11

Aaron Queenan