0
votes

I'm currently building a modal directive that I can use to populate with some data tables. I have an attribute on the directive called modal-visible that will be a boolean value, defaulting at false. If the default is true, on load, the modal is visible. However, when I initially set it to false and change the value from an event, the attribute changes, but the directive does not pick the change up. I have a watch in the link portion of the directive, but that only picks up the initial change.

Below is my code examples. I just have a simple test page with a button and the modal. The modal attribute modal-visible is set to 'false'. When the button is clicked, the modal's modal-visible attribute is updated to 'true', but the modal does not show on the page. Can someone please guide me to this solution and also give me a good explination on why this is happening? Thanks.

testing.html

<button id="btn1" class="btn btn-default">Click Me</button>
<modal id="chartModal" modal-title="Testing a Modal" modal-visible="false">
  <p>Body</p>
</modal>

<script type="text/javascript">
    var modal = $("#chartModal");
    var btn = $("#btn1");

    btn.on("click", function() {
        modal.attr("modal-visible", "true");
    })
</script>

modal.js

(function() {

    var app = angular.module("contactAuthApp");


//////////////////////////
//  BEGIN - DIRECTIVES
//////////////////////////

    var modalDirective = function() {
        return {
            restrict: "E",
            scope: {
                modalVisible: "=",
                modalTitle: "@"
            },
            transclude: true,
            controller: function($scope) {

            },
            templateUrl: "utils/modal.html",
            link: function (scope, element, attrs) {
                //Attach base classes
                element.addClass("modal fade");

                //Hide or show the modal
                scope.showModal = function (visible) {
                    if (visible) {
                        element.modal("show");
                    } else {
                        element.modal("hide");
                    }
                }

                //Check to see if the modal-visible attribute exists
                if (!attrs.modalVisible) {

                    //The attribute isn't defined, show the modal by default
                    scope.showModal(true);

                } else {

                    //Watch for changes to the modal-visible attribute
                    scope.$watch("modalVisible", function (newValue, oldValue) {
                        scope.showModal(newValue);
                    });

                    //Update the visible value when the dialog is closed through UI actions (Ok, cancel, etc.)
                    element.bind("hide.bs.modal", function () {
                        scope.modalVisible = false;
                        if (!scope.$$phase && !scope.$root.$$phase)
                            scope.$apply();
                    });

                }
            }
        };
    }

//////////////////////////
//  END - DIRECTIVES
//////////////////////////  


    app.directive("modal", modalDirective);

})();

modal.html (directive template)

<div class="modal-dialog">
    <div class="modal-content">
        <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
            <h4 class="modal-title">{{modalTitle}}</h4>
        </div>
        <div class="modal-body">
            <div data-ng-transclude></div>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-default pull-right" data-dismiss="modal">Close</button>
        </div>
    </div>
</div>

EDITED

Hey runTarm: here is my updated code changing the isolate scope of modalVisible to '@' and observing the attribute in link. Still can't get it to work:

var modalDirective = function() {
    return {
        restrict: "E",
        scope: {
            modalVisible: "@",
            modalTitle: "@"
        },
        transclude: true,
        controller: function($scope) {

        },
        templateUrl: "utils/modal.html",
        link: function (scope, element, attrs) {

            //Attach base classes
            element.addClass("modal fade");

            //Hide or show the modal
            scope.showModal = function (visible) {
                if (visible === true) {
                    element.modal("show");
                } else {
                    element.modal("hide");
                }
            }

            //Watch for changes to the modal-visible attribute
            attrs.$observe("modalVisible", function (val) {
                console.log(val);
                scope.showModal(val);
            });

            //Update the visible value when the dialog is closed through UI actions (Ok, cancel, etc.)
            element.bind("hide.bs.modal", function () {
                scope.modalVisible = false;
                if (!scope.$$phase && !scope.$root.$$phase)
                    scope.$apply();
            });

        }
    };
}
2

2 Answers

2
votes

You have the watch in else block for this condition !attrs.modalVisible, hence it gets triggered on fail of that condition. Modify the directive (removing the if-else block) to something like below:

//Watch for changes to the modal-visible attribute
scope.$watch("modalVisible", function (newValue, oldValue) {
    if( newValue ) {
        scope.showModal(newValue);
    } else {
        //Update the visible value when the dialog is closed 
        //through UI actions (Ok, cancel, etc.)
        element.bind("hide.bs.modal", function () {
            scope.modalVisible = false;
            if (!scope.$$phase && !scope.$root.$$phase)
                scope.$apply();
        });
    }
});
0
votes

The two-way binding between a directive's isolated scope and a parent scope cannot be used with a constant like that.

<modal modal-visible="false"> // false is a constant boolean value not a variable in the parent scope

You have to choose between keep using the two-way binding or use a custom watch function to detect the attribute changes.

Please see http://plnkr.co/edit/ZwrL6qlKTQCWZJ3EsXdM?p=preview for examples.

In the plunker above, modal-two is using the two-way binding:

<button id="btn2" class="btn btn-default" ng-click="isModalVisible = true">Click Me v2</button>
<modal-two id="chartModal2" modal-title="Testing a Modal" modal-visible="isModalVisible">
  <p>Body</p>
</modal-two>

and modal is using the custom watch function:

scope.$watch(function() {
  return element.attr('modal-visible');
}, function (newValue, oldValue) {
  scope.showModal(newValue === 'true');
});

However, I would discorage the use of the custom watch function, it isn't an angular way to do it and might cause a performance issue.