说明
通过嵌套继承
,UIOTOS
实现了页面组件化
,并支持对复杂界面或逻辑层层封装,本质上可以理解为是一款面向对象
的前端图形化编程
工具,并且图灵完备。这也是能够达到媲美代码开发,可以按照指定原型,实现高度定制化前端应用的关键所在。
在嵌套(基础篇)中,介绍了嵌套的基本原理和用法,本篇继续讲嵌套涉及的更多内容。
原理
基础篇的原理有介绍到,嵌套是通过容器组件实现的,本篇将介绍细节。
加载流程
容器组件在页面路径(display)
属性设置时,会触发执行UIOTOS
中的加载引擎。以下是容器通过引擎逐层加载主要工作流程(支持无限多层页面嵌套):
此过程中,属性嵌套继承
尤为重要,这样才能让内嵌页
的组件和属性,在上层页面
就能配置,跟组件自身属性一样使用。这也是实现页面组件化
的关键。
了解html
iframe
网页嵌套的知道,一个网页嵌套到另一个网页中,能实现界面整合。如果要进一步功能融合,需要代码开发(postMessage
),实现网页之间通信。
本质上,iframe
嵌套只是界面集成。UIOTOS
中如果页面和页面之间只有嵌套,没有属性继承,那么效果跟iframe
没有多大区别。
页面嵌套
+属性继承
,UIOTOS
可以实现完全无代码,将一个页面的功能无缝融合到另一个页面中,不仅仅只停留简单集成上。
从这个维度上说,UIOTOS
是iframe
做网页嵌套的全新解决方案。当然,UIOTOS
嵌套的更多都是工具搭建的页面,而非任意网页。
属性标识(串)
在名词中有提到属性名称和属性标识。通常区别在于属性名称
可以变动,而属性标识
不能随意变动。
以多国语言版切换为例,属性面板
上的显示文字即属性名称,需要切换不同版本显示;属性标识则不能变,否则配置好的页面逻辑,切换会导致异常!如下所示:
连线操作属性(目标)和关联属性(来源),提示显示的是属性名称,但是连线配置是以属性标识来存放。
同样,在属性继承中,用的也是属性标识。并且尤为注意的是,继承到容器的属性标识,与内嵌属性标识不是一样的,否则极容易发生标识冲突。
同一个组件中,属性标识(比如
输入框
的值内容
,标识为a:value
)不可能重复。但是不同组件可以相同(比如文本框
的值内容
,标识也为a:value
)。
因此同一个页面中,这两个组件的属性,都继承到上层容器时,如果都用属性标识,就无法区分了!
UIOTOS
中,属性每经过一次嵌套继承,就会按照规则,自动在原先的属性标识中追加一段,生成追加继承到上层容器的属性标识,并且确保在上层容器的所有属性中唯一、不重复。
这类属性标识通常称为属性keyURL
,因为随着嵌套层数增加,属性标识会越来越长,类似URL一样形成一串,也叫属性标识串
。如下所示:
- 属性
**keyURL**
规则:
- 带上页面名称(区分不同的页面)
- 带上组件标签(确保同一个页面内不重复)
- 带上一个默认为0的索引编号(确保容器组件多个内嵌页时能区分)
- 属性
**keyURL**
示例:
a:上层页>0>gv1>a:次底层>0>gv1>a:底层页>0>cbox1>a:value
a:次底层>0>gv1>a:底层页>0>cbox1>a:value
a:底层页>0>cbox1>a:value
a:value
- 继承属性名称规则:
属性面板显示的是属性名称
,每经过一层嵌套名称前面加一个点.
- 属性keyURL面板显示:
在属性绑定面板中,属性项显示的就是keyURL
(继承属性默认省去了前缀a:
未显示)。
赋值同步
原理
通过前面的了解,可以知道,页面直接运行时,是作为独立的前端应用,而且属于顶层页面。当被嵌套时,页面的性质就自动从应用变成了组件,且无需任何改动。
此时,一个重要问题就需要考虑了:
继承的属性,是用于
读取
内嵌属性值,还是用于设置
内嵌属性值?(关于读写,参见属性按操作分类)
两种情况都有。
继承属性用于设置值通过继承,给内嵌页属性设置新的值,运行时输出不同内容。
如下所示,内嵌页直接运行时,输出按钮组件的文字。
通过嵌套继承该属性后,在上层页面对基础属性重新设置值,并锁定生效后,以上层设置的值为准输出。
继承属性用于读取值通过继承,获取到内嵌页的属性值,读取输出在上层页面。
如下所示,将内嵌页用于接受输出结果的属性(文本框值内容),通过嵌套继承到上层页面,并将继承的属性值输出到上层弹窗显示。
此外,另一个重要问题也需要考虑:
逐层嵌套的属性,同一时刻,每层页面继承的属性,值一定相同还是可以不相同?
一定相同!
**上层对继承属性赋值(比如编辑时),将逐层向下传递。**举例如下,三层嵌套后从顶层到底层,属性标识keyURL
的变化:
a:上层页>0>gv1>a:次底层>0>gv1>a:底层页>0>cbox1>a:value
a:次底层>0>gv1>a:底层页>0>cbox1>a:value
a:底层页>0>cbox1>a:value
a:value
只有底层组件属性值改变,组件界面等才会变化。因此编辑时对继承属性赋值,必须逐层向下同步,直到触发底层组件的属性赋值,才能实现对继承属性配置就像配置内置属性一样,起到所见即所得的效果。
**对组件输入设置(运行交互时),属性值将逐层向上传递。**举例如下,三层嵌套后从底层到顶层,属性标识keyURL
的变化:
a:value
a:底层页>0>cbox1>a:value
a:次底层>0>gv1>a:底层页>0>cbox1>a:value
a:上层页>0>gv1>a:次底层>0>gv1>a:底层页>0>cbox1>a:value
与编辑时相反,运行时交互,直接操作的是底层组件,让其属性值发生变化。此时属性值需要逐层向上传递,将值逐层同步给继承的属性,直到最顶层。否则起不到上面“继承属性用于读取值”的目的。
从这里可以看出,不论哪种情形,属性对应的各层继承属性,要么自上而下,要么自下而上,都会同步成相同的值。
基于以上两个问题的分析,对于多层嵌套继承的情形,得出2点结论:
- 属性值的配置,要么同步覆盖到下层,要么被下层的值覆盖,不存在同一个继承的属性,不同层属性值不一样的情况。
- 多层嵌套中,任何一层触发的属性值变化,需要向上同步到顶层、向下同步到底层,保持多层属性继承中,各层的值保持一致。
机制
UIOTOS
提供了表单变量(iotos.form
、iotos.formReset
、iotos.formValue
)用于属性绑定(什么是表单?)。
目的是用来解决嵌套加载时,属性赋值同步等问题(加载后的同步则不区分表单绑定
类型),详见如下:
form绑定
-
属性为
form
绑定的,上层继承的属性将默认为formReset
。 -
将继承的属性改为
form
绑定后,设置的属性值将保持住,刷新、重加载、运行预览,将向下同步给内嵌属性,采用上层设置的值。 -
属性绑定面板中的
锁定
按钮可一键设置form
绑定。(参见属性绑定中的锁定有什么用?)
formReset绑定 -
属性为
formReset
绑定的,上层继承也将默认为formReset
。 -
继承属性保持默认
formReset
绑定的,在刷新加载、运行预览页面时,将采用内嵌属性值,向上同步给继承属性,覆盖设置的值。
formValue绑定 -
属性为
formValue
绑定的,上层继承也将默认为formValue
。 -
用于属性赋值同步时,与
formReset
绑定相同,没有区别。 -
纯表单专有用途,详见关于纯表单(formValue)。
**注意:初始继承时才默认对应(form→formReset等)**内嵌表单绑定和继承的绑定,默认对应只在初始继承时才有:form
→formReset
;formReset
→formReset
;formValue
→formValue
。继承后的修改不会同步影响。比如重新修改继承属性的绑定,不会影响内嵌属性;同样,反过来也是一样。
那么,在多层嵌套中,自上而下看(从顶层到底层),如果某个继承属性如果有中间不连续的form
绑定: -
顶层页面加载后继承的属性,初始值应该采用哪一层?
-
底层组件属性的最终值,采用哪一层
**form**
绑定的属性值?
示例如下:> 对value
属性5层嵌套继承,自上而下继承属性分别为formReset
→formReset
→**form**
→formReset
→formReset
→**form**
**formReset**
a:次顶层页>0>gv1>a:更上层页>0>gv1>a:上层页>0>gv1>a:次底层>0>gv1>a:底层页>0>cbox1>a:value
**formReset**
a:更上层页>0>gv1>a:上层页>0>gv1>a:次底层>0>gv1>a:底层页>0>cbox1>a:value
**form**
**a:上层页>0>gv1>a:次底层>0>gv1>a:底层页>0>cbox1>a:value**
**formReset**
a:次底层>0>gv1>a:底层页>0>cbox1>a:value
**formReset**
a:底层页>0>cbox1>a:value
**form**
a:value
答案:用近原则,自上往下看,均采用最靠近当前层做fom绑定值!> 判断技巧:将formReset
理解为透明玻璃,form
为不透明。当多层叠放一起后,自上往下看,自然看到的是最靠近上层的不透明层!
也就是说,某层属性
form
绑定,而有更上层继承属性也有form
绑定时,页面加载会以更上层form
绑定的属性值为准,直到采用最靠近顶层的,以此值向上显示到继承属性,向下同步给组件底层属性。
上面示例将采用3的属性值,即:
**form**
**a:上层页>0>gv1>a:次底层>0>gv1>a:底层页>0>cbox1>a:value**
**深入:多层继承的属性都有连线操作,各层的执行顺序如何?**分两种情况,页面加载时,是自上而下加载并同步初始值;连线执行时,底层属性值是自下而上冒泡同步。
- 页面加载时
先对最上层form
绑定的属性,取作初始值逐层向下同步,然后再由底层页面开始,逐层执行各页面连线逻辑。
- 连线执行时
页面运行会触发连线操作,过程中改变某个组件的属性值时,会逐层向上层继承的属性值冒泡同步(放到下一个时序,先执行完当前层的连线逻辑),并触发上层页面的连线执行(如果有)。
实例:API接口组件嵌套封装,属性继承且各层分别执行连线逻辑:
这里对该机制仅做简要说明,多数时候无需关心。
如果涉及复杂场景较,需了解更多,可参考工具中有对应单元测试示例。文档后续持续补充。
使用
- 嵌套(基础篇)中的使用,已经介绍了
容器嵌套
的用法。 - 机制中介绍了嵌套中对
继承属性
的表单绑定
,尤其是锁定
(form
绑定),实现属性配置在页面重加载时不被还原。 - 现在详细介绍嵌套中属性的
纯表单
(formValue
)绑定,以及对应的功能和使用。
纯表单介绍(formValue
)
纯表单
是表单相关概念中,中仅跟formValue
有关的绑定、属性、组件、页面、容器,以及数据。比如:
纯表单绑定指的是对属性指定用formValue
绑定,不用form
或formReset
。
纯表单属性有做了纯表单绑定(formValue
)的属性。
纯表单数据由纯表单属性构成的表单数据(不包括form
、formReset
绑定的属性),并且字段不再是属性标识,可以自定义,详见自定义属性别名。
表单属性可以用于输出表单数据(详见什么是表单属性,有什么用处?),且数据的字段固定为属性标识。不过,实际的表单(通常API接口请求需要用到)通常需要指定字段甚至结构。如下所示:
以下两个示例,页面都是一样的:
- 内嵌页有两个输入框
- 上层页面通过对话框容器,嵌套内嵌页
- 点击按钮,触发对话框弹窗
- 对话框点击确定后,表单数据输出给另一个对话框显示(连线对外操作时,未关联属性时输出表单对象数据)。
默认表单数据输出示例示例中,对话框内嵌页有两个输入框,都继承了属性到上层。默认情况下对话框输出的表单对象数据,就是所有表单属性(包括继承的)的:
示例的输出表单对象数据如下,实际上我们需要的是标绿色的属性值:
{
"a:show": false,
"a:titleText": "对话框",
"a:display": "./表单内嵌页.json",
"a:innerLayoutMode": "fullScreen",
"a:debugOccupied": false,
"a:footerButtonsText": [
"取消",
"确定"
],
"a:表单内嵌页>0>input1>a:value": "aaa",
"a:表单内嵌页>0>input1>a:instantTrigger": true,
"a:表单内嵌页>0>input1>a:initialTrigger": false,
"a:表单内嵌页>0>input1>a:readOnly": false,
"a:表单内嵌页>0>input2>a:value": "bbb",
"a:表单内嵌页>0>input2>a:instantTrigger": true,
"a:表单内嵌页>0>input2>a:initialTrigger": false,
"a:表单内嵌页>0>input2>a:readOnly": false,
"a:_bindEvents_0": "*",
"a:_bindEvents_1": "*"
}
纯表单格式输出示例如下所示,输入账号、密码,输出的表单数据是只有这两个字段的键值对,并且字段可以服务要求,通常这才是实际需要的表单数据,与上面默认的表单数据相差比较大。
{
"username": "aaaa",
"password": "bbbbb"
}
上述第二个示例,正是通过纯表单(formValue)
实现的。完整特性如下:
- 筛选指定属性输出,过滤掉无关的属性。
- 可以对指定字段(属性标识),通过别名修改成想要的。
- 别名支持
../
以及./xx/yy
等“相对路径”形式,可以调整字段的层次结构,实现高度定制化的表单对象数据结构。- 多层嵌套下,对上层继承属性设置别名,即可代替内嵌属性的别名设置(如果有),无需内嵌页做修改。
- 容器组件
纯表单
下输出什么格式,连线表单操作时(一根连线),提供一样格式的对象数据,就能将值回写到对应的组件属性上。
接下来介绍具体的使用步骤。
纯表单使用(formValue)
属性绑定纯表单
在表单页面中,对要作为纯表单的组件和属性,设置formValue
绑定,这样可以用于筛选指定的属性,过滤掉无关的。
form
/formReset
/formValue
都属于表单绑定
,默认输出的表单数据,这三类的属性都有包含,通常比较多,很多无关的。
**如何设置?**点击属性右侧的绑定 → 弹窗中
变量
右边点击选择
→ 二级弹窗列表中选择
iotos
下面的formValue
→ 点击确定
即可。如下所示:
注意:已经做过嵌套的,需逐层确保继承的属性都是
formValue
绑定。只有初始嵌套继承,上层属性才会默认绑定formValue。每层如何快速查找属性绑定类型?参见属性搜索。
容器勾选纯表单
表单页面需通过嵌套容器
才能起作用:输出表单数据,或数据回写到表单组件。容器组件
都有一个纯表单
属性(开关类型,默认未勾选),需要勾选,才能输出仅包含formValue
属性的纯表单数据,如下所示:
自定义属性别名
勾选纯表单
属性后,容器对外输出的表单数据。该数据只包含了纯表单属性,但字段和结构通常还不符合要求,需要自定义。具体规则和使用如下:
**字段默认组件标识,值为纯表单属性键值对(只有一个时,值就为属性值)。**在前面关于纯表单(formValue)示例中,不勾选和勾选容器的纯表单
选项,输出数据的对应字段变化如下:
a:表单内嵌页>0>input1>a:value
变为input1
a:表单内嵌页>0>input2>a:value
变为input2
上面是对两个输入框
分别只对一个属性(值内容
)做纯表单绑定的情况,如果设置多个纯表单属性,如下所示:
- 示例中第一个
输入框
(标识为input1
)中,新增用户数据
(userData
)属性,作纯表单
绑定(formValue
)。- 上层对话框容器容器,继承内嵌的
用户数据
属性,并且确认也是纯表单属性(formValue
绑定)。
输出数据对照如下:
{
"input1": { //input1为第一个输入框的标签
"value": "aaa", //value为第一个纯表单属性标识
"userData": "测试数据" //userData为第二个纯表单属性标识
},
"input2": "bbb" //input2为第二个输入框的标签
}
{
"input1": "aaa", //input1为第一个输入框的标签
"input2": "bbb" //input2为第二个输入框的标签
}
规则结论如下:
- 表单只有一个纯表单属性,那么字段为该组件的标签,值为属性值。
- 表单有多于一个纯表单属性,字段为组件标签,值为这多个属性构成的标识键值对对象。
也可以说:默认以当前组件的标签作为字段,以属性标识键值对作为值。而当只有一个纯表单属性时,将直接取属性值作为值,省去属性标识键值对。
**属性标识可自定义别名,支持相对名称形式(…/或./xx/yy)调整字段结构。**在属性绑定面板中,每个属性标识都有对应的别名配置,如下所示:
别名通常只用于纯表单属性和数据用途。当属性做了formValue
绑定,通过别名就可以调整纯表单数据的字段名称和结构,具体规则为:
以未配置别名时输出数据为调整目标,
../
为向上跳出一级字段(忽略此前的上级字段),./xx
为向下新增一级名为xx
字段。
其中,../
和./xx/yy
这种“相对名称”,可完全参照相对路径理解。
示例1:每个表单组件通常仅设置一个纯表单属性,用组件标识自定义即可。> - 组件只有个一个纯表单属性时,表单数据字段为组件标识,对应值就是属性值。此时修改组件标签就可以自定义表单字段,无需用属性别名。
- 在前面基础上,修改第一个输入框的标签
input1
临时改成username
,第二个由input2
临时改成password
。并且取消input1
中的userData
这个属性的formValue``纯表单
绑定,确保这两个组件分别都只有一个纯表单属性。- 修改标签后,需要再到容器组件中做属性继承!
{
"input1": "aaa",
"input2": "bbb"
}
{
"username": "aaa",
"password": "bbb"
}
示例2:组件有两个纯表单属性,对其中一个别名字段,调整到上一级。> - 在前面基础上,对值内容
属性(标识为value
)的别名设置为../username
,其他不动。
{
"input1": { //input1为输入框的标签
"value": "aaa", //value为第一个纯表单属性标识
"userData": "测试数据" //userData为第二个纯表单属性标识
},
"input2": "bbb"
}
{
"username": "aaa", //username为别名提取到上级的字段名
"input1": "测试数据" //剩下标识为userData的属性,因数量只有一个,因此值直接给组件标签字段!
"input2": "bbb",
}
示例3:组件有两个以上纯表单属性,其中一个用别名调整到上一级,以及增加下一级字段等其他情形。> - 前面的示例中只有两个纯表单属性,其中一个调整到上一级后,剩下的一个,被自动当成唯一纯表单属性来处理了。
- 本示例增加一个到总共三个纯表单属性,看下输出对比效果。
- 在示例2的基础上,新增
占位提示符
属性(标识为palceholder
)为纯表单属性,其他不动。
注意,需要属性继承:
操作过程:
{
"input1": {
"value": "aaa", //该属性将设置别名../username
"userData": "测试数据",
"placeholder": "请输入"
},
"input2": "bbb"
}
{
"username": "aaa", //原先在input1内的value,跳到外层指定username字段了!
"input1": {
"userData": "测试数据",
"placeholder": "请输入"
},
"input2": "bbb"
}
如果再进一步,将属性别名改成…/username/newField:
输出如下:
注意,因为跳到上一级已经到第一级字段了,没有更上级字段,因此属性别名再加一层
../
(成为../../username.newTag
),输出结果也一样。
{
"username": { //属性别名设置了../username/newTag,将增加下一级自定义字段newTag
"newTag": "aaa"
},
"input1": {
"userData": "测试数据",
"placeholder": "请输入"
},
"input2": "bbb"
}
如果再进一步,将三个属性别名,以及第二个输入框的属性别名,都分别加上…/,依次改成这样:
../username
../password
../desc
{
"username": "aaa", //input1的value,别名为../username
"password": "测试数据", //input1的username,别名为../password
"email": "请输入", //input1的placeholder,别名为../email
"desc": "bbb" //input2的value,别名为../desc
}
继续,不用
**../**
,改用**./**
,即保持当前层,只增加下级字段的情况。去掉所有纯表单的别名,仅对input1的value属性,别名设置为**./username**
:
输出如下:
注意:因为调整别名之前,输出的数据中,属性标识
value
已经是在组件标签input1
下一级,因此用./username
,修改的时候value
字段本身,并非保持不变仅增加下一级字段!
{
"input1": {
"username": "aaa", //value属性的别名调整成./username时。如果不调整,此处字段会是"value"
"userData": "测试数据",
"placeholder": "请输入"
},
"input2": "bbb"
}
在上面示例基础上继续,别名设置为
**./username/newField**
:
输出如下:
{
"input1": {
"username": { //原先字段为value,设置别名后这里修改为username,并且增加下级字段newField
"newField": "aaa"
},
"userData": "测试数据",
"placeholder": "请输入"
},
"input2": "bbb"
}
表单输入和输出
表单通常是要支持双向的(参见什么是表单?),这样才能满足数据的新增
、修改
、查看
等操作。UIOTOS
中纯表单的输入,跟输出的配置完全一致(同样也要求被赋值的容器组件,勾选纯表单
),具体规则为:纯表单输出
是什么格式的数据,输入以同样的格式即可!
也就是说,按照输出时的表单数据字段,输入时,只需要保持字段不变,填入相应的值,这样数据回写时,就能将数值对应到各自的纯表单组件和属性上,实现查看、编辑对表单数据的加载!
如下所示,通过按钮的用户数据属性,连线操作给对话框容器表单赋值(如何操作?):
以上介绍的是容器嵌套时的纯表单,如果要获取常规组件的纯表单数据,参见:常规组件纯表单如何获取?
Q&A
什么是表单?
表单是前端交互界面中极为常见的概念。比如用户登录
的时候,输入用户名
、密码
,点击登录(提交表单)。其中用于输入的界面叫表单界面
,提交的数据叫表单数据
(格式参见对象和键值对)。
表单通常要求是双向的,除了由表单界面
生成表单数据
用于提交,还要能将表单数据
(比表格的行数据)回写输入到表单界面上,用于查看或编辑等,如下所示:
在UIOTOS
中,表单相关的概念说明如下:
表单变量属性变量绑定中,专用于表单用途的变量:iotos.form
、iotos.formReset
、iotos.formValue
。(介绍是通常省去iotos.
前缀)
表单绑定对组件属性以表单变量(form
/formReset
/formValue
)进行的绑定。
表单属性组件中做了表单绑定的属性。详见表单属性以及用途。
表单组件有表单属性的组件,比如输入框、文本框、下拉框等,参见组件
-表单。
表单页面有表单组件的页面,与表单数据对应。
表单数据表单组件中全部表单属性构成的键值对数据对象(也叫表单对象
)。如果组件是容器,也包括表单页面嵌套后继承过来的表单属性键值对。
表单输出(表单组件)直接或(表单页面通过容器)间接输出表单数据。
表单输入把表单数据回写设置到表单页面、组件的属性上。
纯表单
如何对表单页面赋值?
我们知道,连线不关联属性时,除了API组件外,所有组件输出的都是组件的表单数据(参见连线不关联属性,则输出表单数据)。
那么对于已有的表单数据,如果赋值回写给表单组件呢?同样也是连线,而且是连入的时候不操作任何属性(即不选择目标属性。类比连出的时候不关联任何属性)!如下所示:
- 连线操作容器组件(必须有勾选了
纯表单
属性),弹出操作属性窗口时,不做任何选择,直接点击确定。- 随后弹出确定窗口,提示是否要执行表单操作,点击确定。
- 随后弹出关联属性列表窗口,正常关联即可,作为来源属性,点击确定,即完成了表单属性的连线操作。
常规组件纯表单如何获取?
Q:上面对纯表单使用的介绍,都是围绕容器组件进行的,基于页面嵌套和纯表单属性。也就是说要获基础组件的纯表单
数据,就需要通过容器嵌套来做。有没有更简便的方式?
A:有的,UIOTOS提供了工具函数组件获取组件属性值,能像容器那样也提供了一个纯表单属性,能实现对非容器组件的纯表单
数据的获取,用法跟纯表单使用一样,包括别名等,区别是不需要容器嵌套。效果如下所示: