rinat.io


AngularJS Directives with jQuery Plugins

AngularJS directives are very powerful, but first time it looks quite tricky on how to set them up with jQuery plugins. Let's say we want to add the spectrum jQuery color picker on our form using a directive. Here's how we could make that with an isolate scope and two way data binding:

var app = angular.module('app', []);

app.directive('colorPicker', function() {
    return {
        scope: {
            color: "="
        },
        link: function(scope, elm, attrs) {

            elm.spectrum({
                color: scope.color,
                change: function(color) {
                    scope.$apply(function() {
                        scope.color = color.toHexString();
                    });
                }
            });

            scope.$watch('color', function(newColor) {
                elm.spectrum('set', newColor);
            });

        }
    };
});

See the Pen by Rinat (@rinatio) on CodePen

Here we're listening for the color picker change event to update the data when user chooses a new one. Note that you have to use $apply here because event is being triggered from outside of angular framework. On the other hand we're watching for external data changes with angular $watch to update the color picker value.

Usually in such cases it's better to use ngModel with NgModelController instead of isolate scope and two way data binding, since it also provides a validation behavior for our input:

var app = angular.module('app', []);

app.directive('colorPicker', function() {
    return {
        require: 'ngModel',
        link: function(scope, elm, attrs, ngModel) {
            elm.spectrum({
                color: ngModel.$viewValue,
                change: function(color) {
                    scope.$apply(function() {
                        ngModel.$setViewValue(color.toHexString());
                    });
                }
            });

            ngModel.$render = function() {
                elm.spectrum('set', ngModel.$viewValue || '');
            };

        }
    };
});

See the Pen by Rinat (@rinatio) on CodePen

Now we can check if the form is valid and show a proper validation error etc. In this example the text input value won't be updated and submit button will be disabled until we select the white color. Note that we cannot use isolate scope with ngModel directive.

Finally imagine we want to reuse this directive somewhere else, but it needs a different options for jQuery plugin:

var app = angular.module('app', []);

app.directive('colorPicker', function() {
    return {
        require: 'ngModel',
        link: function(scope, elm, attrs, ngModel) {
            var options = angular.extend({
                color: ngModel.$viewValue,
                change: function(color) {
                    scope.$apply(function() {
                        ngModel.$setViewValue(color.toHexString());
                    });
                }
            }, scope.$eval(attrs.colorPicker));

            ngModel.$render = function() {
                elm.spectrum('set', ngModel.$viewValue || '');
            };

            elm.spectrum(options);
        }
    };
});

See the Pen by Rinat (@rinatio) on CodePen

Now we can set an additional plugin options in the colorPicker attribute like this: <input color-picker="{showInput: true}">. Notice how they are getting extended with angular.extend.