0

I'm trying to use the prettyCheckbox plugin to apply styling to my checkboxes. For those of you who don't know the plugin works the way that it hides the original checkbox and displays another styled object instead. I created a directive which instantiates the plugin for each checkbox.

The plugin on click of the fake checkbox sets the property of the real input element and calls change - this however doesn't seem to update the model:

input.prop('checked', true).change();

I amended the prettyCheckbox plugin slightly, so the original checkbox is being changed using a click event in order to update the model:

input.trigger('click');

This works fine, however the checkbox seems to always have the exact opposite value you would expect it to have: true if checkbox isn't checked and false if checkbox is checked. I can't seem to work out why this is happening. I created a plunkr to show you what is happening:

http://plnkr.co/edit/YVq2is0khGgLRWbHk2aB?p=preview

Thanks in advance for any helping hand :)

2 Answers 2

1

input.trigger('click') is called in the middle of another event handler. Because JavaScript is single threaded, the event loop can only process one event at a time and an event is already being processed, so this event will be handled as soon as the event queue becomes free. In your case, by the time your 'click' event can be handled, it is after angular has finished running a digest cycle. That means that angular has already done its processing before your checkbox is actually clicked and when the checkbox click is complete, angular ignores it because it sees the "showRedBox" value as being clean (not dirty). It isn't until you click again on the pretty check box that angular looks again at the "showRedBox", compares it's current value to the old one, recognizes that it is dirty, and updates the DOM. Of course, by the time that is done, your checkbox hasn't changed states again, so its out of sync. Calling $scope.$apply() or $scope.$digest() won't help in this situation because angular already thinks that "showRedBox" is clean. You'd have to first modify angular's data so that it recognizes the "showRedBox" value as dirty, which you can only do if you have access to the scope (which you don't).

Solution: Use scope.showRedBox = fakeCheckable.hasClass("checked"); instead of input.trigger('click') to modify the "showRedBox" value directly. This solution will require that you fix the scope of your plugin to fall within the closure of the directive.

Sign up to request clarification or add additional context in comments.

1 Comment

thanks for your answer, it did help me to understand what's going on. I found a jsfiddle with a really nice solution to the problem, and implemented that for my purpose. will post it in a separate answer.
1

Right, so I found this jsfiddle which really helped me to figure things out without using pretty checkbox plugin:

http://jsfiddle.net/evaneus/z9rge/

As James mentioned in his answer the directive needs access to the controller:

angular.module('buttonToggle', []).directive('buttonToggle', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function($scope, element, attr, ctrl) {
            var classToToggle = attr.buttonToggle;
            element.bind('click', function() {
                var checked = ctrl.$viewValue;
                $scope.$apply(function(scope) {
                    ctrl.$setViewValue(!checked);
                });
            });

            $scope.$watch(attr.ngModel, function(newValue, oldValue) {
                newValue ? element.addClass(classToToggle) : element.removeClass(classToToggle);
            });
        }
    };
});


<p>
  <input id="gradeCheck" type="checkbox" ng-model="myModel['A']"> A
  <input type="checkbox" ng-model="myModel['B']"> B
  <input type="checkbox" ng-model="myModel['C']"> C
  <input type="checkbox" ng-model="myModel['D']"> D
</p> 

<p>
  <button button-toggle="active" class="btn" ng-model="myModel['A']">A</button>
  <button button-toggle="active" class="btn" ng-model="myModel['B']">B</button>
  <button button-toggle="active" class="btn" ng-model="myModel['C']">C</button>
  <button button-toggle="active" class="btn" ng-model="myModel['D']">D</button>
</p>

then I only had to style the button the way I wanted it with and without active style and all working nicely :)

1 Comment

So I've already been told re: angularJS that I really need to change my way of thinking. This was a complete epiphanous moment for me, however, in realizing just how stupid I've been. Of course using a button instead of a crazy jQuery-based solution is easier and in the form of true angularJS methodology. This question/answer has helped immensely.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.