当前位置: 主页 > 前端开发

简单的前端表单验证-jq简单表单验证

发布时间:2023-02-12 16:24   浏览次数:次   作者:佚名

肖鹏:灵雀云前端开发工程师,Kubernetes官方社区sig-ui兴趣组成员

(). 本文是肖鹏老师以Angular响应式表单为例对Kubernetes实战经验的分享与总结。

文本:

Kubernetes 集群的用户在日常工作中经常需要接触 Deployment 等 Kubernetes 对象。 熟悉Kubernetes的朋友就会知道,虽然Kubernetes的对象结构强调可移植性,但不同的对象有着相似的设计理念,但即使是最熟练的系统运维或开发工程师也未必会把玩Kubernetes对象。

为了让用户更方便的操作Kubernetes对象,灵雀云Kubernetes发行版的前端界面提供了一个人性化的UI形式方案。 用户可以通过UI形式更方便地编辑Kubernetes对象,同时还提供了YAML格式和UI形式实时转换的功能。

读者可以先从这个Deployment形式demo中体验一下实时互转的效果(注:此demo为本文单独开发,与灵雀云产品无关):

jq 前端表单验证_简单的前端表单验证_jq简单表单验证

困难

YAML 是 Kubernetes 对象最常见的表示和修改形式。 对于前端来说,如果我们需要以一种同时支持表单和YAML的方式来编辑Kubernetes资源简单的前端表单验证,那么最后我们都不得不退回到编辑YAML。

从数据表示格式来看,表单的模型数据是一个JSON对象,而YAML是一个字符串。 YAML 和表单数据之间的转换类似于序列化和反序列化的过程。 我们需要在转换的边界使用js-yaml等库进行转换。

其实数据形式的问题还是比较容易解决的。 难点在于YAML表示的Kubernetes资源对象模型与表单数据之间的转换。 在实践过程中,表单数据与K8S对象之间的转换有很多问题需要克服。 例如:

实施方案推导

模板驱动的表单

每个 Angular 表单开发人员都应该接触过两种不同的 Angular 表单实现思想,模板驱动表单和响应式表单。 一些开发人员可能会将反应式表单与动态表单混淆,但实际上这两个概念彼此没有任何关系。 不熟悉的同学可以看看Angular官网的表单介绍,给出了以下两者的区别:

总的来说:1)反应式表单更健壮:它们更具可扩展性、可重用性和可测试性。 如果表单是您应用程序的关键部分,或者您已经在使用响应式编程模式构建应用程序,请使用响应式表单。 2) 模板驱动的表单对于向您的应用程序添加简单的表单很有用,例如邮件列表注册表单。 它们很容易添加到应用程序中,但不像反应式表单那样容易扩展。 如果您有非常基本的表单需求和足够简单的逻辑,可以使用模板进行管理,请使用模板驱动的表单。

用模板驱动的表单编写前端表单真的很容易:给定任意数据对象,通过[(ngModel)]指令将需要的字段与模板的表单控件进行数据绑定; 根据实际需要简单的前端表单验证,绑定一些需要的表单验证指令就完成了。 鹅姐姐!

但是,一旦这样做,用户就将数据的“权限”交给了模板,留下了对数据的实际控制权,只能被动地接受数据更新、表单状态和生命周期、数据验证等。从模板。 事件。 对于复杂表单的业务逻辑,你很难通过这种模式扩展到大规模、复杂的表单数据逻辑处理。

反应形式和受控组件

使用Angular响应式表单对于初学者来说有点啰嗦和麻烦:为了维护表单的状态,我们需要显式地创建一套完整的表单控制器对象层次结构,并通过FormGroup / FormControl等指令传递这个对象绑定到模板上的表单控件。 乍一看,Angular 的响应式形式的思想似乎有点违背了现在的 MV* 设计模式,因为它把一些本可以由框架隐式管理的工作暴露给了开发者自己,增加了很多额外的工作工作。

熟悉 React 实现表单控件的人应该了解 React 的受控组件和非受控组件的概念。 通过受控组件,用户可以通过单一数据流的思想掌握表单控件数据的实际控制权。 但是,对于实际完整的表单应用场景,用户还需要处理表单提交状态、表单验证逻辑等信息。 Angular框架内置了一套相对成熟稳定的响应式表单解决方案,帮助开发者在开发表单相关业务的同时,更加可控地管理表单的状态。

Angular 表单控件的基础:Control Value Accessor

Angular 的表单控件的神奇之处与 React 的受控组件的思想非常相似,都是典型的单项数据流处理模式。 另外,无论是模板驱动表单还是响应式表单,都是围绕ControlValueAccessor接口设计实现的。

如果组件提供注入到模板的 DI 上下文中的 NG_VALUE_ACCESSOR 令牌并实现 ControlValueAccessor 接口,那么该组件可以绑定任何 Angular 表单指令。

ControlValueAccessor有两个关键点:registerOnChange和writeValue,这两个函数分别对应单次数据流从窗体内部到外部和从窗体外部到内部的数据变化。

适配器模式

聪明的朋友肯定会注意到,Control Value Accessor接口在调用onChange和writeValue时,并不要求表单数据格式与输入一致。 因此,我们可以在业务表单控件组件中实现本地资源对象和UI表单数据转换的逻辑。 比如上面提到的,我们可以通过它来实现一个键值对的表单控件。

简单的前端表单验证_jq 前端表单验证_jq简单表单验证

它暴露为一个普通的键值对控件,值类型为{ [key: string]: string }。 当数据由外向内时,可以通过Object.entries通过writeValue将键值对改为[string,string]的数组,最后绑定到窗体内部的FormArray控件上; 同时,当内部状态改变时,调用 onChange 之前将 [string, string][] 转换为外部的 { [key: string]: string } 对象类型。

如果我们以窗体控件为界,那么这个界线的两侧就是Host上绑定的窗体控件NgControl和窗体控件内部的响应式窗体。 使用 onChange 和 writeValue,我们可以围绕控件值访问器实现部分适配器模式。

通过这个思路,我们可以继续延伸:

由于基于adapter模式进行数据转换的思想,对于每个表单控件,我们可以通过onChange和writeValue这两个接口来改变数据和表单的模型,从而实现UI不一致的需求数据模型和实际暴露的数据模型。

对于复杂的资源对象表单,我们可以将问题分解为多个子表单控件组件,每个子表单控件组件实现适配器模式。 同时,各个子窗体控件组件仍然可以按照同样的方式进行拆装。 这样,通过不断递归拆解资源对象,配合表单的组合嵌套,最终实现复杂的表单树。

嵌入式表单的实现隔离了复杂表单的实现逻辑。 每个子窗体控件虽然对外暴露的是一个窗体控件数据,但内部却是一个完整的窗体。 父(宿主)组件可以完全忽略子表单组件控件的内部处理逻辑,例如表单错误处理、数据转换等,同时由于K8s的设计,很多子表单可以复用在不同的资源中,降低我们的开发成本。

表单控件本身提供K8s数据的同时,也可以表示为一个独立的K8s对象资源。 我们可以将部分相关的业务逻辑完全封装在这个表单控件组件内部,实现精神与动作的统一。 这个非常重要。 通过这样,我们可以更容易地划分K8s资源的问题范围,更快地对代码结构做出判断,减少开发和维护成本的精神负担。

也有一些开发者倾向于将业务处理逻辑分离出来,不放在组件内部,让组件只负责一层薄薄的视图渲染逻辑。 我觉得不是不可以,但是从复杂表单组件嵌套和复用的角度来看,本文使用的方法可能更容易维护。

由于上述实现过程具有比较规范的方法,我们可以设计一个标准的开发K8s资源对象形式的实现范式。 这种范式可以大大减轻开发人员在开发和维护复杂表单实现时的思考负担。 上面的过程利用递归的思想简单有效地拆解了一个问题:

Kubernetes 对象的反应式表单开发范例

中心思想

我总结了这个开发范式的几个关键点:

过程

为任何 Kubernetes 对象开发表单的过程可以总结如下:

1.了解目标Kubernetes对象的基本功能,对其YAML Schema有基本概念。 A。 由于我们前端人员对YAML字段的透明度高,修改灵活度足够,所以我们需要了解相关k8s对象的业务/特性。 2. 编写目标API对象的TypeScript类型(接口/类型等)。 3.将k8s对象类型拆解成一系列的子对象,将每一个可复用的子对象封装成一个单独的表单组件。 A。 例如PodSpec、Container、Env等。 4.对每个拆解出来的子对象表单组件,实现表单到对象的转换。 5.组合子对象形式,最终组合成一个完整的K8S对象形式

后面我们会以部署表单为例,详细讲解流程细节。

用例分析:部署表单

熟悉 Deployment 对象的结构

先参考官网Deployment的API文档,输出一组TypeScript接口供后续参考:

jq 前端表单验证_jq简单表单验证_简单的前端表单验证

部署表单拓扑

对于部署形式,我们分为3种主要形式:

jq 前端表单验证_简单的前端表单验证_jq简单表单验证

K8s资源对象表单控件组件-模板

最外层的组件,对象的使用者仍然可以使用模板驱动的形式将视图双向绑定到数据:

jq简单表单验证_jq 前端表单验证_简单的前端表单验证

编写内部模板更容易:单个表单由普通表单控件(如select、input等)和其他子对象表单控件(如pod-spec-form)组成。

部署模板使用反应形式:

简单的前端表单验证_jq简单表单验证_jq 前端表单验证

K8s资源对象表单控制组件-controller

资源对象组件控制器(即TS部分)的职责如下:

组件初始化时,需要生成响应式表单控件树。 根据实战,我总结出以下经验:

例如,为了部署表单,我们需要生成具有以下结构的表单控件:

jq 前端表单验证_jq简单表单验证_简单的前端表单验证

控件需要暴露为一个普通的窗体控件,内部窗体的错误向上传递给Host上的NgControl指令。 关键是实现ControlValueAccessor接口:

setFormByResource 和 setResourceByForm

前面提到,在为表单设置资源对象数据时,可以直接调用form.patch Value(form Model),这样可以快速填充结构化表单。 一个问题是Angular在调用patch Value方法时将表单Model的schema限制为表单结构的子集,但一般来说,表单的控制器结构有时不需要覆盖完整的schema(比如status字段等) .).

我设计了setForm By Resource函数来解决这个问题。 方法是遍历表单层级中的所有控制器,以控制器的路径为线索,在资源对象上找到对应的值,然后设置到表单控制器中; 同时当form的一个controller是FormArray时,根据数据源的大小进行缩放。

setResource ByForm函数与setForm ByResource函数相反。 当表单数据写回资源对象时,使用它遍历表单层次结构控制器并在资源对象上设置值。 通过setResourceByForm,我们还可以在UI数据写回资源对象时,避免触及UI表单没有的字段,避免数据转换过程中丢失数据的可能。

ng-resource-form-util 资源表单助手库

一个表单的单项数据流基本上可以用一个简单的图来表示:

jq 前端表单验证_jq简单表单验证_简单的前端表单验证

由于controller的用法和行为在大多数情况下是高度相似的,灵雀云前端将表单的这些功能和行为抽象封装到BaseResourceFormComponent基类中,并在这里开源代码。

上述过程中还遗留了一些关键细节。 我简单提一下这个想法,整理成下面的问答:

Q:表单如何处理表单验证,甚至异步表单验证逻辑,并将表单验证状态向上传递? A:将内部表单的错误处理封装成一个validator,绑定到DI中从NgControl获取的Validator。 Q:如何处理内部表单的提交逻辑? A:从DI获取FormGroupDirective,监听表单提交事件。 Q:某些形式的数据不属于当前Kubernetes对象。 如何处理这种情况? A:使用 ng-content、TemplateRef 或 Portal 将与资源无关的模板映射到组件中。

大家可以通过DEMO以及DEMO的源码继续了解一个比较完整的方案是什么样子的。

写在最后

基于本文的表单开发范式,灵雀云前端开发可以快速实现K8s相关资源对象表单,实现YAML与资源对象相互转换的需求。

本文介绍一种基于Angular响应式表单编辑K8s对象的总体实现思路和范式。 事实上,这个想法不仅限于 Angular 或 K8s 对象。 读者甚至可以根据自己的需要,使用本文的最终形式,将本文的思想带入到React或Vue应用中。

谢谢阅读!