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

Custom field / Nested key breaking validation #841

Closed coxrichuk closed 7 years ago

coxrichuk commented 7 years ago

I have created a Custom Textbox and I want to return a nested json to the model.

When I set the key in the form to myfield.value

It breaks the validation and never returns the model

However if I use the standard Schema form text box it works as expected return the model with a nested key

Schema / Form

{
    "schema":{
        "type":"object",
        "properties": {
            "myfield.value": {
                "type":"string"
            },
            "field.value": {
                "type":"string"
            }
        },
        "required":[]
    },
    "form":[
        {
            "type": "customTextBox",
            "key": "myfield.value",
            "title": "Font label (not working)"
        },
        {
            "type": "string",
            "key": "field.value",
            "title": "Font label (working)"
        }
    ],
    "model":{
    }
}

Custom field

  angular.module('customApp').config(function (schemaFormDecoratorsProvider, sfBuilderProvider) {

    schemaFormDecoratorsProvider.defineAddOn(
      'bootstrapDecorator',           // Name of the decorator you want to add to.
      'customTextBox',                     // Form type that should render this add-on
      'custom-text-box.html',  // Template name in $templateCache
      sfBuilderProvider.stdBuilders                     // List of builder functions to apply.
    );

  });
<div coh-id-tagger class="form-group custom-text-box {{form.htmlClass}}" ng-class="{'no-label': !showTitle()}" ng-hide="form.isActive===false">
  <!-- Surrounding DIV for sfField builder to add a sfField directive to. -->
  <div class="form-group-inner">

    <label class="control-label {{form.labelHtmlClass}}" ng-class="{'sr-only': !showTitle()}"
           for="{{form.key.slice(-1)[0]}}">{{form.title}}</label>
    <coh-helper-button info="form.helperInfo"></coh-helper-button>

    <div class="custom-form-input" ng-show="!dir.proxyIsVariable">

      <div class="schema-form-{{form.type}}"
           ng-class="{'has-error': form.disableErrorState !== true && hasError(), 'has-success': form.disableSuccessState !== true && hasSuccess(), 'has-feedback': form.feedback !== false }">
        <input
          ng-if="!form.fieldAddonLeft && !form.fieldAddonRight" ng-show="form.key" type="text" step="any"
          sf-changed="form" placeholder="{{form.placeholder}}" class="form-control {{form.fieldHtmlClass}}"
          id="{{form.key.slice(-1)[0]}}" ng-model-options="form.ngModelOptions" sf-field-model="replaceAll"
          ng-model="$$value$$"
          ng-disabled="form.readonly" schema-validate="form" name="{{form.key.slice(-1)[0]}}"
          aria-describedby="{{form.key.slice(-1)[0] + 'Status'}}">
        <div ng-if="form.fieldAddonLeft || form.fieldAddonRight"
             ng-class="{'input-group': (form.fieldAddonLeft || form.fieldAddonRight)}">
          <span ng-if="form.fieldAddonLeft"
                class="input-group-addon"
                ng-bind-html="form.fieldAddonLeft"></span>
          <input ng-show="form.key" type="text" step="any" sf-changed="form" placeholder="{{form.placeholder}}"
                 class="form-control {{form.fieldHtmlClass}}" id="{{form.key.slice(-1)[0]}}"
                 ng-model-options="form.ngModelOptions" sf-field-model="replaceAll" ng-model="$$value$$"
                 ng-disabled="form.readonly"
                 schema-validate="form" name="{{form.key.slice(-1)[0]}}"
                 aria-describedby="{{form.key.slice(-1)[0] + 'Status'}}">
          <span ng-if="form.fieldAddonRight"
                class="input-group-addon"
                ng-bind-html="form.fieldAddonRight"></span>
        </div>
        <span ng-if="form.feedback !== false" class="form-control-feedback"
              ng-class="evalInScope(form.feedback) || {'glyphicon': true, 'glyphicon-ok': hasSuccess(), 'glyphicon-remove': hasError() }"
              aria-hidden="true"></span>
        <span ng-if="hasError() || hasSuccess()"
              id="{{form.key.slice(-1)[0] + 'Status'}}"
              class="sr-only">{{ hasSuccess() ? '(success)' : '(error)' }}</span>
      </div>

    </div>

    <span class="coh-description" ng-class="{'coh-form-hidden': !form.description}" sf-message="form.description"></span>

  </div>
</div>

Model I am expecting when all fields completed

   {
    "myfield" : {
        "value" : "custom field output"
    },
    "field" : {
        "value" : "schema form field output"
    }
  };

Model I am getting when all fields are completed

   {
    "field" : {
        "value" : "schema form field output"
    }
  };

myfield is being returned as invalid

Anthropic commented 7 years ago

Essentially both fields were getting only "value" as their id/name because of "{{form.key.slice(-1)[0]}}", so only one got updated. Names and Id's are now generated differently in the alpha of v1 using a function to get the full key path, you could try just joining your keys with .join("-") in your template.

Please re-open if you do not feel the question has been answered sufficiently.