26

I have a bunch of connected ul lists in my view which are using a jQuery UI: Sortable directive to facilitate drag and drop and reordering of the list items.

Changes I make via jQuery UI's drag and drop I apply to the $scope using the $apply function, this part works…

The issue I am running into now however is that on drop into some of these lists I provide a custom form that the user needs to fill out.

The user has the option to:

  1. fill out the form and continue which then does the $apply call to persists the data into the $scope
  2. click a cancel button which instead of calling the $apply to store info, should revert the last drag/drop interaction effectively 're-rendering' all my lists to reflect the data that is still in the $scope at this stage (since the latest drag had not had any effect on it yet).

The effect of this "cancel" button is effectively reverting everything to the point before the user picked up the list item and dragged it into another list.

How can I force a 'refresh' or 're-render' of my ng-repeats so that they visually refresh and show the current $scope data again?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jannis
  • 17,025
  • 18
  • 62
  • 75
  • 3
    Normally, just changing the data model ($scope) will work, if done inside AngularJS. If done outside, calling $scope.$apply() should trigger the update/refresh. At the end of your cancel manipulations, are you calling $scope.$apply(), or wrapping your changes in a $scope.$apply(function() { ... changes here... }) ? – Mark Rajcok Sep 26 '12 at 14:55
  • I am indeed calling `$scope.apply()` from inside the jQuery based directive but it doesn't seem to force a reload/re-render of the existing data. Probably because Angular thinks everything is exactly the same, not knowing about me having done DOM manipulation without telling it about it. So what I really need is just a 'force refresh' type of call, where Angular regardless of `$scope` state just forces a refresh of its rendered data. – Jannis Sep 26 '12 at 19:41
  • Well, as a possible workaround, what if you remove the dragged item from $scope inside your call to $scope.$apply() -- Angular should see that and rerender -- then on the next line (still inside your $scope.$apply() call) add it back again using $timeout(): `$timeout( function(){ ...add removed item back to $scope here... });` The $scope manipulation you perform in the the $timeout function should cause a second render to happen. – Mark Rajcok Oct 02 '12 at 21:00
  • Nice idea, a bit unfortunate having to re-render twice but yea that might work. Will give it a go later, thanks for your comment. – Jannis Oct 03 '12 at 01:05

1 Answers1

23

When the user starts filling out the form, I would set

$scope.oldData = angular.copy($scope.data);

Then let the user edit $scope.data using the form as he likes.

Then if the user presses cancel, just set $scope.data = $scope.oldData.

Andrew Joslin
  • 43,033
  • 21
  • 100
  • 75
  • I used this way, but I needed to call $apply twice; scope.oldData = angular.copy(scope.irs); scope.irs.length = 0; scope.$apply(); scope.irs = scope.oldData; scope.$apply(); – Aladdin Mhemed Mar 01 '13 at 06:32
  • It's a great solution. It helped me trigger a re-rendering of a ng-repeat where the data itself did not change but had to get forcefully re-rendered. I only did a $scope.data[0] = angular.copy($scope.data[0]); instead of copying the entire array. Would be nice to know if there is an angularjs function for this re-render triggering stuff; – Daniel F Sep 21 '13 at 09:42
  • 3
    By default, angular will track if an ng-repeat element needs to get re-rendered by putting a `$$hashKey` variable on each object in the repeater. If the `$$hashKey` changes on one of the indexes, it re-renders that. A hashKey could change through an element moving in the array, being removed, or a new one being added. You can tell angular what property to track to see if it should re-render by doing `ng-repeat="item in items track by item.id"` or the like - then if `item.id` changes at an index, it will re-render. – Andrew Joslin Sep 24 '13 at 15:41