Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS : Two Way Data Binding in a Custom Directive Not Working

I'm having a problem trying to grasp why two way data binding is't working in my custom directive?

The codez:

Directive code:

.directive('invoiceFilter', function () {
    return {
        restrict: 'E',
        replace: true,
        templateUrl: '_invoice-filter.tpl.html',
        scope: {
            selectedItem: '=',
            selectedItemChange: '&'
        },
        link: function(scope) {

            scope.items = {
                all: 'Show all invoices',
                draft: 'Show drafts only',
                open: 'Show open invoices',
                paid: 'Show paid invoices'
            };

            scope.raiseSelectedItemChange = function (key) {


                alert('Selected item in directive: ' + key + " (which seems to work!)");

                scope.selectedItem = key;
                scope.selectedItemChange();

            };

        }
    };
});

Directive template

<div class="btn-group dropdown">
    <button class="btn dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown">
        {{ items[selectedItem || 'open' ] }}
        <span class="caret"></span>
    </button>
    <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
        <li ng-repeat="(key, value) in items">
            <a href="javascript:void(0)" ng-click="raiseSelectedItemChange(key)">{{ value }}</a>
        </li>
    </ul>
</div>

As you can see I'm just adding some data to the scope (in the link function) and relying on the behavior of ng-repeat and ng-click to raise an event/callback when an item is selected. The alert correctly displays the selected item.

But when I start using the directive like this:

HTML

  <body ng-controller="MainController">
    <h1>Hello Plunker!</h1>
    <invoice-filter selected-item="filter" selected-item-change="filterChange()"></invoice-filter>
  </body>

Controller

    .controller('MainController', function($scope) {

        $scope.filter = "all";

        $scope.filterChange = function() {

          alert("Selected item in controller: " + $scope.filter + " (does not work, databinding problem???)");

        };

    })

The $scope.filter value never gets updated with the item I selected in the directive, even though I specified '=' on the directive scope which to my understanding should enable two way data binding, right?

What am I missing here?

Plunk playground

Here's a plunkr with the setup described above, so you can verify that it doesn't work :o/

Thanks to anyone who can help!

like image 469
Dave Avatar asked Dec 26 '22 08:12

Dave


1 Answers

The two-way binding works, but happens on the next cycle. That's why when you reproduce it again, the value of the last cycle is shown. This is because AngularJS has no chance to run its data binding inbetween the line where you set the value and the line where you call the callback. JavaScript, in that sense, doesn't allow intervention, yet.

Changing scope.selectedItemChange(); to $timeout(scope.selectedItemChange); is an easy fix (don't forget to inject $timeout), by forcing the callback to be called on the next cycle.

like image 196
Steve Klösters Avatar answered Dec 28 '22 07:12

Steve Klösters