RIA base for union business
npm i -g jsdoc
cd {ub-ria}
jsdoc -c doc/conf.json
open doc/api/index.html
RIA base for union business
License: GNU General Public License v2.0
有用到的业务线需要注意下,不过这个接口应该很难用到。。
发现什么就到这里记一下:
Action
中的entityDescription
之类的字段,不再依赖config
模块。进一步说,所有的类都不要依赖config
模块,复用时config
是不打包走的Model
中写死,或者需要抽象一层出来做转换underscore
的统一改为common/util
,此模块继承underscore
RequestManager
理论上不需要entityName
和backendEntityName
了,这2个属性在RequestStrategy
中体现,但这并不是必须的,因此在ub-ria
中不再使用,仅作为一种最佳实践,项目需要的话自己继承config
中的type
不配模块id,改用ActionFactory
实现,工厂要负责组装的东西有:Action
、Model
、View
、Data
(可能有多个)、RequestStrategy
(这里需要entityName
和backendEntityName
),这导致ActionFactory
可能的构造函数参数会很复杂,因此干脆不用构造函数兼容各种情况了,使用属性来解决,未来会移交给IoCData
依赖于ajax
,但ajax
的配置是全局的,是否要做成模块级,有待根据需求决定,在两种方式间切换成本并不高Action
的group
参数也由外部组装,这并不属于Action
内部的业务逻辑,仅仅是Action
的一种标识,是由所在系统决定的把原来的addData
设定为protected
的,以便IoC的组装控制
AbstractBoxGroup.setProperties:230
if (changes.hasOwnProperty('rawValue')) {
/**
* @event change
*
* 值变化时触发
*/
this.fire('change');
}
注释中写的是值变化时触发。实际上未变化仍然触发了。
现在的实现是独立的h3结构,导致了内部在 titleElement 上的处理比较杂乱,放入 label 中正好利用了 esui 自身的控件树实现进行元素定位
当继承关系较深时,可能每个父类都会有自己的datasource
配置,比如:
- SingleEntityModel - 根据id加载单个
- DetailModel - 加载summary信息
- XxxDetailModel - 加载一些查询域信息和权限
类似这处多级的datasource
,都要在load
的时候进行合并后一起加载,但合并并非简单的扩展,需要考虑到并行还是串行的问题,并不简单
需要仔细地设计这一块
可以把文本框等做成模板块来供使用,如:
<!--
use: textbox(
prefix = 'slot-form',
id = 'name',
title = '广告位名称',
required = true
maxLength = ${rule.maxLength},
pattern = ${rule.charaterOnly}
)
-->
进一步也可直接生成表单中的一行:
<!--
use: textboxField(
prefix = 'slot-form',
id = 'name',
title = '广告位名称',
field = '名称',
required = true
maxLength = ${rule.maxLength},
pattern = ${rule.charaterOnly}
)
-->
要考虑的是类似这样的要抽出多少,现在管家中至少有:
listSearchBox(placeholder)
:列表的搜索框batchButton(statusName, status, command)
:批量操作按钮URL中对于实体名称是否要+s
这会带来困扰,例如:实体名称以s结尾,是否要+es?如果实体是man,复数是men?
不如都不+s
第一种:
var proto = {
constructor: function () {
},
foo: 1,
bar: function () {
}
};
var Child = Class.create(Parent, proto);
proto
有个对象语法,多用了4个空格的缩进;不能把私有方法混在中间,因为对象字面量不能断开第二种:
var proto = {};
proto.contructor = function () {
};
proto.foo = 1;
proto.bar = function () {
};
var Child = Class.craete(Parent, proto);
proto
上的还是闭包内部函数需要控件如下:
Uploader
RichSelector
系列ToggleButton
TogglePanel
Chart
系列以上控件均需按基础库的**重新审视一下并重构
当切换AbstractBoxGroup的值事,change事件会被连续触发两次
此功能的基本**是针对一些比较简单,但是多变的应用模块,可以采用由后端返回Action配置信息,前端通用的Action处理解析并提供相关的功能。
应用过程如下:
后端返回配置的描述:
"path": "module/entity/page",
"type": "form",
"model": {
"fieldName": "value",
"fieldName2": 123,
......
},
"view": {
"title": "模块描述",
"fieldSections": [
{
"name": "basicInfo",
"title": "基本信息",
"toggleable": false,
"fields": [
{
"name": "filedName",
"desc": "字段描述",
"ctrl": "textbox",
"option": {
"defaultValue": "请输入内容"
},
"rule": {
"maxLength": 100,
"required": true,
"pattern": "@rule.xxx"
},
"condition": { "${abc}=='1' && ${def}!= 'left' || ${ghi}== 'normal'" }
},
......
]
},
......
]
}
需要讨论的问题是:
1、关于配置中信息的组织,尤其Model
字段中的信息,是否用Field
字段中的value
来表示,还是独立放在Model
中?
2、模板类型和控件的需要支持到什么程度?
初步希望的模板类型包括了常见的基类控件,Textbox、Textarea、RadioButton、Checkbox、Selector以及其扩展类型;中期会根据需求支持Uploader、ColorPicker、ToggleSelector等类型;
3、condition个字段的支持程度?
当前设计仅支持到== != && ||
四种逻辑,不支持其它逻辑和括号
tableFields
挂在ListView
上,而不是闭包内变量getTableFields
获取,以便扩展当前第一要务,让ub-ria
能正确跑起来的基础,我会再去看下浩寅的实现,进行一些优化后更新esui
版本
业务项目基类若使用 uiEvents
和 uiProperties
绑定控件事件或者控件属性,那么子类便无法直接使用这两个属性。
可以类似 BaseModel
的 putDatasource
接口,为控件事件和控件属性也增加两个接口实现事件和属性的继承复用。
现在的设计中,一个Model
可能持有多个Data
,但是在销毁时,只会销毁一个主Data
,其它的几个没办法销毁,根本原因就是BaseModel
并不知道自己到底关联了几个Data
几个解决方案:
第一是把Data
完全作为一个模块来管理,构造函数中:
this.data.init({ slot: new SlotData(), creative: new CreativeData() });
使用:
this.data('slot').search(...)
this.data.slot.search(...)
第二是创建时注册,构造函数中:
this.slotData = this.addData(new SlotData());
this.creativeData = this.addData(new CreativeData());
使用:
this.slotData.search(...)
当切换AbstractBoxGroup的值事,change事件会被连续触发两次
如BES-C项目,在列表中单击“新建”后是自右向左出现一个抽屉式的层(离左边留一些空间),在其中加载表单页
这种方式和管家中的弹层其实是一个概念,只是视觉效果不同,因此我们需要做的是:
popActionDialog
,改叫openActionLayer
,用 layer 这样通用化的词来表达“这和主Action不是一个东西”的概念preventDefault()
阻止默认跳转然后打开层一个关键的问题是,管家依旧是直接跳转的,要如何去做兼容?
stopImmediatePropagation()
不让框架的执行,然后自己跳转在RequestManager
里,有几个方法是用来处理请求的参数统一化的,比如URL的规则等
在不同系统里,这个规则可能是不同的,但RequestManager
的子类实现中方法的主体是相同的,因此要把这个可变的点抽出来
因此添加一个RequestStrategy
,把一系列formatXxx
方法转到上面去,形成组合关系
RT
https://github.com/ecomfe/ub-ria/blob/master/src/mvc/ListAction.js#L95
这里调用了 e.args
,但是 mini-event
的 Event.js
构造函数里面把 args
内容放到 this
上了,没有 this.args
。
前后端接口的URL和数据、响应格式各系统均不同,因此把原来BaseData
中的东西再拿回去
如textbox
、textboxField
等
在用户在登录状态时( /users/current/status),是否获取用户的基础信息
为了安全,建议公开的信息越少越好,
现在想到的是
userName 用户名(非登录帐号)
newMessageCount 未读信息数
这在有些情况下用得着,
一般在用户登陆后右上角都会显示用户名及显示是否有未读信息。
buildAction
函数new Model
时未添加entityDescription
,列表批量删除时会用到entityDescription
原本CSS隔离的最简单方法就是利用系统自动为Action容器加上的class,默认的逻辑是这样的:
var categories = [];
var category = u.dasherize(this.getCategory());
var entityName = u.dasherize(this.getEntityName());
if (category) {
categories.push(category + '-page');
}
if (entityName) {
categories.push(entityName + '-page');
}
if (category && entityName) {
categories.push(entityName + '-' + this.category + '-page');
}
return categories;
但是这样做的话,需要所有的Action重写getPageCategories
方法,且无法用继承实现(各Action类的继承关系本来就不一致),因此这个的成本相当高,且基本断了项目有特殊需求换用Action并且不继承自模块提供的Action的场景
为此,我希望实现一个更透明的方法,大致如下:
在css/fooMain.less
中添加一个变量,控制整个Scope
@ssp-foo-container-class: ssp-foo-package;
所有的CSS在这个作用域下
.@{ssp-foo-container-class} {
.esui-label { color: red; }
}
更新ub-ria,给Action增加一个package
属性
修改getPageCategories
,自动为容器生成{package}-package
的class,如ssp-foo-package
IoC管理package
属性,可以有自动规则(同remoteModule
)
如果package
属性有变化,在less中相应重写变量值
从业务上来说,package
是“将一类业务模块打包”的概念,打包后的各页面有相同的class
是否合理?
将Action
的组装从Action
逻辑内部抽出来,在外部组装,有助于一个模块跨项目复用,因为可以方便地替换Model
或View
或其它部件
从设计上来看,View
会拥有ViewModel
的引用,但反之不应该有,因此ViewModel
没有办法去监听View
的事件
在这种情况下,我们的交互不像Action
注册View
的事件这种方式,要如何进行?
一个方案是View
直接通过set
方法来改变ViewModel
的数据,再引起change
事件刷新View
,这样是不是一个合理或者大众化的方式?
所传的参数中只有 page 没有 pageSize
有的系统每页显示多少条是用户可以实时选的,所以还是得有pageSize
在返回的数据中 有pageNo,与所传的参数page意义相同,却用不同的写法,建议统一
相关pull request #97
表单提交后有两种结果需要处理,成功和失败:
toast
提醒现在的ub-ria.FormAction
提交成功的处理逻辑是:
notifySubmitSuccess
:空函数fire('entitysave')
fire('handlefinish')
back();
为了应对将来可能出现更多不同的提交成功后的处理逻辑,pr #97 对现有的handleSubmitResult
的逻辑进行了修改,提取出两个submitHandler
,submitHandler.handle
是真正的处理逻辑:
ToastSubmitHandler
template
:''
setTemplate(template)
: 设置toast
模版getToastMessage
:获取toastMessage
showToast
:BaseView
不在提供showToast
方法handle
:显示toast
RedirectSubmitHandler
template
:默认值'/${entityName}/list'setTemplate()
:设置跳转url的模版redirect()
:返回到前一页或指定urlgetData
:获取url模版的数据handle
:fire('entitysave')
> fire('handlefinish')
> redirect
另外提供CompositeSubmitHandler
,将多个handler
组合起来
CompositeSubmitHandler
setChildHandlers(handlers)
:hanlders
为数据handle()
:调用childHandler
的handle
ub-ria.FormAction
提供setSubmitHandler
,handleSubmitResult
方法调用submitHandler
的handle
方法。ub-ria.FormAction
未设定任何handler,需使用者setSubmitHandler
。
业务层可以继承或定义新的hanlder
,通过CompositeSubmitHandler
组合使用。
现在我们有不少选择:
<div>
作主元素,简单粗暴,代价是小心看花眼<input />
元素,这本意是为了让模板在无ESUI的时候也能用,一种奇怪的执着我也不知道为啥从 @errorrik 时代就传到我这了……<label>
,使用自定义元素的时候就会失效,毕竟我们没有registerElement
这种API大家讨论一下,怎么样的情况下用什么,有个最佳的实践和规范出来
以下是一个典型的用于Model做数据校验和脚手架生成相关代码时使用的对实体的定义格式,大家看一下,是否有补充的?
关于脚手架相关的内容可以参考:ecomfe/ub-ria-tool#6
/**
* <%-: project.alias %>
* Copyright 2014 Baidu Inc. All rights reserved.
*
* @ignore
* @file <%-: description %>实体定义模块
* @author <%-: developer.name %>(<%- developer.email %>)
*/
define(
function (require) {
/*
* Entity对象用于描述这个模块的实体,主要包括:
* 字段:{key}、字段类型:{type}、字段描述:{字段描述} 和 字段配置:{TypeOption}
*
* 一个典型的实体字段定义描述如下:
* {key}: [{Type}<string>, {字段描述}<string>, {TypeOption}<object>]
*
* Type: 'string' | 'enum' | 'number' | 'bool' | 'array' | 'object' | 'reference' | 'reference-set'
* 说明:字符串 | 枚举 | 数字 | 布尔值 | 数组 | 对象 | 外键 | 外键集
*
* 当Type为'string'时,TypeOption属性有:
* 字 段 required | maxLength | pattern
* 说 明 是否必填 | 最大长度 | 正则
* email: ['string', '邮件', { required: true, maxLength: @rule.mail.maxLength, pattern: @rule.mail.pattern }]
*
* 当Type为'enum'时,TypeOption属性有:
* 字 段 required | datasource
* 说 明 是否必填 | 枚举数据源
* 其中datasource为对应的enum.js中枚举变量的路径,同require规则,如:
* 引用本模块的Enum模块,则为'./enum/{EnumName}'
* 引用其它模块,如role模块下的的枚举量,则为'role/enum/{EnumName}'
* 两个典型的例子如下:
* status: ['enum', '状态', { datasource: './enum/Status' }],
* roles: ['enum', '角色', { datasource: 'role/enum/Roles' }]
*
* 当Type为'number'时,TypeOption属性有:
* 字 段 required | max | min | pattern
* 说 明 是否必填 | 最大值 | 最小值 | 正则
*
* 当Type为'bool'时,默认对应了Checkbox控件,TypeOption属性有:
* 字 段 required
* 说 明 是否必填
*
* 当Type为'array'时,TypeOption属性有:
* 字 段 required | item
* 说 明 是否必填 | 数组元素的定义
* item字段为对数组每一项的实体定义,可嵌套,如字符串数组:
* authorities: [
* 'array', '权限列表',
* {
* item: ['string', '权限项', { maxLength: 50 }],
* required: true // 不填默认为Ture
* }
* ]
*
* 当Type为'object'时,TypeOption属性有:
* 字 段 required | content
* 说 明 是否必填 | 对象的定义
* content为对象的定义,key为该对象展开后的各项key,value为每项的实体定义,可嵌套,如:
* authorities: [
* 'object', '权限集',
* {
* content: {
* slotView: ['string', '查看广告位', { maxLength: 50 }],
* slotModify: ['string', '修改广告位', { maxLength: 50 }],
* creative: [
* 'object', '创意权限集',
* {
* creativeView: ['string', '查看创意', { maxLength: 50 }],
* ......
* }
* ],
* ......
* },
* required: true // 不填默认为Ture
* }
* ]
*
* 当Type为'reference'时,默认对应ToggleSelector控件, TypeOption属性有:
* 字 段 required | datasource
* 说 明 是否必填 | 对应的实体名,一般用复数
*
* 当Type为'reference-set'时,默认对应CascadingSelector, TypeOption属性有:
* 字 段 required | datasource
* 说 明 是否必填 | 对应的实体名,一般用复数
*/
var entity = {
id: ['number', 'ID'],
name: ['string', '广告位名称', { maxLength: 200 }],
status: ['enum', '状态', { datasource: './enum/Status' }],
displayOrder: ['number', '显示顺序', { min: 1, max: 10000 }],
description: ['string', '说明备注', { maxLength: 4000 }]
// TODO: 添加或修改实体定义,完成后请删除该注释内容
};
return entity;
}
);
common-xxx.png
,ui下叫ui-xxx.png
(此规范待考察,主要是避免命名冲突)。待补充。。。
初步排查后确认问题出在extention/ui.js以下三个函数中的getErrorMessage.apply(this, arguments);
前少了一个return
:
MaxRule.prototype.getErrorMessage = function (control) {
if (control.get('maxErrorMessage')) {
var getErrorMessage = Rule.prototype.getErrorMessage;
getErrorMessage.apply(this, arguments);
}
var rangeErrorMessage = getRangeErrorMessage(control);
if (rangeErrorMessage) {
return rangeErrorMessage;
}
return Rule.prototype.getErrorMessage.apply(this, arguments);
};
MinRule.prototype.getErrorMessage = function (control) {
if (control.get('maxErrorMessage')) {
var getErrorMessage = Rule.prototype.getErrorMessage;
getErrorMessage.apply(this, arguments);
}
var rangeErrorMessage = getRangeErrorMessage(control);
if (rangeErrorMessage) {
return rangeErrorMessage;
}
return Rule.prototype.getErrorMessage.apply(this, arguments);
};
PatternRule.prototype.getErrorMessage = function (control) {
var pattern = control.get('pattern') + '';
if (control.get('patternErrorMessage')
|| !NUMBER_REGEX.hasOwnProperty(pattern)
) {
var getErrorMessage = Rule.prototype.getErrorMessage;
getErrorMessage.apply(this, arguments);
}
var rangeErrorMessage = getRangeErrorMessage(control);
if (rangeErrorMessage) {
return rangeErrorMessage;
}
return Rule.prototype.getErrorMessage.apply(this, arguments);
};
由于新增了ViewModel
这一概念,那么其中存放怎么样的数据我们应该有一个规范和约束,我大致觉得有几种可选:
ViewModel
的数据结构和视图一致,如果一个控件要一个对象,VM
中对应的属性也是一个对象,数组也一样另外还涉及一个问题,一些字符串的拼接,比如加个前缀之类的,应该在ViewModel
还是View
中处理,我更倾向于在ViewModel
中处理以提高可测性
通用409的格式,可能难以应付所有的错误情况。有些错误与所传字段无关,纯粹因为该数据正审核中不能修改 或 用户无权限。
为此可能需要增强通用409格式
如:
{
fields:[ ................ ],
message: {string},
errorId: {number}
}
fields用来处理有关所传输字段相关的错误,外围的message和errorId用以处理与字段无关的错误信息。
我认为所有的表示错误的返回信息,包括500,结构都应该包含message和errorId,当然404除外啦。
有没有必要暴露一个全局的处理ajax error的接口函数?
常见的情况是给用户弹一个窗来提示。
TableRichSelector
的设计应该尽可能地像Select
TreeRichSelector
的设计应该尽可能地像Tree
如题描述,如果一个属性是一个对象/数组,其中的一个属性/某一项变了,我们要怎么做:
Model
或Collection
,冒泡change
等事件。这会导致直接对原对象/数组的变更无法产生效果,外部必须知道“该对象变成了一个Model
”这一事实change
事件。这种策略鼓励使用“不可变值”,即不对一个对象/数组作修改,需要的时候生成新的对象/数组并直接set
回去$digest
的东西,定期去抓变化。累……我比较建议第2种方案,不可变值能带来其它的一些收益,如代码更易维护不用担心在未知的地方有所变化
有些属性是依赖其它一些属性计算的,典型的就是fullName = lastName + firstName
这一场景,会有几种做法:
change
事件自行关注依赖的其它属性完成更新。这么做的好处是整个change
事件非常统一change
事件的时候会存在一些问题,主要问题是当其依赖的属性更新时,这个属性的值会立即变化,此时的change
事件无法得到oldValue
EmberJS是使用第2种方法的,WPF因为属性本身就有getter所以也相当于是第2种方法,都是丢失了oldValue
的,Angular现在不明……
原来通过View
进行检验,但无论View
是否有做校验,Model
都应该对自己接收的参数进行严格的检验
原来的顺序是submit -> fillEntity -> validateEntity -> save / update
,现在可以改为submit -> save / update
,而在save / update
中调用validateEntity
来确保save
及update
作为一个接口方法的完备性
这种情况下,View
可以不进行检验,当save
或update
检验失败时,返回一个类似后端返回的检验错误信息的数据格式即可
现在控件中没有接口支持将已经上传的图片清除功能。
讨论一下这两个情景:
Uploader是基于 type="file" 类型input控件基础上开发的。
原生HTML文件上传控件,当选中文件后,控件value变为文件地址,当取消提交文件时,会将value置空。当value变化时触发change事件。
Uploader.js的receiveFile作为change事件的监听函数,当已经上传图片后,再次选择文件但选择取消,会重复触发。
这边需要做一层判断,对于不选择文件的情况进行抛弃。
RT
把校验移到Model后,理论上我们View层不需要这么多的检验逻辑,但是View上不进行校验的话,任何时候都可以成功提交,提交的过程会经过getEntity() -> fillEntity()
,这2个方法是否能在View没有校验提交了不完整的表单时保持不出问题呢?
因此,到底在不在View上再加一个检验,需要讨论一下:
getEntity()
和fillEntity()
需要重写实现时,要关注表单不完整的情况以避免出错getEntity()
至少不需要关注表单不完整,但fillEntity()
从设计的理念上来说,不应该知道View会检验,所以还是要加相关逻辑使用场景和功能规则描述:
点击复制进入复制页面,将被复制的内容填充至表单页面对应位置。
提交和验证规则与新建流程一致。
1. 列表
2 表单
叫做SideMenu更合适。垂直菜单栏。
功能与TogglePanel有所重合,不同只是DropDown增加了其它区域点击收起容器的事件。可以考虑扩展TogglePanel,增加配置开关。
选择框组,比BoxGroup支持的样式更灵活,支持通过skin定义了各种不同样式的选择展示。
skin-cloud
skin-flat-radio
skin-size
skin-switch-button
未完,待续。。。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.