kristianmandrup / schema-to-yup

Schema to Yup validation
Other
283 stars 50 forks source link

How can I define error message in json (schema) #108

Closed beckyi closed 2 years ago

beckyi commented 2 years ago

hi 😬

when I used buildYup,

I would like to define error message in schema json

or define errMessages in config

I want to schema(json) like the result below.

 yup.object().shape({
    fruits: yup
      .array()
      .of(
        yup.object().shape({
          apple: yup.string().required('MUST EAT APPLE!'), //<<< constraints (define error message)
          orange: yup.string().required(),
        }),
      )
      .required('I NEED!'), 
  })

( It can only be in a declarative way >>> json)

const json = {
    "$id": "fruits",
    "type": "object",
    "title": "HELP Schema",
    "$schema": "",
    "required": [
        "startDate"
    ],
    "properties": {
        "fruits": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "apple": {
                        "type": "string",
                        "required": true,   //<<< define error message 🥲
                    },
                    "orange": {
                        "type": "string",
                        "required": true,   //<<< define error message 🥲
                    }
                },
                "propertyNames": [
                    "apple", "orange"
                ]
            },
            "description": "fruit world",
            "required": true
        }
    }
}
const config = {
      errMessages: {
             fruits: "I NEED!"
      },
}
kristianmandrup commented 2 years ago

If you search for errMessages in the code you will find this in mixed.js

  initHelpers() {
    const { config } = this;
    const errorMessageHandlerFactoryFn =
      this.config.createErrorMessageHandler || this.createErrorMessageHandler;

    this.errorMessageHandler = errorMessageHandlerFactoryFn(this, config);

This hints that you can pass your own factory function createErrorMessageHandler on the config object to create a custom ErrorMessageHandler.

I would simply subclass the built in ErrorMessageHandler

class CustomErrorMessageHandler extends ErrorMessageHandler {
  constructor(typeHandler, config = {}) {
    super(typeHandler, config);
    this.errMessages[key] = typeHandler.value.errMessage
  }

  errMessageFor(msgName) {
    const { errMessages, key } = this;
    const errMsg = errMessages[key];
    return errMsg ? errMsg[msgName] : errMessages[`$${msgName}`];
  }

Then use in config

const config = {
  createErrorMessageHandler: (typeHandler, config) => new CustomErrorMessageHandler(typeHandler, config)
}

Should pretty much work.

A simpler approach - make a PR which modifies error-message-handler.js

export class ErrorMessageHandler extends TypeMatcher {
  constructor(typeHandler, config = {}) {
    super(config);
    this.typeHandler = typeHandler;
    this.init()
  }

  init() {
    const { typeHandler } = this
    this.constraints = typeHandler.constraints;
    this.errMessages = typeHandler.errMessages;
    this.key = typeHandler.key;
    this.type = typeHandler.type;
    this.description = typeHandler.description
    this.title = typeHandler.title
    this.setErrMessage()  
  }

  setErrMessage() {
    const { typeHandler, errMessages } = this
    if (!typeHandler.value.errMessage) return
    errMessages[this.key] = typeHandler.value.errMessage
  }

Note that value contains the object for the JSON key (such as for apple):

{
  "type": "string",
  "required": true,
  "errMessage": "My custom error" // <--- extension
},

PS: For PR please add tests to make sure it works.

kristianmandrup commented 2 years ago

Alternatively (or additionally you can improve the valErrMessage method in mixed.js

  valErrMessage(msgName) {
    return this.errorMessageHandler.valErrMessage(msgName);
  }

You could make a PR to change it to sth like this

  valErrMessage(msgName) {
    const valErrMessageFn = (this.config.valErrMessage || this.errorMessageHandler.valErrMessage).bind(this)
    return valErrMessage(msgName, this);
  }

Then you can customize validation error msg handling simply by supplying a custom valErrMessage function on the config object

{
  valErrMessage: (msgName, typeHandler) => {
    const { constraints, description, title, value } = typeHandler;
    const errMsg = value.errMsg || this.errMessageFor(msgName);
    return typeof errMsg === "function" ? errMsg(constraints, { description, title}) : errMsg;
  }
}
kristianmandrup commented 2 years ago

The latest push to master includes these changes and a Readme update that describes how to use them

bongofury commented 2 years ago

Hello @kristianmandrup , it looks like this latest push on valErrMessage in config object has not been published to npm.

Thanks a lot!

kristianmandrup commented 2 years ago

Hi @bongofury,

Indeed. The latest changes have not yet been published as I wanted you guys to try out the change and confirm if it sufficient for your requirements or needs some further work to be more flexible towards that end. Please try it out from the github master first. Thanks.

You may also check out some of the other recent issues and suggested fixes/improvements.

kristianmandrup commented 2 years ago

Just published 1.11.11 with the changes and improvements. Please also see the new updated Readme section on custom error handling. You can now also control the key names to be used.

bongofury commented 2 years ago

Wonderful work @kristianmandrup ! Many thanks to you and to all the contributors 🤗