Idkt
Idkt

Reputation: 2996

AngularJS : Two way binding inside directives

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?

Upvotes: 0

Views: 250

Answers (1)

c0bra
c0bra

Reputation: 3012

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

Upvotes: 3

Related Questions