Vue3 json Schema form

Development log

Build the project

@ vue / cli 4.5.10

Custom: default+ts+unit test+no class-style+eslint+prettier+jest

Use prettier

Install Prettier-Code formatter vscode plugin

Create .prettierrc

{
    "semi": false,
    "singleQuote": true,
    "arrowParens": "always",
    "trailingComma": "all"
}

Configuration setting: Format On Save, it is recommended to save as workspace configuration

Some concepts

API in vue3
sfc’s new development method -0000

Please check the latest https://github.com/vuejs/rfcs/pull/227

<script setup>
  // imported components are also directly usable in template
  import Foo from './Foo.vue'
  import { ref } from 'vue'

  // write Composition API code just like in a normal setup()
  // but no need to manually return everything
  const count = ref(0)
  const inc = () => {
    count.value++
  }
</script>

<template>
  <Foo :count="count" @click="inc" />
</template>

Compiled Output

<script setup>
  import Foo from './Foo.vue'
  import { ref } from 'vue'
export default {
setup() {
const count = ref(1)
const inc = () => {
count.value++
}

   return  {
    Foo ,  // see note below
    count ,
    inc ,
   }
 } ,

}
</script>

<template>
<Foo :count="count" @click="inc" />
</template>

Note: the SFC compiler also extracts binding metadata from <script setup> and use it during template compilation. This is why the template can use Foo as a component here even though it’s returned from setup() instead of registered via components option.

Declaring Props and Emits

<script setup>
  import { defineProps, defineEmit } from 'vue'

  // expects props options
  const props = defineProps({
    foo: String,
  })
  // expects emits options
  const emit = defineEmits(['update', 'delete'])
</script>
component interface

####### defineConponent function

Mainly returns the type definition of the component, the specific implementation is very simple

// vue-next/package/runtime-core/src/apiDefineComponent.ts 
// various definitions
...
// implementation, close to no-op
export function defineComponent(options: unknown) {
  return isFunction(options) ? { setup: options, name: options.name } : options
}

Define props

...
<script lang="ts">
import { defineComponent, PropType } from "vue";

export  default  defineComponent ( { 
  name : "HelloWorld" , 
  props : { 
    // shorthand 
    msg : String , 
    // complete 
    name : { 
      type : String  as  PropType < string > , 
      required : true , 
    } , 
  } , 
} ) ; 
</ script >

The pit that require does not work when extracting common props definitions

...
<script lang="ts">
import { defineComponent, PropType } from "vue";

const  PropsType  =  { 
    msg : String , 
    name : { 
        type : String  as  PropType < string > , 
        // required 
        required : true , 
    } , 
// solution: add as const, manually tell ts that this object is read-only 
}  as  const

export  default  defineComponent ( { 
    name : "HelloWorld" , 
    props : PropsType , 
    mounted ( )  { 
        // The name prompt here may also be undefined 
        // Reason: {required: true} here is not tried correctly because ts does not know This object is read-only, and only read-only can allow {required: true} to be tested correctly by ts 
        // View the comment mentioned in the source defineComponent definition: 
        // the Readonly constraint allows TS to treat the type of { required: true} as constant instead of boolean. 
        // PropsOptions extends Readonly<ComponentPropsOptions>, 
        this . name 
    } 
} ) ; 
</ script >
h function

Equivalent to createElement in React

// main.ts
import { createApp, defineComponent, h } from "vue";
// import App from "./App.vue";

import  HelloWorld  from  './components 
/ 
HelloWorld.vue ' // ts verification does not pass the transposition of require's writing // import img from'./assets 
/logo.png 
' // eslint error: require is not allowed, cancel the current line Eslint check const  img  =  require ( './assets/logo.png' )  // eslint-disable-line

// Use the h function to simulate app files (sfc: single file component) to generate vue components. App files are almost compiled like this 
const  App  =  defineComponent ( { 
    render ( )  { 
        // h is equivalent to createElement in React for creating nodes 
        / / Native nodes use strings 
        // Parameters: node type, attributes, child nodes 
        return  h ( 'div' ,  { id : 'app' } ,  [ 
            h ( 'img' ,  { 
                alt : "Vue logo" , 
                // image It can’t be displayed, because the image address vue-loader in the template will be addressed. Here you need to import the image yourself 
                // src: "./assets/logo.png", 
                src : img , 
            }),
            h(HelloWorld, {
                msg: "Welcome to Your Vue.js + TypeScript App",
                name: "naxies",
            })
        ])
    }
})

createApp(App).mount("#app");
Source code

In fact, it is the package of createVNode. It is also possible to replace the following code h with createVNode

// vue-next/package/runtime-core/src/apiDefineComponent.ts 
// various definitions
...
// Actual implementation
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
  const l = arguments.length
  if (l === 2) {
    if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
      // single vnode without props
      if (isVNode(propsOrChildren)) {
        return createVNode(type, null, [propsOrChildren])
      }
      // props without children
      return createVNode(type, propsOrChildren)
    } else {
      // omit props
      return createVNode(type, null, propsOrChildren)
    }
  } else {
    if (l > 3) {
      children = Array.prototype.slice.call(arguments, 2)
    } else if (l === 3 && isVNode(children)) {
      children = [children]
    }
    return createVNode(type, propsOrChildren, children)
  }
}

In addition to the three parameters like h, the createVNode parameters also have some optimized parameters, and vue-loader will make some optimizations through these

setup
// App.vue
 < template > 
  < img  alt =" Vue logo " src =" ./assets/logo.png " />
   < HelloWorld  msg =" Welcome to Your Vue.js + TypeScript App " name =" naxies " / >
   < h2 > {{state.age}}~ </ h2 > 
  <!-- It is not necessary. The value is because sfc will judge whether it is a ref currently --> 
  < h2 > ref {{ageRef}}~ </ h2 > 
  < h2 >computedAgeRef {{computedAgeRef}} ~ </ h2>
</template>

<script lang="ts">
import { defineComponent, reactive, ref, computed, watchEffect } from 'vue'
import HelloWorld from './components/HelloWorld.vue'

export  default  defineComponent ( { 
  name : 'App' , 
  components : { 
    HelloWorld , 
  } , 
  mounted ( )  { 
    // The ref in this is a proxy or you don't need to write .value, it will automatically determine the 
    console . log ( this . ageRef ) 
  } , 
  // and the same data only once at initialization 
  Setup ( the props ,  { slots ,  attrs ,  EMIT } )  { 
    // returns: sfc returns the object 
    const  State  = reactive({
      age: 18
    })
    setInterval(() => {
      state.age += 1
    }, 1000)

    // Return {value: xxx} structure object, value is 
    const  ageRef  =  ref ( 18 ) 
    setInterval ( ( )  =>  { 
      ageRef . Value  +=  1 
    } ,  1000 )

    // computed
    const computedAgeRef = computed(() => {
      return ageRef.value + 2
    })

    // watchEffect: will be executed when all reactive function references and changes to ref 
    watchEffect ( ( )  =>  { 
      // assignment will be executed each time ageRef 
      Console . Log ( ageRef . Value ) ; 
    } )

    // Can't use {...state} to return, what is returned is not a responsive 
    return  { 
      state , 
      ageRef ,
      computedAgeRef
    }
  }
})
</script>

Setup returns the usage of render function

// main.ts
import { createApp, defineComponent, h, reactive, ref } from "vue";
// import App from "./App.vue";

import HelloWorld from './components/HelloWorld.vue'
const img = require('./assets/logo.png') // eslint-disable-line

// setup returns the writing of the render function ======================= 
const  App  =  defineComponent ( { 
    setup ( )  { 
        // You can also write other codes here 
        // Both reactive and ref are used to define reactive data. It is more recommended to define complex data types. ref is more recommended to define basic types. It can be simply understood that ref is a secondary packaging of reactive and access to data defined by ref One more time. value 
        const  state  =  reactive ( { 
            age : 18 
        } )

        const ageRef = ref(18)
        setInterval(() => {
            ageRef.value += 1
        }, 1000)

        // Return to the render function 
        return  ( )  =>  { 
            // Put this sentence outside the render function and the page display will not dynamically change according to the timer 
            // Because setup is only executed once, ageRefNum is always the initialized value 
            // reactive or The change of ref will re-execute this render function to form a dom tree, so the final value reading needs to be executed in render 
            const  ageRefNum  =  ageRef . value 
            return  h ( 'div' ,  { id : 'app' } ,  [ 
                h ( 'img' ,  { 
                    alt : "Vue logo" , 
                    src : img , 
                } ) , 
                h ( HelloWorld,  { 
                    msg : "Welcome to Your Vue.js + TypeScript App" , 
                    name : "naxies" , 
                } ) , 
                // You can get the value of the closure 
                h ( 'h2' ,  state . age ) , 
                h ( 'h2' ,  'ageRef '  +  ageRefNum ) 
            ] ) 
        } 
    } 
} )

createApp(App).mount("#app");
watch
// watching a getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// directly watching a ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

// A watcher can also watch multiple sources at the same time using an array
const firstName = ref('');
const lastName = ref('');

watch([firstName, lastName], (newValues, prevValues) => {
  console.log(newValues, prevValues);
})

firstName.value = "John"; // logs: ["John",""] ["", ""]
lastName.value = "Smith"; // logs: ["John", "Smith"] ["John", ""]
jsx

https://github.com/vuejs/jsx-next

Install the plugin with:

npm install @vue/babel-plugin-jsx -D

Then add the plugin to .babelrc:

{
  "plugins": ["@vue/babel-plugin-jsx"]
}

Advantages: ts can be verified at compile time (ts cannot recognize the type exported in the vue file, it is a unified type), flexible to use (return and reuse html fragments through functions, etc.), and can use instructions

json-schema

Used to define json data, verify data, multi-terminal universal

Official draft: json-schema.org

ajv

js library of json-schema, https://ajv.js.org/

// or ESM/TypeScript import
// import Ajv from "ajv"
// Node.js require:
const Ajv = require("ajv").default
const addFormats = require("ajv-formats")
const localize = require('ajv-i18n');

// Simple 
let  schema  =  { 
    type : 'string' , 
    minLength : 10 , 
} 
// More complicated 
schema  =  { 
    type : 'object' , 
    properties : { 
        name : { 
            type : 'string' , 
            maxLength : 10 , 
        } , 
        age : { 
            type : 'number' , 
        } , 
        pets : { 
            type :'array' , 
            // The first way of defining 
            // items: { 
            // type:'string', 
            // }, 
            // The second kind of 
            items : [ 
                {  type : 'string' ,  } , 
                {  type : 'number ' ,  } 
            ] 
        } , 
        email : { 
            type : 'string' , 
            format : 'email' , 
        } , 
        testFormatProperty : { 
            type : 'string' , 
            format : 'testFormat' ,
        } , 
        testKeywordProperty : { 
            type : 'string' , 
            testKeyword : 'testKeyword' , 
            // provided by ajv-errors 
            // Any rule error will only display this error message 
            // errorMessage:'You are wrong again! ', 
            // Distinguish keyword setting error message 
            errorMessage : { 
                type : 'What you passed is not a string, don't you even recognize a string! ' , 
                testKeyword : 'Guess where you are wrong again! ' 
            } , 
        } 
    } , 
    required : [ 'name' ,  'age' ] , 
}

let data = 'naixes'
data = {
    name: 'naixes',
    age: 18,
    pets: ['egg core', 2],
    testFormatProperty: 'testFormat',
    testKeywordProperty: 'hello!',
}

// To use ajv-errors, you need to pass in {allErrors: true} 
const  ajv  =  new  Ajv ( { allErrors : true } )  // options can be passed, eg {allErrors: true} 
// version 7 and above formats need to install a plug-in separately 
addFormats ( ajv )

// 引入 ajv-errors 库
require ( 'ajv-errors' ) ( ajv )

// 定义 定义 format 
ajv . addFormat ( 'testFormat' ,  ( data )  =>  { 
    return  data  ===  'testFormat' 
} )

// Custom keyword 
ajv . addKeyword ( { 
    keyword : 'testKeyword' ,

    // Method 1: Call when validate 
    // validate: xx = (schema, data) => { 
    // console.log('schema', schema,'data', data); 
    // // Custom error message, You can also use the library to customize error information 
    // xx.errors = [ 
    // { 
    // keyword:'testKeyword', 
    // dataPath:'/testKeywordProperty', 
    // schemaPath:'#/properties/testKeywordProperty/testKeyword', 
    / / params: {}, 
    // message:'You are wrong! ' 
    //} 
    //] 
    // return data.length === 3 
    // },

    // Method 2: Call when compile 
    // compile: (sch, parentSchema) => { 
    // console.log('sch', sch,'parentSchema', parentSchema); 
    // // To return a function 
    // return () => true 
    // }, 
    // // The schema of the value received by this keyword 
    // metaSchema: (),

    // Method 3: Equivalent to combining multiple schema 
    macros : ( sch ,  parentSchema )  =>  { 
        // console.log('sch', sch,'parentSchema', parentSchema); 
        // To return a schema 
        return  { 
            minLength : 10 , 
        } 
    } , 
    metaSchema : { } ,

    // Will overwrite the custom error message in Method 1 
    errors : false , 
} )

const validate = ajv.compile(schema)
const valid = validate(data)
if (!valid) {
    localize.zh(validate.errors)
    console.log(validate.errors)
}

installation:npm i ajv

####### Formats

format: some commonly used validation rules, only for string and number types

installation:npm i ajv-formats

From version 7 Ajv does not include formats defined by JSON Schema specification - these and several other formats are provided by ajv-formats plugin.

To add all formats from this plugin:

import Ajv from "ajv"
import addFormats from "ajv-formats"

const  ajv  =  new  Ajv ( ) 
addFormats ( ajv )
  • date: full-date according to RFC3339.
  • time: time with optional time-zone.
  • date-time: date-time from the same source (time-zone is mandatory).
  • duration: duration from RFC3339
  • uri : full URI.
  • uri-reference: URI reference, including full and relative URIs.
  • uri-template: URI template according to RFC6570
  • url (deprecated): URL record.
  • email: email address.
  • hostname: host name according to RFC1034.
  • ipv4: IP address v4.
  • ipv6: IP address v6.
  • regex: tests whether a string is a valid regular expression by passing it to RegExp constructor.
  • uuid: Universally Unique IDentifier according to RFC4122.
  • json-pointer: JSON-pointer according to RFC6901.
  • relative-json-pointer: relative JSON-pointer according to this draft.

Custom format

ajv.addFormat (name: string, format: Format): Ajv

type Format =
  | true // to ignore this format (and pass validation)
  | string // will be converted to RegExp
  | RegExp
  | (data: string) => boolean
  | Object // format definition (see below and in types)

Add format to validate strings or numbers.

If object is passed it should have properties validatecompare and async:

interface FormatDefinition { // actual type definition is more precise - see types.ts
  validate: string | RegExp | (data: number | string) => boolean | Promise<boolean>
  compare: (data1: string, data2: string): number // an optional function that accepts two strings
    // and compares them according to the format meaning.
    // This function is used with keywords `formatMaximum`/`formatMinimum`
    // (defined in [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package).
    // It should return `1` if the first value is bigger than the second value,
    // `-1` if it is smaller and `0` if it is equal.
  async?: true // if `validate` is an asynchronous function
  type?: "string" | "number" // "string" is default. If data type is different, the validation will pass.
}

Formats can be also added via formats option.

####### keywords

Four ways to customize keywords

ajv.addKeyword({
  keyword: "constant",
  validate: (schema, data) =>
    typeof schema == "object" && schema !== null ? deepEqual(schema, data) : schema === data,
  errors: false,
})

const schema = {
  constant: 2,
}
const validate = ajv.compile(schema)
console.log(validate(2)) // true
console.log(validate(3)) // false

const schema = {
  constant: {foo: "bar"},
}
const validate = ajv.compile(schema)
console.log(validate({foo: "bar"})) // true
console.log(validate({foo: "baz"})) // false
ajv.addKeyword({
  keyword: "range",
  type: "number",
  compile([min, max], parentSchema) {
    return parentSchema.exclusiveRange === true
      ? (data) => data > min && data < max
      : (data) => data >= min && data <= max
  },
  errors: false,
  metaSchema: {
    // schema to validate keyword value
    type: "array",
    items: [{type: "number"}, {type: "number"}],
    minItems: 2,
    additionalItems: false,
  },
})

const schema = {
  range: [2, 4],
  exclusiveRange: true,
}
const validate = ajv.compile(schema)
console.log(validate(2.01)) // true
console.log(validate(3.99)) // true
console.log(validate(2) )  // false 
console . log ( validate ( 4 ) )  // false
ajv.addKeyword({
  keyword: "range",
  type: "number",
  macro: ([minimum, maximum]) => ({minimum, maximum}), // schema with keywords minimum and maximum
  // metaSchema: the same as in the example above
})

####### Error message

Display Chinese error message:

Install ajv-i18n

var Ajv = require('ajv'); // version >= 2.0.0
var localize = require('ajv-i18n');

// option `i18n` is required for this package to work
var ajv = Ajv({ allErrors: true });
var validate = ajv.compile(schema);
var valid = validate(data);

if (!valid) {
    // ru for Russian
    localize.ru(validate.errors);
    // string with all errors and data paths
    console.log(ajv.errorsText(validate.errors, { separator: '\n' }));
}

Custom error message

...
 // Custom keyword 
ajv . AddKeyword ( { 
    keyword : 'testKeyword' ,

    // Method 1: When calls validate 
    validate : XX  =  ( Schema ,  Data )  =>  { 
        Console . Log ( 'Schema' ,  Schema ,  'Data' ,  Data ) ; 
        // custom error message 
        XX . Errors  =  [ 
            { 
                keyword : 'testKeyword' , 
                dataPath : '/testKeywordProperty' , 
                schemaPath : '#/properties/testKeywordProperty/testKeyword' , 
                params : { }, 
                message : 'You are wrong! ' 
            } 
        ] 
        return  data . length  ===  3 
    } ,

    // overrides custom error messages 
    // errors: false, 
} ) 
. . .

: : Ajv-errors

const Ajv = require("ajv").default
const ajv = new Ajv({allErrors: true})
// Ajv option allErrors is required
require("ajv-errors")(ajv /*, {singleError: true} */)

const schema = {
  type: "object",
  required: ["foo"],
  properties: {
    foo: {type: "integer"},
  },
  additionalProperties: false,
  errorMessage: "should be an object with an integer property foo only",
}

const validate = ajv.compile(schema)
console.log(validate({foo: "a", bar: 2})) // false
console.log(validate.errors) // processed errors

Component definition and interface

Props
<JsonSchemaForm
	schema={schema}   
    value={value}
    locale={locale}
    onChange={handleChange}
    contextRef={someRef}
    uiSchema={uiSchema}
></JsonSchemaForm>
schema

json schema object, used to define the data, but also the basis for us to define the form

value

The data result of the form, you can change the value from the outside. When the form is edited, the onChangevalue will be revealed through

It should be noted that because Vue uses variable data, if we change valuethe object address every time the data changes , the entire form will need to be re-rendered, which will cause performance degradation. From a practical point of view, the internal modification of the value of the field of the object we passed in will basically not have any side effects, so we will use this method to implement it. In other words, if it valueis an object, then JsonSchemaFormthe value modified from the inside will not change the valueobject itself. We will still trigger onChange, because the user may need to perform some actions after the form changes.

onChange

The callback method will be triggered when there is any change in the form value, and the new value will be returned

locale

Language, use the language of the ajv-i18nspecified error message

contextRef

You need to pass in a vue3 Refobject, we will mount the doValidatemethod on this object , you can pass

const yourRef = ref({})

onMounted(() => {
  yourRef.value.doValidate()
})

<JsonSchemaForm contextRef={yourRef} />

In this way, the form is actively verified.

uiSchema

Make some customizations to the display of the form, the types are as follows:

export interface VueJsonSchemaConfig {
  title?: string
  descrription?: string
  component?: string
  additionProps?: {
    [key: string]: any
  }
  withFormItem?: boolean
  widget?: 'checkbox' | 'textarea' | 'select' | 'radio' | 'range' | string
  items?: UISchema | UISchema[]
}
export interface UISchema extends VueJsonSchemaConfig {
  properties?: {
    [property: string]: UISchema
  }
}
vue-jss

The css in js library is based on the secondary development of jss

npm i vue-jss jss jss-preset-default

monaco-editor

Code editor

/* eslint no-use-before-define: 0 */

import { defineComponent, ref, onMounted, watch, onBeforeUnmount, shallowReadonly, shallowRef } from 'vue'

import * as Monaco from 'monaco-editor'

import type { PropType, Ref } from 'vue'
import { createUseStyles } from 'vue-jss'

// 返回一个方法
const useStyles = createUseStyles({
  container: {
    border: '1px solid #eee',
    display: 'flex',
    flexDirection: 'column',
    borderRadius: 5
  },
  title: {
    backgroundColor: '#eee',
    padding: '10px 0',
    paddingLeft: 20,
  },
  code: {
    flexGrow: 1
  }
})

export default defineComponent({
  props: {
    code: {
      type: String as PropType<string>,
      required: true
    },
    onChange: {
      type: Function as PropType<(value: string, event: Monaco.editor.IModelContentChangedEvent) => void>,
      required: true
    },
    title: {
      type: String as PropType<string>,
      required: true
    }
  },
  setup(props) {
    // must be shallowRef, if not, editor.getValue() won't work
    const editorRef = shallowRef()

    const containerRef = ref()

    let _subscription: Monaco.IDisposable | undefined
    let __prevent_trigger_change_event = false // eslint-disable-line

    onMounted(() => {
      const editor = editorRef.value = Monaco.editor.create(containerRef.value, {
        value: props.code,
        language: 'json',
        formatOnPaste: true,
        tabSize: 2,
        minimap: {
          enabled: false,
        },
      })

      _subscription = editor.onDidChangeModelContent((event) => {
        console.log('--------->', __prevent_trigger_change_event) // eslint-disable-line
        if (!__prevent_trigger_change_event) { // eslint-disable-line
          props.onChange(editor.getValue(), event);
        }
      });
    })

    onBeforeUnmount(() => {
      if (_subscription)
        _subscription.dispose()
    })

    watch(() => props.code, (v) => {
      const editor = editorRef.value
      const model = editor.getModel()
      if (v !== model.getValue()) {
        editor.pushUndoStop();
        __prevent_trigger_change_event = true // eslint-disable-line
        // pushEditOperations says it expects a cursorComputer, but doesn't seem to need one.
        model.pushEditOperations(
          [],
          [
            {
              range: model.getFullModelRange(),
              text: v,
            },
          ]
        );
        editor.pushUndoStop();
        __prevent_trigger_change_event = false // eslint-disable-line
      }
      // if (v !== editorRef.value.getValue()) {
      //   editorRef.value.setValue(v)
      // }
    })

    const classesRef = useStyles()

    return () => {

      const classes = classesRef.value

      return (
        <div class={classes.container}>
          <div class={classes.title}><span>{props.title}</span></div>
          <div class={classes.code} ref={containerRef}></div>
        </div>
      )
    }
  }
})

Plug-in: monaco-editor-webpack-plugin

  const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')

module.exports = {
  chainWebpack(config) {
      config.plugin('monaco').use(new MonacoWebpackPlugin())
  },
}

Display item

app.tsx

import { defineComponent, reactive, ref, Ref, watchEffect } from "vue";
import {createUseStyles} from 'vue-jss'

import MonacoEditor from './components/MonacoEditor'
import demos from './demos'
import SchemaForm from '../lib'

// TODO:在lib中export
type Schema = any
type UISchema = any

function jsonToString(data: any) {
    return JSON.stringify(data, null, 2)
}

// 样式
const useStyles = createUseStyles({
    ...
})

export  default  defineComponent ( { 
    setup ( )  { 
        // Record the currently selected demo 
        const  selectedRef : Ref < number >  =  ref ( 0 )

        const  demo : { 
            schema : Schema | null , 
            data : any , 
            uiSchema : UISchema | null , 
            schemaCode : string , 
            dataCode : string , 
            uiSchemaCode : string , 
        }  =  reactive ( { 
            schema : null , 
            data : { } , 
            uiSchema : { } , 
            schemaCode: '' , 
            dataCode : '' , 
            uiSchemaCode : '' , 
        } )

        watchEffect(() => {
          const index = selectedRef.value
          const d = demos[index]
          demo.schema = d.schema
          demo.data = d.default
          demo.uiSchema = d.uiSchema
          demo.schemaCode = jsonToString(d.schema)
          demo.dataCode = jsonToString(d.default)
          demo.uiSchemaCode = jsonToString(d.uiSchema)
        })

        const handleChange = (v: any) => {
          demo.data = v
          demo.dataCode = jsonToString(v)
        }

        // 工厂函数
        const handleCodeChange = (
          field: 'schema' | 'data' | 'uiSchema',
          value: string
        ) => {
            let json: any
            try {
                json = JSON.parse(value)
                demo[field] = json
                ;(demo as any)[`${field}Code`] = value
            } catch (err){
                console.log(err);
            }
        }
        const handleSchemaChange = (v: string) => handleCodeChange('schema', v)
        const handleDataChange = (v: string) => handleCodeChange('data', v)
        const handleUISchemaChange = (v: string) => handleCodeChange('uiSchema', v)

        const classesRef = useStyles()

        return () => {
            const classes = classesRef.value
            const selected = selectedRef.value

            return (        // <StyleThemeProvider>
              // <VJSFThemeProvider theme={theme as any}>
              <div class={classes.container}>
                <div class={classes.menu}>
                  <h1>Vue3 JsonSchema Form</h1>
                  <div>
                    {demos.map((demo, index) => (
                      <button
                        class={{
                          [classes.menuButton]: true,
                          [classes.menuSelected]: index === selected,
                        }}
                        onClick={() => (selectedRef.value = index)}
                      >
                        {demo.name}
                      </button>
                    ))}
                  </div>
                </div>
                <div class={classes.content}>
                  <div class={classes.code}>
                    <MonacoEditor
                      code={demo.schemaCode}
                      class={classes.codePanel}
                      onChange={handleSchemaChange}
                      title="Schema"
                    />
                    <div class={classes.uiAndValue}>
                      <MonacoEditor
                        code={demo.uiSchemaCode}
                        class={classes.codePanel}
                        onChange={handleUISchemaChange}
                        title="UISchema"
                      />
                      <MonacoEditor
                        code={demo.dataCode}
                        class={classes.codePanel}
                        onChange={handleDataChange}
                        title="Value"
                      />
                    </div>
                  </div>
                  <div class={classes.form}>
                    <SchemaForm
                      schema={demo.schema}
                      onChange={handleChange}
                      value={demo.data}
                    />
                    {/* <SchemaForm
                      schema={demo.schema!}
                      uiSchema={demo.uiSchema!}
                      onChange={handleChange}
                      contextRef={methodRef}
                      value={demo.data}
                    /> */}
                  </div>
                </div>
              </div>
              // </VJSFThemeProvider>
              // </StyleThemeProvider>
            )
        }
    }
})

Component development

SchemaItem: Distribute different types of schemas

fields/xxxField: specific implementation

Simple node rendering
<template>
    <input type="text" :value="value" @input="handleChange" />
</template>

<script lang='ts' setup>
import {defineProps} from 'vue'

import {FieldPropsDefine, Schema} from '../types'

// Using FieldPropsDefine directly will report an error 
const props =  defineProps ({ ... FieldPropsDefine })

const handleChange = (e: any) => {
    props.onChange(e.target.value)
}
</script>
Complex node rendering
Object

Do not refer to components circularly, it is difficult to find errors, use plug-ins to remindnpm i -D circular-dependency-plugin

Use provide to solve the problem of circular references

Source code: apiInject.ts

// /packages/runtime-core/src/apiInject.ts
import { isFunction } from '@vue/shared'
import { currentInstance } from './component'
import { currentRenderingInstance } from './componentRenderUtils'
import { warn } from './warning'

export interface InjectionKey<T> extends Symbol {}

export  function  provide < T > ( key : InjectionKey < T > | string | number ,  value : T )  { 
	// Determine whether it is in the component rendering process, that is, you can only use 
    if  ( ! currentInstance )  { 
        if  ( __DEV__ )  { 
            warn ( `provide() can only be used inside setup().` ) 
        } 
    }  else  { 
        let  provides  =  currentInstance .provides 
        // by default an instance inherits its parent's provides object 
        // but when it needs to provide values ​​of its own, it creates its 
        // own provides object using parent provides object as prototype. 
        // this way in `inject` we can simply look up injections from direct 
        // parent and let the prototype chain do the work. 
        // By default, the instance inherits the Provides object of its parent object 
        // But when it needs to provide its own value, it will use the parent provided object as prototype to create objects 
        // in this way, the `inject`, we can look directly from the parent object directly injected into the query so that the prototype chain works 
        const  parentProvides  = 
              the currentInstance of . parent  &&  the currentInstance of . parent . the Provides 
        IF  ( parentProvides  == = provides) {
            provides = currentInstance.provides = Object.create(parentProvides)
        }
        // TS doesn't allow symbol as index type
        provides[key as string] = value
    }
}

...
export function inject(
  key: InjectionKey<any> | string,
  defaultValue?: unknown,
  treatDefaultAsFactory = false
) {
  // fallback to `currentRenderingInstance` so that this can be called in
  // a functional component
  const instance = currentInstance || currentRenderingInstance
  if (instance) {
    // #2400
    // to support `app.use` plugins,
    // fallback to appContext's `provides` if the intance is at root
    const provides =
      instance.parent == null
        ? instance.vnode.appContext && instance.vnode.appContext.provides
        : instance.parent.provides

    if (provides && (key as string | symbol) in provides) {
      // TS doesn't allow symbol as index type
      return provides[key as string]
    } else if (arguments.length > 1) {
      return treatDefaultAsFactory && isFunction(defaultValue)
        ? defaultValue()
        : defaultValue
    } else if (__DEV__) {
      warn(`injection "${String(key)}" not found.`)
    }
  } else if (__DEV__) {
    warn(`inject() can only be used inside setup() or functional components.`)
  }
}

Optimization: Encapsulate common logic

Array
/** 
* Three cases 
* 
* Single-type arrays, with no length limitation by default, are all the same type 
* { 
* items: {type:'string'} 
*} 
* Fixed length, which types are respectively 
* { 
* items: [ 
* {type:'string'}, 
* {type:'numer'} 
*] 
*} 
* enum means optional 
items * { 
* items: {type:'string', enum: ['1', '2']} 
*} 
*/

Fixed-length array rendering

Single type array rendering

Contains optional array rendering

Download Details:

Author: Naixes

Source Code: https://github.com/Naixes/vue3-json-schema-form

#vue #vuejs #javascript

What is GEEK

Buddha Community

Vue3 json Schema form
Brain  Crist

Brain Crist

1600347600

SCHEMAS in SQL Server -MS SQL Server – Zero to Hero Query Master

Introduction

This is part 3 of “MS SQL Server- Zero to Hero” and in this article, we will be discussing about the SCHEMAS in SQL SERVER. Before getting into this article, please consider to visit previous articles in this series from below,

A glimpse of previous articles
Part 1

In part one, we learned the basics of data, database, database management system, and types of DBMS and SQL.

Part 2
  • We learned to create a database and maintain it using SQL statements.
  • Best practice methods were also mentioned.

#sql server #benefits of schemas #create schema in sql #database schemas #how to create schema in sql server #schemas #schemas in sql server #sql server schemas #what is schema in sql server

Brandon  Adams

Brandon Adams

1625637060

What is JSON? | JSON Objects and JSON Arrays | Working with JSONs Tutorial

In this video, we work with JSONs, which are a common data format for most web services (i.e. APIs). Thank you for watching and happy coding!

Need some new tech gadgets or a new charger? Buy from my Amazon Storefront https://www.amazon.com/shop/blondiebytes

What is an API?
https://youtu.be/T74OdSCBJfw

JSON Google Extension
https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa?hl=en

Endpoint Example
http://maps.googleapis.com/maps/api/geocode/json?address=13+East+60th+Street+New+York,+NY

Check out my courses on LinkedIn Learning!
REFERRAL CODE: https://linkedin-learning.pxf.io/blondiebytes
https://www.linkedin.com/learning/instructors/kathryn-hodge

Support me on Patreon!
https://www.patreon.com/blondiebytes

Check out my Python Basics course on Highbrow!
https://gohighbrow.com/portfolio/python-basics/

Check out behind-the-scenes and more tech tips on my Instagram!
https://instagram.com/blondiebytes/

Free HACKATHON MODE playlist:
https://open.spotify.com/user/12124758083/playlist/6cuse5033woPHT2wf9NdDa?si=VFe9mYuGSP6SUoj8JBYuwg

MY FAVORITE THINGS:
Stitch Fix Invite Code: https://www.stitchfix.com/referral/10013108?sod=w&som=c
FabFitFun Invite Code: http://xo.fff.me/h9-GH
Uber Invite Code: kathrynh1277ue
Postmates Invite Code: 7373F
SoulCycle Invite Code: https://www.soul-cycle.com/r/WY3DlxF0/
Rent The Runway: https://rtr.app.link/e/rfHlXRUZuO

Want to BINGE?? Check out these playlists…

Quick Code Tutorials: https://www.youtube.com/watch?v=4K4QhIAfGKY&index=1&list=PLcLMSci1ZoPu9ryGJvDDuunVMjwKhDpkB

Command Line: https://www.youtube.com/watch?v=Jm8-UFf8IMg&index=1&list=PLcLMSci1ZoPvbvAIn_tuSzMgF1c7VVJ6e

30 Days of Code: https://www.youtube.com/watch?v=K5WxmFfIWbo&index=2&list=PLcLMSci1ZoPs6jV0O3LBJwChjRon3lE1F

Intermediate Web Dev Tutorials: https://www.youtube.com/watch?v=LFa9fnQGb3g&index=1&list=PLcLMSci1ZoPubx8doMzttR2ROIl4uzQbK

GitHub | https://github.com/blondiebytes

Twitter | https://twitter.com/blondiebytes

LinkedIn | https://www.linkedin.com/in/blondiebytes

#jsons #json arrays #json objects #what is json #jsons tutorial #blondiebytes

Dicanio Rol

Dicanio Rol

1597067523

JSON Schema, Schema.org, JSON-LD: What’s the Difference?

Recently, I have seen several questions like “what’s the difference between JSON-LD and JSON Schema” or “can I use JSON Schema and Schema.org”. I come from a linked data background (which is close to the world of Schema.org) but have recently started using JSON Schema a lot and I have to admit that there is no trivial answer to these questions. There is the obvious similarity in the standard names like “Schema” and “JSON”. If you compare the Schema.org page for  Person to this  example on the JSON Schema page, you have to admit that they kind of look alike. Combine this with the fact that Schema.org touts JSON-LD, which — by design — very much looks like regular JSON completes the confusion. So there definitely are enough reasons to write this article.

JSON Schema

JSON Schema is to JSON what XML Schema is to XML. It allows you to specify the structure of a JSON document. You can state that the field “email” must follow a certain regular expression or that an address has “street_name”, “number”, and “street_type” fields.  Michael Droettboom’s book  “Understanding JSON Schema” illustrates validation quite nicely with red & green examples.

The main use case for JSON Schema seems to be in JSON APIs where it plays two major roles:

  1. Clients and servers can validate request and response objects in a generic way. This makes development a lot easier, since the implementation can “outsource” these checks to a standard component. Once a message passed the validation, you can safely assume that the document adheres to the rules.
  2. As with any API, documentation is key when developers write code that uses it. JSON Schema is increasingly used to describe the structure of requests and responses by embedding it in an overall API description. Swagger is probably the most prominent example of this paradigm. Consider the pet-store example. Scroll all the way down on the page and you see this JSON Schema definition of “Pet”, which is a basic element in requests and responses of this API (you can find the actual JSON Schema embedded in the raw Swagger file — note that currently there are still some differences between the OpenAPI specification and JSON Schema which will be resolved with OpenAPI 3.1).

Image for post

As with all things related to code, reuse is a good idea. JSON Schema has the ability to import schemas using the $ref keyword. There are also efforts to share schemas.  JSON Schema Store is one example. Its main use case is to support syntax highlighting for editors, for instance when editing a swagger file. At the time of writing, it contains over 250 schemas including — drum-roll please / you certainly guessed it — Schema.org. These describe things like  Action and  Place. So the idea could be to centrally define JSON Schema building blocks that can be re-used in different APIs, making it easier to consume them, maybe even to the point where intelligent software can interact with APIs automatically. But before we get carried away, let’s have a look at Schema.org.

#schema #swagger #json-ld #json-schema

Hermann  Frami

Hermann Frami

1673587800

Angular: JSON Powered forms For Angular

Form.io Angular JSON Form Renderer

This library serves as a Dynamic JSON Powered Form rendering library for Angular. This works by providing a JSON schema to a <formio> Angular component, where that form is dynamically rendered within the front end application. This allows forms to be dynamically built using JSON schemas.

Angular Material

If you are looking for Angular Material support, then this is within a separate library @ https://github.com/formio/angular-material-formio

Running Demo

To run a demo of the Form.io Angular renderer, please follow these steps.

  1. Make sure you have the Angular CLI installed on your machine.
  2. Download the Angular Demo Application to your computer.
  3. With your terminal, type npm install
  4. Now type ng serve

This will startup an example application where you can see all the features provided by this module.

Here is the hosted demo application https://formio.github.io/angular-demo/

Using within your application

You can easily render a form within your Angular application by referencing the URL of that form as follows.

<formio src='https://examples.form.io/example'></formio>

You can also pass the JSON form directly to the renderer as follows.

<formio [form]='{
    "title": "My Test Form",
    "components": [
        {
            "type": "textfield",
            "input": true,
            "tableView": true,
            "inputType": "text",
            "inputMask": "",
            "label": "First Name",
            "key": "firstName",
            "placeholder": "Enter your first name",
            "prefix": "",
            "suffix": "",
            "multiple": false,
            "defaultValue": "",
            "protected": false,
            "unique": false,
            "persistent": true,
            "validate": {
                "required": true,
                "minLength": 2,
                "maxLength": 10,
                "pattern": "",
                "custom": "",
                "customPrivate": false
            },
            "conditional": {
                "show": "",
                "when": null,
                "eq": ""
            }
        },
        {
            "type": "textfield",
            "input": true,
            "tableView": true,
            "inputType": "text",
            "inputMask": "",
            "label": "Last Name",
            "key": "lastName",
            "placeholder": "Enter your last name",
            "prefix": "",
            "suffix": "",
            "multiple": false,
            "defaultValue": "",
            "protected": false,
            "unique": false,
            "persistent": true,
            "validate": {
                "required": true,
                "minLength": 2,
                "maxLength": 10,
                "pattern": "",
                "custom": "",
                "customPrivate": false
            },
            "conditional": {
                "show": "",
                "when": null,
                "eq": ""
            }
        },
        {
            "input": true,
            "label": "Submit",
            "tableView": false,
            "key": "submit",
            "size": "md",
            "leftIcon": "",
            "rightIcon": "",
            "block": false,
            "action": "submit",
            "disableOnInvalid": true,
            "theme": "primary",
            "type": "button"
        }
    ]
}'></formio>

This is a very simple example. This library is capable of building very complex forms which include e-signatures, columns, panels, field conditionals, validation requirements, and the list goes on and on.

Usage

To use this library within your project, you will first need to install it as a dependency.

npm install --save @formio/angular formiojs

You can now include the module in your Angular application like so.

import { FormioModule } from '@formio/angular';
@NgModule({
    imports: [ BrowserModule, CommonModule, FormioModule ],
    declarations: [ AppComponent ],
    bootstrap: [ AppComponent ]
})
export class AppModule { }

Included Libraries

This library is a combination of multiple libraries that enable rapid Serverless application development using Form.io. These libraries are as follows.

  1. Form Renderer - The form renderer in Angular
  2. Form Builder - The form builder in Angular
  3. Form Manager - The form management application used to manage forms.
  4. Authentication - Allows an easy way to provide Form.io authentication into your application.
  5. Resource - A way to include the Resources within your application with full CRUDI support (Create, Read, Update, Delete, Index)
  6. Data Table (Grid) - A way to render data within a Table format, which includes pagination, sorting, etc.

Click on each of those links to read more about how they work and how to utilize them to their fullest potential.

Demo Application

If you would like to run a demonstration of all the features of this module, then you can check out the Angular Demo Application, which is the code behind the following hosted application @ https://formio.github.io/angular-demo

Application Starter Kit

For help in getting started using this library, we created the angular-app-starterkit repository to help you get started with best practices with using Form.io within an Angular application. You can try this applicatoin by downloading that application and then doing the following.

npm install
npm start

Full Documentation

To read up on the full documentation of this library, please check out the Wiki Page

About Form.io

Form.io is a combined form and data management API platform created for developers who are building "Serverless" form-based applications. Form.io provides an easy drag-and-drop form builder workflow allowing you to build complex forms for enterprise applications quickly and easily. These forms are then embedded directly into your application with a single line of code that dynamically renders the form (using Angular or React) in your app while at the very same time generating the RESTful API to support those forms. The Form.io platform also offers numerous 3rd-party services that are fully integrated into the form building process allowing you to extend the power and capability of your apps while saving time and effort.

You can use this renderer with Form.io by simply pointing the src parameter to the URL of the form. For example, the following URL points to the JSON schema of a form built on Form.io.

https://pjmfogrfqptslvi.form.io/test

To render this form, you simply provide that URL to the <formio> directive like so.

<formio src="https://pjmfogrfqptslvi.form.io/test"></formio>

Not only will this render the form, but it will also submit that form to the provided API endpoint.

Download Details:

Author: formio
Source Code: https://github.com/formio/angular 
License: MIT license

#serverless #angular #json #schema #forms 

Yogi Gurjar

1600307723

Laravel 8 Form Example Tutorial - Complete Guide

Laravel 8 form example. In this tutorial, i would love to show you how to create form in laravel. And how to insert data into database using form in laravel 8.

How to Submit Form Data into Database in Laravel 8

  1. Step 1 – Install Laravel 8 Application
  2. Step 2 – Configuring Database using Env File
  3. Step 3 – Create Model & Migration File For Add Blog Post Form
  4. Step 4 – Create Routes
  5. Step 5 – Creating Controller
  6. Step 6 – Create Blade File For Add Blog Post Form
  7. Step 7 – Start Development Server
  8. Step 8 – Run Laravel 8 Form App On Browser

https://laratutorials.com/laravel-8-form-example-tutorial/

#insert form data into database using laravel #laravel bootstrap form #laravel post forms #laravel 8 form tutorial #laravel 8 form example #laravel 8 form submit tutorial