3

I'm building a simple app with AngularJS. The app make a async AJAX call to the server and the server returns an array like this:

{
    paragraphs: [
        {content: "content one"},
        {content: "cnt two"},
        {content: "random three"},
        {content: "last one yeeaah"}
    ]
}

So I'm setting this content to the StorageService factory via my set method. Everything is fine here.

I'm using ng-repeat to render the results and JQuery UI sortable to be able to change the order of the elements. When an item is swapped my script is calling the StorageService.swap method and the element order in StorageService is updated, BUT ng-repeat isn't rerendering the change, but if I remove/add or change the content it's working. How I can force the angular to rerender the ng-repeat?

= JSFIDDLE =

http://jsfiddle.net/6Jzx4/3/

= Example =

Example

When a swap occurs the ng-repeat should be rerendered, so the IDs are consecutive

= Code =

HTML

<div ng-controller="Test" sortable>
    <div ng-repeat="item in data().paragraphs" class="box slide_content" id="{{$index}}"> 
        {{item.content}}, ID: {{$index}}
    </div>

    <input type="button" ng-click="add()" value="Add">
</div>

JS

var App = angular.module("MyApp", []);

App.controller("Test", function($scope, StorageService) {
    StorageService.set({
        paragraphs: [
            {content: "content one"},
            {content: "cnt two"},
            {content: "random three"},
            {content: "last one yeeaah"}
        ]
    });

    $scope.data = StorageService.get;

    $scope.add = StorageService.add;
});

App.directive("sortable", function(StorageService) {
    return {
        link: function(scope, element, attrs) {
            $(element[0]).sortable({
                cancel: ".disabled",
                items: "> .slide_content:not(.disabled)",
                start: function(e, t) {
                    t.item.data("start_pos", t.item.index());
                },
                stop: function(e, t) {
                    var r = t.item.data("start_pos");
                    if (r != t.item.index()) {
                                    StorageService.sort($(this).sortable("toArray"));
                    }
                }
            });
        }
    };
});

App.factory('StorageService', function() {
    var output = {};

    return {
        set: function(data) {
            angular.copy(data, output);
            return output;
        },
        get: function() {
            return output;
        },
        add: function() {
            output.paragraphs.push({
                content: 'Content'
            });
        },
        sort: function(order) {
            var localOutput = [];

            for (var j in order) {
                var id = parseInt(order[j]);
                localOutput.push(output.paragraphs[id]);
            }

            console.log('new order', localOutput);

            output.paragraphs = localOutput;
            return output;
        }
    };
});
9
  • I don't see the relevant code in the jsfiddle...it also should be here in the post itself. I imagine it's because you didn't correctly utilize promises or you somehow ended up needing a $scope.$apply(). Commented Dec 28, 2013 at 23:21
  • what is demo supposed to show us? Hard to help if demo doesn't replicate problem listed Commented Dec 28, 2013 at 23:30
  • @charlietfl well look the picture below "Example", it's explaining my problem. When a swap occurs ng-repeat isn't rerendered for some reason. See the middle picture ( after swap ), notice the second element has ID:2 and the third has ID:1, it should be visa-versa as showed in the picture 3. Commented Dec 29, 2013 at 0:02
  • 1
    The proximal issue is the need for $apply but then it looks like there are a number of other incompatibilities between jquery sortable and Angular. So I'd recommend looking at Angular UI sortable It'll take some restructuring to get it going but then you should have something much more compatible. Commented Dec 29, 2013 at 1:43
  • 1
    Great- glad that worked for you Commented Dec 29, 2013 at 17:02

1 Answer 1

4

Angular doesn't know you've changed the array. Executing your sort inside a scope.$apply() will address that.

Note that I've added a that variable since this changes meaning inside the apply.

var that = this;
scope.$apply(function() {   
   StorageService.sort($(that).sortable("toArray"));
}

But that fix uncovers other problems that appear to be caused by the interaction between the jQuery sortable and Angular (here's a fiddle that shows an attempt at resolving the problems but still has issues). These issues have been solved in Angular UI Sortable. So a good path forward may be to switch to this.

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

2 Comments

Thanks for the answer. I found a few problems tho. Go to the fiddle, swap second field with third, press new: i.imgur.com/6fgbaRd.png , Another one: Swap last with first, press new, see what happens.
Right you'll need to update add to consider position. Perhaps the other functions too- but you get the concept. Here's one way to update add: jsfiddle.net/6Jzx4/5

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.