Search code examples
javascriptangularjsangular-directive

Angular two-way databinding on empty array not working


I have a directive for a popup bubble, where the buttons to be displayed in the popup are provided in an attribute as an array of objects:

JS (Controller)

$scope.buttons = [{ label: 'OK' }, { label: 'Cancel' }]

The directive looks like this:

HTML

<ui-popup buttons="buttons"></ui-popup>

JS (Directive)

'use strict';

angular
    .module('ui')
    .directive('uiPopup', [function () {

        return {
            replace: true,
            restrict: 'E',
            transclude: true,
            templateUrl: '/uikit/views/ui-popup.html',
            scope: {
                buttons: '=',
                anchor: '@',
                x: '@',
                y: '@'
            },
            link: function ($scope, element, attrs, ngModel) {
                ...
            }
        };
    }]);

This works fine. However, what I need is to start with no buttons in the bubble, then add the buttons when an event in the application occurs. So I start with an empty array, and use $scope.buttons.push(obj) to add each button. Note I'm maintaining the original array, not replacing the array, so data-binding shouldn't be broken. However, the new button doesn't show up in the bubble, and debugging shows the button isn't added in the directive scope.

After experimenting, I found by chance that if I start with a non-empty array and then add it works just fine. Why does angular data-binding break on an empty array? What can I do to work around the issue?

Edit

The event is called on ng-click and looks like this:

JS (Controller)

$scope.onClick = function () {

    $scope.locked = true;
    $scope.buttons.push({ label: 'OK' });
};

Solution

  • The directive HTML looked like this:

    ...
    <div class="buttons" ng-if="buttons">
        <ui-button color="primary" size="small" ng-repeat="button in buttons">{{ button.label }}</ui-button>
    </div>
    ...
    

    You can see the buttons in the popup are only shown if buttons is truthy. Interestingly, the div.buttons wasn't showing when buttons was empty, even though !![] gives true in javascript, so I can only assume Angular uses its own equality function to test here. However, even when a button was subsequently added to the array, the div wasn't showing.

    When I changed the equality to ng-if="buttons.length", (as it probably should have been from the start) things worked correctly.