TL; DR

Hello everyone, I have built two specifications and written related implementation, with the relevant implementation and tools, you can

  • Create schemas and validate data
    • Simple and expressive syntax and keywords
    • Describes the dependencies between fields
    • Dynamic field
  • The client and server share the schema, although currently only JS implementation, after the addition of different language implementation, theoretically can be cross-language
  • To generate the form
    • You pass the schema into the form component to get a form
    • Supports vUE and React
    • With very little code, you can support a component library that currently supports iView and ANTD

Here are two examples of forms generated under IView and ANTD using the same schema, vue + iView

react + antd

I’ll briefly describe each specification and the associated tools.

GateSchema

GateSchema is a specification for describing the structure and format of data. Like JSON-Schema, GateSchema uses a JSON object to describe data, so different languages can share the same Schema. Unlike JSON-Schema, GateSchema uses “constraints” to describe a data. A Schema is a list of “constraints” that expresses all the constraints that the data should conform to. For details, see GateSchema-Specification

Writing a schema by hand can be tedious, so we do not recommend writing it by hand and instead build it using the convenient syntax provided by the implementation, as shown below.

gateschema-js

Gateschema-js is a javascript implementation of Gateschema that provides a very simple syntax for creating schemas and validating data. Here’s a slightly more complicated example

Import _ from 'gateschema' const schema = _ // Data should be a map with the following keys and values. Map ({// gender must exist, enum(enumeration) type, can only be 1, 2 Gender: _.required. Enum ({MALE: 1, FEMALE: 2}), // Name must exist, map type, including firstName and lastName: _.required.map({ firstName: _.required.string.notEmpty, lastName: _.required.string.notEmpty, }), email: Format ('email'), // phone Optional, number(number) type phone: _. Optional. Number, // skills must exist, list(list) type skills: _. Must exist (cannot be undefined or null), string, Cannot be an empty string (") _. Required. A string. The notEmpty)}) / / / / can continue to add constraints. It also has the following key and the value map ({the introduction: _.optional. String}) // Conditional. Switch ('/gender', [{// if gender is 1 case: _.value(1), // There must be a birthday key of type string and format date schema: _. Map ({birthday: _. Required. The string. Format (' date ')})}) / / validation data const input = {/ /... } schema.validate(input, (err) => {// If a constraint is not met, Log (err) // ValidationError {// keyword: 'required', // msgParams: {KEY: 'required' }, // path: '/gender', // value: undefined, // msg: 'should not be null or undefined' } })Copy the code

The following example states that one of the two fields must exist

import _ from 'gateschema'  

const schema = _
  .map({
    email: _
      .switch('/phone'[{// If phone exists
          case: _.required,
          / / then & emsp; Email is optional
          schema: _.optional
        },
        {
          // All other cases
          case: _.any,
          // Email must exist
          schema: _.required
        }
      ])
      .string
      .format('email'),
    phone: _
      .switch('/email'[{case: _.required,
          schema: _.optional
        },
        {
          case: _.any,
          schema: _.required
        }
      ])
      .string
      .number
  })
Copy the code

If you still don’t think this is neat enough to write, you can encapsulate some of the logic and use aliases separately. In addition, you can customize error messages. This example uses built-in aliases and custom error messages

const schema = _
  .map({
    name: _
      // r -> required
      .r.$msg('Please enter a login name')
      // str -> string
      .str
      .notEmpty,
    password: _
      .r.$msg('Please enter your password')
      .str
      .length([6.16]).$msg('Passwords should contain between 6 and 16 characters'),
    repassword: _
      .r.$msg('Please enter your password confirmation')
      .equal('/password').$msg('Confirm password is different from password')})Copy the code

Refer to the Gateschema-js API for detailed usage

StateForm

StateForm defines a JSON-based structure that describes the state of a form and defines form events and built-in components. With the correlation implementation, you can use a JSON object to generate the form

Current implementation

  • stateform-iview
  • stateform-antd

GateSchema Form

Gatescheme-form-vue and Gateschema-form-React convert a Gateschema into a StateForm state object, and then use the StateForm to display the form. When the user has new input, Update the entire state and thus the display of the entire form.

Here is an example using Gatescheme-form-vue

// file: GateSchemaForm.js

import Vue from 'vue'
// stateform implementation
import createStateForm from '@stateform/iview'
import "@stateform/iview/dist/stateform-iview.css"

import { createForm } from 'gateschema-form-vue'

Create the StateForm component. If you want to use the upload related component, you need to implement handleUpload and handleRemove functions yourself
const StateForm = createStateForm({
  upload: {
    handleUpload(file, props, cb) {
      // Upload your file here, then call the cb function, passing in an upload result
      // By default, the url is used as the output value of the upload component. If you need to export other information, you can set a 'value' field in the upload result
      setTimeout((a)= > {
        cb({
          status: 'done'.// 'done' | 'error',
          url: 'http://.... '.// error: 'error msg',
          // value: {name: 'file name', url: 'http://.... '},})},1000)
    },
    handleRemove(file) {
      // This function is just a notification where you can tell the server to delete files}},components: {
    // You can use custom components in forms}})// 2. Create GateSchemaForm
const GateSchemaForm = createForm({
  StateForm
})
// Register to global
Vue.component('GateSchemaForm', GateSchemaForm)
Copy the code
// file: App.vue
<template>
  <GateSchemaForm 
    :schema="schema" 
    v-model="value" 
    @submit="handleSubmit" 
  />
</template>
<script>
  import _ from 'gateschema'
  // your schema
  const schema = _
    .required
    .map({
      name: _
        .required
        .string
        .notEmpty,
      gender: _
        .required
        .enum({
          MALE: 0,
          FEMALE: 1
        }),
      age: _
        .optional
        .number,
      intro: _
        .optional
        .string
        .other('form', {
          component: 'Textarea'
          // StateForm options
          // see https://github.com/stateform/StateForm-Specification
        })
    })
  export default {
    data() {
      return {
        schema: schema,
        value: {}
      }
    },
    methods() {
      handleSubmit() {
        console.log(this.value)
      }
    }
  }
</script>
Copy the code

See Gateschema-form-vue and Gateschema-form-React for more details

The last

At present, the documents of each project are not perfect enough. I will write detailed examples when I have time. In addition, I plan to write a specification (PartonAPI) describing the RPC interface, which is currently in draft form. Just as SwaggerAPI describes restful interfaces, PartonAPI will use GateSchema to describe RPC interfaces.

If you have any ideas or suggestions, please make an issue or PR. You can also add my wechat liang_weiliang GateSchema QQ group: 516628141