json-schema-form / angular-schema-form

Generate forms from a JSON schema, with AngularJS!
https://json-schema-form.github.io/angular-schema-form
MIT License
2.47k stars 653 forks source link

Is there some way to require all fields in an object? #805

Closed Lorless closed 7 years ago

Lorless commented 7 years ago

Is there some way to validate fields in an object that never have inputs associated with them? Im my example the model is set by a directive.

The docs don't explain much about how validation works on different field types. I've tried this:

colourPicker1: {
          type: "object",
          properties: {
            hex:{
              type:"string"
            },
            rgba: {
              type: "string"
            }
          },
          required: [
            "hex", "rgba"
          ]
        },

I've also tried custom validators but the validator is never even run on validate:

EDIT I did not have schema-validate alongside my ng-model so my custom validators were not running. It seems however that changes to a field inside an object model do not trigger the customValidators. Neither does broadcasting schemaFormValidate

EDIT With further investigation I concluded that custom validators do not deep watch objects and therefore will not trigger on a field change within an object. The hack to get around this is to angular.copy the whole model every time a change occurs. Not great.

This is a custom add on and its fairly complicated but I'm not sure it should matter. Surely if the model is blank like colourPicker1 : {} or colourPicker: {hex:'',rgba:''} then the model will be used to detect that neither of the fields i want exist?

Anthropic commented 7 years ago

@Lorless if required didn't work then I am not sure why not. Can you make a plunker?

Lorless commented 7 years ago

Okay here it is: https://plnkr.co/edit/pAyuDP8zfT95wedXMzrQ?p=preview

As you can see the custom validators are run immediately which I don't think should happen. The required custom validator works for this scenario but does not update when fields inside the object change requiring a copy for every change. Also the custom validators do not run on the broadcast of schemaFormValidate.

I have also tried to use the required : [] array syntax in the schema to produce the validation necessary but this doesn't seem to work.

EDIT: Apologies for the flat file structure, not even sure if plunker allows folder file structures.

EDIT: The file you want to look at is coh-colourpicker-form-ctrl.js

Would be nice to get some help with this!

Lorless commented 7 years ago

Can someone take the tag off as this now has a plunker. The main problem here is validation on an object that contains fields rather than validation on the fields themselves. I need a way to check if all those fields are properly filled out but custom validators don't work as I would expect and the required array does work at all.

Anthropic commented 7 years ago

@Lorless, I made my own decorator for custom validation of an entire object and passed the whole object into tv4 to validate independently. You can see where I have the line ngModel.$validators.

This is my substitute version of anyOf validation and very very old.

This is what it looks like:

(function(angular, undefined) {'use strict';
  angular
    .module('schemaForm')
    .directive('oyInline', [ 'schemaForm', 'sfValidator', 'sfPath', 'sfSelect',
      function(schemaForm, sfValidator, sfPath, sfSelect) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function(scope, element, attrs, ngModel) {
            var useKey = sfPath.stringify(scope.form.key),
                schema = {},
                title = scope.form.title || scope.form.key.join('.') || '';

            scope.invalidAnyOf = false;

            angular.copy(scope.form.schema, schema);

            if (schema.properties && schema.anyOf) {
              scope.form.schema.allowInvalid = true;
              delete schema.properties;
              delete schema.description;
            };

            ngModel.$name = title;
            ngModel.$options = {allowInvalid: true}

            scope.$watchCollection('model' + useKey, function(newVal, oldVal) {
              if (ngModel.$validate) {
                ngModel.$validate();
                if (ngModel.$invalid) { // The field must be made dirty so the error message is displayed
                  ngModel.$setValidity('anyOf', false);
                  scope.invalidAnyOf = true;
                }
                else {
                  ngModel.$setValidity('anyOf', true);
                  scope.invalidAnyOf = false;
                }
              }
              else {
                ngModel.$setViewValue(ngModel.$viewValue);
              }
            });

            ngModel.$validators = {
              anyOf: function(modelValue, viewValue) {
                tv4.validate(modelValue, schema);
                return tv4.valid;
              }
            };

            // Listen to an event so we can validate the input on request
            scope.$on('schemaFormValidate', function() {
              if (ngModel.$validate) {
                ngModel.$validate();
                if (ngModel.$invalid) { // The field must be made dirty so the error message is displayed
                  ngModel.$dirty = true;
                  ngModel.$pristine = false;
                }
              }
              else {
                ngModel.$setViewValue(ngModel.$viewValue);
              };
            });
          }
        };
      }
    ]);
})(window.angular);
Anthropic commented 7 years ago

@Lorless did my last comment help at all? I am closing this as it's been two months please feel free to re-open it if you have an enhancement suggestion you think should be added :)