2

I am trying to write custom directive and one of the requirements is that I should be able to disable one of the elements based on the expression set on attribute of the directive. Directive is declared like this

<sr-payment-edit payment="vm.paymentInfo" disablepaymenttypeselection="!vm.isPolicySelected || someOtherCondition">

Basically it is supposed to hide a payment type if a policy is not selected yet. Once policy gets selected, payment type would be enabled. Here is html template for that portion of the directive

<div class="row" data-ng-hide='hidePaymentType'>
    <div class="col-xs-12 p-l-0 p-r-0">
        <div class="col-xs-3">
            <div class="form-group">
                <label>Payment Type:</label>
                <select data-ng-model="payment.paymentTypeCode"
                        data-ng-disabled="disablePaymentType" class="form-control" style="width: 150px">
                    <option value="CASH">Cash</option>
                    <option value="CHCK">Check</option>
                    <option value="CCPP">Credit Card - Pre Pay</option>
                    <option value="MNOD">Money Order</option>
                </select>
            </div>
        </div>
    </div>
</div>

Here is directive code, in early stages

(function (angular) {

    'use strict';

    angular.module('app').directive('srPaymentEdit', srPaymentEditDirectiveFactory);

    function srPaymentEditDirectiveFactory() {
        return {
            restrict: 'E',
            templateUrl: 'app/app_directives/sr-paymentEdit/sr-paymentEdit.html',
            scope: {
                payment: '=',
                disablepaymenttypeselection: '@',
                hidepaymenttypeselection: '@'
            },
            transclude: false,
            controller: controller,
            link: link
        };
    }

    function link($scope, element, attrs, model) {

        if (attrs.hidepaymenttypeselection) {
            $scope.hidePaymentType = $scope.$eval(attrs.hidepaymenttypeselection);

        }

        if (attrs.disablepaymenttypeselection) {

            $scope.$watch(attrs.disablepaymenttypeselection, function (value) {
                $scope.disablePaymentType = $scope.$eval(attrs.disablepaymenttypeselection);
            });

        }  
    }

    function controller($scope) {

        if ($scope.payment != null) {

            if ($scope.payment instanceof PaymentInfo === false) {
                throw 'Invalid datasource type, must be instance of PaymentInfo';
            }

        } else {
            var info = new PaymentInfo();
            info.paymentTypeCode = 'CHCK';
            $scope.payment = info;
        }
    }
})(angular);

So far so good, watch fires and disables the selection, but after "vm.isPolicySelected" changes, naturally, watch for the attribute does not fire.

Is it possible to trigger watch so that "disablepaymenttypeselection" is re-evaluated?

epitka
  • 17,275
  • 20
  • 88
  • 141

3 Answers3

0

Looks like the problem is that in directive scope configuration you need to use = binding, not attribute @. So try this:

scope: {
    payment: '=',
    disablepaymenttypeselection: '=',
    hidepaymenttypeselection: '@'
},

and also change watch part a little (use just property name):

$scope.$watch('disablepaymenttypeselection', function(value) {
    $scope.disablePaymentType = $scope.$eval(attrs.disablepaymenttypeselection);
});
dfsq
  • 191,768
  • 25
  • 236
  • 258
  • You are close but this will not work, as it is isolated scope, and "vm" is not passed in scope, so "vm.isPolicySelected" cannot be properly evaluated. – epitka Feb 19 '15 at 16:04
  • check the demo I was testing my approach, looks like it works: http://plnkr.co/edit/OFRrH7NqZZQhy0DkPmZ3?p=preview – dfsq Feb 19 '15 at 16:07
  • Thanks for putting demo together, but I can see flag changing from true to false, yet I am still able to click and open payment type selection, that part never changes for me. I am using Chrome. – epitka Feb 19 '15 at 16:25
0

You declare that your attributes 'disablepaymenttypeselection' and 'hidepaymenttypeselection' are one way binded as text:

        scope: {
            payment: '=',
            disablepaymenttypeselection: '@',
            hidepaymenttypeselection: '@'
        },

I think you need to use a two way binding ('=') so you can toggle the (boolean) property to hide or show your html.

With your code if you 'console.log($scope.disablepaymenttypeselection)', the you would get the following output as string:

!vm.isPolicySelected || someOtherCondition

Read this for more info about directives and bindings: When writing a directive in AngularJS, how do I decide if I need no new scope, a new child scope, or a new isolated scope?

Community
  • 1
  • 1
com2ghz
  • 2,706
  • 3
  • 16
  • 27
  • You are close but this will not work, as it is isolated scope, and "vm" is not passed in scope, so "vm.isPolicySelected" cannot be properly evaluated. – epitka Feb 19 '15 at 16:05
0

Got it working. My problem was that I had created an isolated scope, yet I wanted to respond to changes in parent scope, without explicitly passing in all dependencies. So I re-wrote directive like this

function srPaymentEditDirectiveFactory() {
    return {
        restrict: 'E',
        templateUrl: 'app/app_directives/sr-paymentEdit/sr-paymentEdit.html',
        scope: true,
        //scope: {
        //    vm:'=',
        //    payment: '=',
        //    disablepaymenttypeselection: '=',
        //    hidepaymenttypeselection: '@'
        //},
        transclude: false,
        //controller: controller,
        link: link
    };
}

function link($scope, element, attrs, model) {

    var info = null;

    if (attrs.payment == undefined) {
        info = new PaymentInfo();
        info.paymentTypeCode = 'CHCK';

    } else {
        info = $scope.$eval(attrs.payment);

        if (info instanceof PaymentInfo === false) {
            throw 'Invalid datasource type, must be instance of PaymentInfo';
        }
    }

    $scope.payment = info;

    if (attrs.hidepaymenttypeselection) {
            $scope.hidePaymentType = $scope.$eval(attrs.hidepaymenttypeselection);
        }

        if (attrs.disablepaymenttypeselection) {
            $scope.$watch(attrs.disablepaymenttypeselection, function (value) {
                $scope.disablePaymentType = $scope.$eval(attrs.disablepaymenttypeselection);
            });
        }
    }

})(angular);
epitka
  • 17,275
  • 20
  • 88
  • 141