24

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.

Adam Łepkowski
  • 2,048
  • 1
  • 24
  • 41
  • When you press Add button how many times SetPreview is executed? Initially should be 4 times but is executed only once. Look at Console window;) – Adam Łepkowski Mar 12 '13 at 07:05
  • each `add` press causes 1 new entry in the console – Arun P Johny Mar 12 '13 at 07:07
  • Exactly but I want to refresh entire ng-repeater then amount of entries in the console should be equal amount of array items. My function SetPrieview can return edited object (it's not implemented in this example) and it's rendered, but when user push Add button then he resign of editing and original value should be displayed but currently only new record is added and edited value is still displayed. I think I must enforce refreshing entire ngRepeat. If you want I can create entire example with editing stuff – Adam Łepkowski Mar 12 '13 at 07:16
  • yes, it will be better if you can share the entire sample – Arun P Johny Mar 12 '13 at 07:19
  • I've edited question I hope now it’s more understandable – Adam Łepkowski Mar 12 '13 at 07:52
  • why are you keeping a different copy for editing, it is not the correct way I think. Is there any particular reason for it – Arun P Johny Mar 12 '13 at 08:22
  • Because there will be 2 buttons Save and Cancel for edit form and when user presses Save button then data will be saved - that way I create object copy. – Adam Łepkowski Mar 12 '13 at 08:39

3 Answers3

17

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.

Boaz
  • 19,892
  • 8
  • 62
  • 70
checketts
  • 14,167
  • 10
  • 53
  • 82
11

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>
Undefitied
  • 747
  • 5
  • 14
3

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"
Aaron Queenan
  • 851
  • 8
  • 14