How to reload simple captcha in directive?

425 views Asked by At

I am using a simple captcha on my pop-up dialog using bPopup plugin.

You can refer the captcha code to this jsfiddle: https://jsfiddle.net/Mazzu/hspxaeqa/

HTML:

<span id="popup-button-email">Click Here!</span>

<div id="popup-second"> <a class="b-close">x<a/>
        <simple-captcha valid="captchaValid"></simple-captcha>
</div>

JS:

app.directive('simpleCaptcha', function() {
    return {
        restrict: 'E',
        scope: { valid: '=' },
        template: '<input ng-model="a.value" ng-show="a.input" style="width:2em; text-align: center;"><span ng-hide="a.input">{{a.value}}</span>&nbsp;{{operation}}&nbsp;<input ng-model="b.value" ng-show="b.input" style="width:2em; text-align: center;"><span ng-hide="b.input">{{b.value}}</span>&nbsp;=&nbsp;{{result}}',
        controller: function($scope) {

            var show = Math.random() > 0.5;

            var value = function(max){
                return Math.floor(max * Math.random());
            };

            var int = function(str){
                return parseInt(str, 10);
            };

            $scope.a = {
                value: show? undefined : 1 + value(4),
                input: show
            };
            $scope.b = {
                value: !show? undefined : 1 + value(4),
                input: !show
            };
            $scope.operation = '+';

            $scope.result = 5 + value(5);

            var a = $scope.a;
            var b = $scope.b;
            var result = $scope.result;

            var checkValidity = function(){
                if (a.value && b.value) {
                    var calc = int(a.value) + int(b.value);
                    $scope.valid = calc == result;
                } else {
                    $scope.valid = false;
                }
                $scope.$apply(); // needed to solve 2 cycle delay problem;
            };


            $scope.$watch('a.value', function(){    
                checkValidity();
            });

            $scope.$watch('b.value', function(){    
                checkValidity();
            });
        }
    };
});

POPUP:

$('#popup-button-email').bind('click', function(e) {
    // Prevents the default action to be triggered. 
    e.preventDefault();

    // Triggering bPopup when click event is fired
    $('#popup-second').bPopup();
});

All looks fine, except one thing.

After I close the pop-up, and I open another pop-up, it didn't reload the captcha, which is very odd.

Any ideas how to reload the captcha after I close the pop-up?

Thanks

1

There are 1 answers

0
Shantanu On

You need to update just one or two things. In checkvalidity function of directive $scope.$apply() causing $digest cycle to run when it's already in progress, so breaking it, to avoid that you can have checkvalidity function in scope variable of directive's controller & remove this explicit calling of $apply. Also, as $('#popup-second').bPopup() calling does not actually re-render the directive so it always showing first time loaded results of directive. So what you can do is have ng-if on that directive element in your view & toggle it on onClose & onOpen events of bPopup(). So your template can be:

<div id="popup-second" style="display: none">
    <div> From Controller : {{mymodel.captchaValid}}
    </div>
    <div ng-if="show">
      <simple-captcha valid="mymodel.captchaValid"></simple-captcha>
    </div>
</div>

Update checkvalidity function inside directive as:

$scope.checkValidity = function() {
    if (a.value && b.value) {
      var calc = int(a.value) + int(b.value);
      $scope.valid = calc == result;
    } else {
      $scope.valid = false;
    }
};

$scope.$watch('a.value', function(){    
    $scope.checkValidity();
});

$scope.$watch('b.value', function(){    
    $scope.checkValidity();
});

And in controller of view call bPopup as:

$('#popup-button-email').bind('click', function(e) {
    // Prevents the default action to be triggered.
    e.preventDefault();

    // Triggering bPopup when click event is fired
    $('#popup-second').bPopup({
      onOpen: function() {
        $scope.show = true;
        $scope.$digest();
      },
      onClose: function() {
        $scope.show = false;
        $scope.$digest();
      }
    });
});

So in this way the directive is re-created every time you trigger it. So it'll give expected results & thanks to isolated scope you can reuse it multiple times on same view or different view of the app.

Working plunker