0

I'm relatively new to Angularjs and I'm writing an app. The app has an array of objects, each of the objects has a private variable called status. I have two filters, which return a subset of these objects based on their status. As an example, the statuses corresponding to these two filters are 'NEW' and 'OLD' respectively.

Next, I've written a directive that makes the divs formed by these objects, draggable. Basically, what the directive does is receive the object through a two way data binding, and then changes the status of the object by calling the corresponding method.

The problem is, the changes in the status of the object doesn't update the filter instantly. For example, when I drag three of the divs to the other half, their status gets updated, but the filter doesn't.

Here's the directive:

.directive('draggable', function ($document) {
return {
    scope: {
        bill: '=' // the bill object
    },
    restrict: 'A',
    link: function (scope, element, attr) {
        // do something here
        console.log('bill', scope.bill);
        var startX = 0,
            startY = 0,
            x = 0,
            y = 0,
            sourceX = 0,
            sourceY = 0,
            windowWidth = 0;

        element.on('mousedown', function (event) {
            event.preventDefault();
            startX = event.pageX - x;
            startY = event.pageY - y;
            $document.on('mousemove', mousemove);
            $document.on('mouseup', mouseup);
            windowWidth = $(window).width();
            sourceY = event.pageY;
            sourceX = event.pageX;
        });

        function mousemove(event) {
            y = event.pageY;
            x = event.pageX;
            element.css({
                top: y + 'px',
                left: x + 'px'
            });
        }

        function mouseup(event) {
            $document.unbind('mousemove', mousemove);
            $document.unbind('mouseup', mouseup);
            console.log('mouseup', startX, startY, event.screenX, event.screenY);
            var mid = windowWidth / 2;

            if (sourceX < mid && event.pageX >= mid) {
                // yes there was a change of sides
                scope.bill.markCooking(); // change the status to COOKING
                console.log('moved to COOKING', scope.bill.getStatus());
            } else if (sourceX >= mid && event.pageX < mid) {
                scope.bill.enq(); // change the status to NEW
                console.log('moved to ENQ', scope.bill.getStatus());
            }
        }
    }
}})

What am I doing wrong here?

1
  • 1
    Your will need to update the angular variable inside scope.apply Commented Mar 10, 2014 at 14:22

1 Answer 1

3

Any time that you're in a browser event handler you are effectively outside of Angular's lifecycle. The user's actions have triggered the event, but Angular doesn't know that it needs to check and update its bindings (in this case, the bill status).

Calling scope.$apply() manually triggers Angular's change detection, and will update your bindings:

scope.$apply(function() {
  if (sourceX < mid && event.pageX >= mid) {
    // yes there was a change of sides
    scope.bill.markCooking(); // change the status to COOKING
    console.log('moved to COOKING', scope.bill.getStatus());
  } else if (sourceX >= mid && event.pageX < mid) {
    scope.bill.enq(); // change the status to NEW
    console.log('moved to ENQ', scope.bill.getStatus());
  }
});

You can read more about scopes and their lifecycle here: http://docs.angularjs.org/guide/scope

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

6 Comments

Any time that you're in a browser event handler you are effectively outside of Angular's lifecycle. That's only true if you don't use Angular's supported ng-* events.
When in doubt, and you're not seeing the view updated, you can always try to throw in a call to apply() to see if it fixes the problem. Angular will yell at you if you are already in a digest cycle, so you will know if that's the problem or not. It sounds like it is here.
Works like a charm, and thanks for the resource! Adding as the answer.
@thescientist absolutely, I should have been more clear. Manually-bound events that don't use Angular directives will require $apply() to update scope.
@Ashesh depends on how you're creating the directives. An element can only have one isolate scope. Maybe take a look at this answer: stackoverflow.com/questions/20609770/…
|

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.