Closed zhangfisher closed 2 months ago
本次PR为omi引入formAssociated和hook特性支持,说明如下:
PR
omi
formAssociated
hook
formAssociated特性目的为WebComponent引入表单功能,当开发扩展表单组件时需要此功能的支持。
WebComponent
@tag("my-input",{ formAssociated:true // 启用此功能 }) export default class MyInput extends Component { static props = { name:String, value:String } as any render(props:any){ return <div><input name={props.name} value={props.value} type="text"/></div> } }
然后在表单中就可以像普通标准input一样使用了,
input
<form> <my-input name="x" value={1}/> <my-input name="y" value={2}/> <my-input name="z" value={3}/> </form>
以上在my-input组件内部仅嵌入一个标准input组件,现在也支持组合多个input,甚至是任意表单内容。
my-input
以下是一个my-ipaddress的组件,支持分别输入IP地址的四个部分。
my-ipaddress
@tag("my-ipaddress",{formAssociated:true}) export default class MyInput extends Component { static props = { name:{ type:String, value:"ip" }, value:String } as any f1= createRef() f2=createRef() f3=createRef() f4=createRef() getFieldValue(){ return { // @ts-ignore [this.props.name]:`${this.f1.current.value}.${this.f2.current.value}.${this.f3.current.value}.${this.f4.current.value}` } } render(){ return <div> <input ref={this.f1} name="f1" value="192" type="text"/> <input ref={this.f2} name="f2" value="168" type="text"/> <input ref={this.f3} name="f3" value="0" type="text"/> <input ref={this.f4} name="f4" value="1" type="text"/> </div> } }
getFieldValue返回一个Record<string,any>,也就是form可以识别的表单字段,这样我们封装的WebCompoent就可以
getFieldValue
Record<string,any>
form
WebCompoent
"my-ipaddress
{x:1,y:2}
tag
{formAssociated:true}
formAssociated API
在实现formAssociated特性时,需要在component.tsx源码中侵入修改:
component.tsx
static formAssociated=true
formAssociatedCallback/formDisabledCallback/formResetCallback
但是考虑到formAssociated特性并不是所有WebComponent均需要的,所以直接修改Component.ts并不是好主意,最入是按需要注入,为此我引入hook机制,并且修改了@tag增加了options参数,可以按需注入相应的formAssociated功能。
Component.ts
@tag
options
通过引入hook机制,一些类似formAssociated功能的功能就可以比较方便地进行注入,并且与核心Component,ts解耦。
Component,ts
hook机制并不是为组件开发者准备的,而为omi的源码贡献者准备的,其实现机制如下:
component.ts
static hooks=ComponentHookRegistry
声明时HOOK
运行时HOOK
define
getClassStaticValue(this,'hooks
来读取所注册的
getClassStaticValue
hooks
目前设计的hook类型包括type ComponentHookType = 'define' | 'initial' | 'connected' | 'disconnected'
type ComponentHookType = 'define' | 'initial' | 'connected' | 'disconnected'
在使用@tag封装组件调用define定义组件前运行,为此本次提交修改了tag装饰器,在运行define定义组件前,从组件类的static hooks中读取到所有define类型的hook,然后运行。
static hooks
之所以要引入此define hook ,是因为在实现formAssociated特性时,需要提供static formAssociated=true和formAssociatedCallback/formDisabledCallback/formResetCallback等实例方法。当调用define创建webcomponent时,这些callback会被注册到DOM中,然后在组件被放到form内时调用。 这样,这些callback,只能直接声明在Component,而不能在组件实例化后动态注入。 因此,就必须引入define hook ,在调用define创建webcomponent前可以修改组件类.
define hook
webcomponent
callback
Component
在组件类构造时handleProps后调用,此时可以得到初始化的props,包含继承链的经过处理的props。
handleProps
props
connected
在connectedCallback运行后调用
connectedCallback
disconnected
在disconnectedCallback运行后调用
disconnectedCallback
Q: 为什么没有为formAssociated增加单元测试?
事实上一开始是写了单元测试的,但是运行时发现组件的attachInternals总是undefined,在坑里扑腾半天,发现原因是jest使用jsdom不支持formAssociated API,所以增加了examples/form用来放置一些运行示例以便进行源码调试。
attachInternals
undefined
jest
jsdom
examples/form
本次
PR
为omi
引入formAssociated
和hook
特性支持,说明如下:formAssociated
formAssociated
特性目的为WebComponent
引入表单功能,当开发扩展表单组件时需要此功能的支持。使用方式:
然后在表单中就可以像普通标准
input
一样使用了,组合表单字段:
以上在
my-input
组件内部仅嵌入一个标准input
组件,现在也支持组合多个input
,甚至是任意表单内容。以下是一个
my-ipaddress
的组件,支持分别输入IP地址的四个部分。getFieldValue
返回一个Record<string,any>
,也就是form
可以识别的表单字段,这样我们封装的WebCompoent
就可以my-input
一样的的简单表单字段"my-ipaddress
的组合表单字段getFieldValue
返回{x:1,y:2}
,一个WebCompoent
包含两个或多个表单字段。其他说明
formAssociated
功能作为一个可选功能,需要在tag
装饰器的第二个参数中{formAssociated:true}
来启用,这样才可以让WebComponent
识别到表单。formAssociated API
中的标准校验等功能,考虑在未来完善。WebCompoent
启用formAssociated
功能,其继承的组件也会同样具有该功能。hook机制
在实现
formAssociated
特性时,需要在component.tsx
源码中侵入修改:static formAssociated=true
formAssociatedCallback/formDisabledCallback/formResetCallback
等实例方法。但是考虑到
formAssociated
特性并不是所有WebComponent
均需要的,所以直接修改Component.ts
并不是好主意,最入是按需要注入,为此我引入hook
机制,并且修改了@tag
增加了options
参数,可以按需注入相应的formAssociated
功能。通过引入
hook
机制,一些类似formAssociated
功能的功能就可以比较方便地进行注入,并且与核心Component,ts
解耦。实现机制
hook
机制并不是为组件开发者准备的,而为omi
的源码贡献者准备的,其实现机制如下:component.ts
中增加一个·static hooks=ComponentHookRegistry
用来保存注册的hook
hook
的包含了声明时HOOK
和运行时HOOK
两类,声明时HOOK
是在使用@tag
封装组件调用define
定义组件时运行,运行时HOOK
在组件实例化后运行。define
定义组件和组件的生命周期中,通过getClassStaticValue(this,'hooks
)来读取所注册的
hook`并运行。getClassStaticValue
方法会读取继承链上的hooks
进行合并,所以hook
也可以在组件继承时得到运行。支持的hook
目前设计的
hook
类型包括type ComponentHookType = 'define' | 'initial' | 'connected' | 'disconnected'
define
在使用
@tag
封装组件调用define
定义组件前运行,为此本次提交修改了tag
装饰器,在运行define
定义组件前,从组件类的static hooks
中读取到所有define
类型的hook
,然后运行。之所以要引入此
define hook
,是因为在实现formAssociated
特性时,需要提供static formAssociated=true
和formAssociatedCallback/formDisabledCallback/formResetCallback
等实例方法。当调用define
创建webcomponent
时,这些callback
会被注册到DOM中,然后在组件被放到form
内时调用。 这样,这些callback
,只能直接声明在Component
,而不能在组件实例化后动态注入。 因此,就必须引入define hook
,在调用define
创建webcomponent
前可以修改组件类.`initial
在组件类构造时
handleProps
后调用,此时可以得到初始化的props
,包含继承链的经过处理的props
。connected
在
connectedCallback
运行后调用disconnected
在
disconnectedCallback
运行后调用其他说明
Q: 为什么没有为
formAssociated
增加单元测试?事实上一开始是写了单元测试的,但是运行时发现组件的
attachInternals
总是undefined
,在坑里扑腾半天,发现原因是jest
使用jsdom
不支持formAssociated API
,所以增加了examples/form
用来放置一些运行示例以便进行源码调试。