Color picker with input validation

Color picker input field that has a border bound to the color you put in the field. The submit button is disabled unless you have a valid hex code.

by Mike McBride

HTML

<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<div class="wrapper" ng-app="app" ng-controller="appCtrl">
    <input type="text" class="color-picker" ng-model="color" ng-style="{ 'border-color': color }" placeholder="#FF0000 or #F00" />
    <button type="submit" class="btn btn-primary" toggle-disabled="canSubmit">Submit</button>
</div>

CSS

html, body {
    margin: 0;
    box-sizing: border-box;
}
.wrapper {
    height: 100%;
    width: 100%;
    margin: 50px;
    box-sizing: border-box;
}
.color-picker {
    border: 1px solid #aaa;
    border-left-width: 30px;
    border-radius: 2px;
    height: 34px;
    width: 300px;
    font-size: 14px;
    line-height: 22px;
    padding-left: 7px;
    -webkit-transition: border-color 0.3s ease;
    -moz-transition: border-color 0.3s ease;
    -o-transition: border-color 0.3s ease;
    transition: border-color 0.3s ease;
}
.color-picker:focus {
    box-shadow: none;
    outline: none;
}
.color-picker::-webkit-input-placeholder {
    font-style: italic;
    color: #ccc;
}

JavaScript

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

app.controller('appCtrl', function($scope) {
    $scope.$watch('color', function(newValue, oldValue, scope) {
        scope.canSubmit = isValidHex(newValue);
    });
    function isValidHex(code) {
        var isValid = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(code);
        return isValid;
    }
});
        
app.directive('toggleDisabled', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            scope.$watch(attrs.toggleDisabled, function (new_value, old_value) {
                if (new_value) {
                    element.removeAttr('disabled');
                }
                else {
                    element.attr('disabled', 'disabled');
                }
            });
        }
    };
});