diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.models.js b/extensions/mistral/static/mistral/js/mistral.workbook.models.js index 53715dd..1d0a103 100644 --- a/extensions/mistral/static/mistral/js/mistral.workbook.models.js +++ b/extensions/mistral/static/mistral/js/mistral.workbook.models.js @@ -559,7 +559,12 @@ 'index': 0, 'panelIndex': 0, 'row': 0 - } + }, + '@constraints': [ + function(value) { + return value !== 'workbook1' ? true : 'The sample validation failure.'; + } + ] }) }, 'description': { diff --git a/merlin/static/merlin/js/merlin.directives.js b/merlin/static/merlin/js/merlin.directives.js index 002e655..8d53ca8 100644 --- a/merlin/static/merlin/js/merlin.directives.js +++ b/merlin/static/merlin/js/merlin.directives.js @@ -112,6 +112,36 @@ } } }) + .directive('validatableWith', function($parse) { + return { + restrict: 'A', + require: 'ngModel', + link: function(scope, element, attrs, ctrl) { + var model; + if ( attrs.validatableWith ) { + model = $parse(attrs.validatableWith)(scope); + scope.error = ''; + model.setValidatable && model.setValidatable(true); + model.on && model.on('validation', function(result) { + var isValid = (result == 'succeeded'), + baseMessage = ''; + // (FIXME): hack until Barricade supports validation of empty required entries + if ( !model.get() && model.isRequired() ) { + isValid = false; + baseMessage = 'This field is required.' + } + ctrl.$setValidity('barricade', isValid); + scope.error = model.hasError() ? model.getError() : baseMessage; + }); + ctrl.$formatters.push(function(modelValue) { + return modelValue === undefined ? + ( ctrl.$isEmpty(ctrl.$viewValue) ? undefined : ctrl.$viewValue ) : + modelValue; + }); + } + } + } + }) .directive('typedField', ['$compile', 'merlin.templates', function($compile, templates) { function updateAutoCompletionDirective(template) { diff --git a/merlin/static/merlin/js/merlin.field.models.js b/merlin/static/merlin/js/merlin.field.models.js index 47ae295..dbc393b 100644 --- a/merlin/static/merlin/js/merlin.field.models.js +++ b/merlin/static/merlin/js/merlin.field.models.js @@ -32,11 +32,18 @@ }); var modelMixin = Barricade.Blueprint.create(function(type) { + var isValid = true, + isValidatable = false; this.value = function() { if ( !arguments.length ) { - return this.get(); + if ( isValidatable ) { + return isValid ? this.get() : undefined; + } else { + return this.get(); + } } else { this.set(arguments[0]); + isValid = !this.hasError(); } }; this.id = utils.getNewId(); @@ -45,6 +52,10 @@ return type; }; + this.setValidatable = function(validatable) { + isValidatable = validatable; + }; + this.setType = function(_type) { type = _type; }; diff --git a/merlin/static/merlin/scss/merlin.scss b/merlin/static/merlin/scss/merlin.scss index b1b842a..b33a08c 100644 --- a/merlin/static/merlin/scss/merlin.scss +++ b/merlin/static/merlin/scss/merlin.scss @@ -54,8 +54,13 @@ } } -editable .fa-pencil { - color: black; +editable { + .fa-pencil { + color: black; + } + button:disabled { + color: gray; + } } .section { diff --git a/merlin/static/merlin/templates/editable.html b/merlin/static/merlin/templates/editable.html index 06bdd31..7d8dd91 100644 --- a/merlin/static/merlin/templates/editable.html +++ b/merlin/static/merlin/templates/editable.html @@ -5,6 +5,6 @@ - + diff --git a/merlin/static/merlin/templates/fields/number.html b/merlin/static/merlin/templates/fields/number.html index 2b53f39..fbcc2b2 100644 --- a/merlin/static/merlin/templates/fields/number.html +++ b/merlin/static/merlin/templates/fields/number.html @@ -3,5 +3,6 @@ + autocompletable="true" validatable-with="value"> +