Reactive
// @namespace Reactive
import { Reactive } from 'rcfm'
Custom Types
@type {ContextInstance} ReactiveInstance
@description - Reactive 实例
@prop {DependencyInstance} dependency - 依赖关系实例
@prop {DispatcherInstance} dispatcher - 订阅器实例
@prop {ExecutorInstance} executor - 任务执行器实例
@prop {FamilyInstance} family - 家族关系实例
@prop {HookInstance} hooks - 钩子函数管理器实例
@prop {CalculatorInstance} calculator - 计算器实例
@prop {ValidatorInstance} validator - 校验器实例
@prop {SchemaInstance} schema - DSL 解析器实例
@prop {Object} members - 环境成员状态存储空间
@typedef {Object} ReactiveMemberType
@description - Reactive 成员类型
@prop {string} type - 类型名称
@prop {ContextHook[]} [hooks] - 钩子定义数组
@prop {Function} [collect = reactive#collect] - 自定义搜集该类型成员状态函数
@prop {Function} [seed = reactive#seed] - 自定义填充该类型成员属性设定值函数
@prop {Object} [schema = {}]
@prop {ReactiveMemberPropSchema[]} [schema.props] - 成员属性定义
@prop {ReactiveMemberPropSchema} [schema.fallback = ReactiveMemberFallbackPropSchema] - 成员属性缺省定义
@prop {Object} [reference = {}]
@prop {Function} [reference.parse = reactive#schema.reference.parse] - 自定义成员路径转唯一标识及属性名称方法
@prop {Function} [reference.stringify = reactive#schema.reference.stringify] - 自定义成员唯一标识及属性名称转路径方法
@typedef {Object} ReactiveMemberPropSchema
@description - Reactive 成员属性定义
@prop {string} prop - 属性名称
@prop {boolean} [schemable = true] - 是否启用 DSL 解析
@prop {boolean} [calculable = true] - 是否启用计算表达式解析,当 schemable 为 true 时有效
@prop {*} [<prop>] - 任意自定义属性配置,通常需要钩子函数实现做支持
@typedef {Object} ReactiveMemberFallbackPropSchema
@description - Reactive 成员属性缺省定义
@prop {string} prop = <a_random_unique_id>
@prop {boolean} schemable = true
@prop {boolean} calculable = true
@prop {null} default = null - 属性初始设定值
@prop {null} rawValidation = null - 属性当前值校验规则
@prop {null} validation = null - 属性设定值校验规则
@type {ContextMember} ReactiveDefaultMember
@description - Reactive 默认成员
@prop {Object} props - 成员属性设定值构成的对象
@prop {string} [props.type = "default"] - 成员类型
@prop {string} [props.name = <a_random_unique_id>] - 成员名称
@prop {integer} [props.order = Infinity] - 成员在同辈中的顺序设定值
@prop {Object[]} props.children = [] - ContextInstance 自动维护的成员子级设定值
@prop {string} props.children[i].id - 子成员唯一标识
@prop {*} props.<custom> - 其它任意自定义属性设定值
Exception Types
@type {ExceptionInstance}
@description - 成员类型冲突
@prop {string} type = "REACTIVE_CONFLICTED_MEMBER_TYPE"
@prop {string} [name = type]
@prop {Object} [data = {}]
@prop {string} [message]
@type {ExceptionInstance}
@description - 成员类型未注册
@prop {string} type = "REACTIVE_UNINSTALLED_MEMBER_TYPE"
@prop {string} [name = type]
@prop {Object} [data = {}]
@prop {string} [message]
@type {ExceptionInstance}
@description - 成员存在依赖约束
@prop {string} type = "REACTIVE_CONSTRAINTS_EXIST"
@prop {string} [name = type]
@prop {Object} [data = {}]
@prop {string} [message]
@type {ExceptionInstance}
@description - 成员名称冲突
@prop {string} type = "REACTIVE_CONFLICTED_MEMBER_NAME"
@prop {string} [name = type]
@prop {Object} [data = {}]
@prop {string} [message]
@type {ExceptionInstance}
@description - 被依赖者不存在
@prop {string} type = "REACTIVE_REFERENCED_PROVIDER_NOT_FOUND"
@prop {string} [name = type]
@prop {Object} [data = {}]
@prop {string} [message]
@type {ExceptionInstance}
@description - 不合规的父成员
@prop {string} type = "REACTIVE_INVALID_PARENT"
@prop {string} [name = type]
@prop {Object} [data = {}]
@prop {string} [message]
@type {ExceptionInstance}
@description - 钩子函数不存在
@prop {string} type = "REACTIVE_HOOK_NOT_EXIST"
@prop {string} [name = type]
@prop {Object} [data = {}]
@prop {string} [message]
@type {ExceptionInstance}
@description - 条件语句不合规
@prop {string} type = "CONDITION_INVALID_CONDITION"
@prop {Object} data = {dueto: "whens"}
@prop {string} [name = type]
@prop {string} [message = <global_i18n_defination>]
@type {ExceptionInstance}
@description - 条件语句布尔表达式不合规
@prop {string} type = "CONDITION_INVALID_CONDITION"
@prop {Object} data = {branchIndex: <invalid_branch_index>, dueto: "rules"}
@prop {string} [name = type]
@prop {string} [message = <global_i18n_defination>]
@type {ExceptionInstance}
@description - 条件语句布尔表达式关键字不合规
@prop {string} type = "CONDITION_INVALID_CONDITION"
@prop {Object} data = {branchIndex: <invalid_branch_index>, ruleIndex: <invalid_rule_index>, dueto: "logic"}
@prop {string} [name = type]
@prop {string} [message = <global_i18n_defination>]
@type {ExceptionInstance}
@description - 条件语句布尔表达式中的被依赖者不合规
@prop {string} type = "CONDITION_INVALID_CONDITION"
@prop {Object} data = {branchIndex: <invalid_branch_index>, ruleIndex: <invalid_rule_index>, dueto: "provider"}
@prop {string} [name = type]
@prop {string} [message = <global_i18n_defination>]
@type {ExceptionInstance}
@description - 条件语句布尔表达式中的校验方法名或校验规则不合规
@prop {string} type = "CONDITION_INVALID_CONDITION"
@prop {Object} data = {branchIndex: <invalid_branch_index>, ruleIndex: <invalid_rule_index>, dueto: "validation"}
@prop {string} [name = type]
@prop {string} [message = <global_i18n_defination>]
@type {ExceptionInstance}
@description - 条件语句分支结果(成员属性设定值)校验未通过
@prop {string} type = "VALIDATOR_VALIDATION_FAILED_RAW"
@prop {Object} data = {branchIndex: <invalid_branch_index>, dueto: "result"}
@prop {string} [name = type]
@prop {string} [message = <global_i18n_defination>]
@type {ExceptionInstance}
@description - 成员属性当前值校验未通过
@prop {string} type = "VALIDATOR_VALIDATION_FAILED"
@prop {Object} [data = {}]
@prop {string} [name = type]
@prop {string} [message = <global_i18n_defination>]
Builtin Hooks
-
预设并冻结成员 type
当成员被创建后,优先设定其
type
属性,如果创建时,未定义成员type
,默认为default
。 -
预设并校验成员 name
当成员被创建后,优先设定其
name
属性,如果创建时,未定义成员name
,默认为其id
, 当name
被更新时,执行同辈成员间name
唯一性校验。 -
解析成员属性 DSL
根据成员属性的设定值是否包含 DSL,自动建立/解除成员间的关联关系,动态解析 DSL 并更新该属性的当前值。
-
阻止创建未知 type 成员
当创建成员时,如果成员的类型未注册,抛出异常。
-
阻止删除被依赖的成员
当删除成员时候,如果该成员正在被引用,抛出异常。
Shared Hooks
-
BLOCK_INVALID_PARENT
子成员只能添加到特定类型范围内的父级成员。
// @example reactive.install({ type: 'MyTextInput', // 限制 MyTextInput 类型的成员 // 只能作为 default 类型成员的子级 hooks: [ Reactive.hooks.use('BLOCK_INVALID_PARENT', {accept: ['default']}) ] })
-
ENABLE_SCHEMA_ALWAYS
如果在成员属性
schema
中定义了always
,成员属性的设定值将始终等于always
的值 。// @example reactive.install({ type: 'MyTextInput', hooks: [ Reactive.hooks.use('ENABLE_SCHEMA_ALWAYS') ], schema: { props: [{ prop: 'value', always: '被冻结了' }] } })
-
ENABLE_SCHEMA_DEFAULT
如果在成员属性
schema
中定义了default
,那么在创建成员时,如果成员属性设定值未定义, 则使用default
相同的值来作为其设定值。// @example reactive.install({ type: 'MyTextInput', hooks: [ Reactive.hooks.use('ENABLE_SCHEMA_DEFAULT') ], schema: { props: [{ prop: 'fakeId', // schema 中定义的属性也可以是函数 default: () => Math.random() }] } })
-
ENABLE_SCHEMA_VALIDATION
如果在成员属性
schema
中定义了validation
,每当属性的当前值变更后,会按照validation
规则对该值进行校验,成员的错误信息将根据校验结果被赋值为null
或一个包含VALIDATOR_VALIDATION_FAILED
类型错误的数组。如果在成员属性
schema
中定义了rawValidation
,每当属性的设定值变更后,会按照rawValidation
规则对该值进行校验,成员的错误信息将根据校验结果被赋值为null
或一个包含VALIDATOR_VALIDATION_FAILED_RAW
类型错误的数组。校验规则可以被定义为
null
,或一个数组,数组中可包含受支持的 DSL 校验表达式,或返回错误信息的函数。// @example reactive.install({ type: 'MyTextInput', // 使用 Reactive 提供的钩子函数 hooks: [ Reactive.hooks.use('ENABLE_SCHEMA_VALIDATION') ], // 根据钩子函数的实现定义 schema schema: { props: [{ prop: 'label', validation: [ // DSL 校验表达式 {dataType: 'String'}, // 自定义校验函数 function({id, value}){ if (/\s/.test(value)) { return 'Cannot contain spaces.' } } ] }] } })
Class Methods
Reactive()
创建一个 Reactive 实例。
// Reactive()
// @returns {ReactiveInstance}
// @example
const reactive = Reactive()
.guid()
获取一个全局唯一 id。
// Reactive.guid()
// @returns {string}
// @example
const id = Reactive.guid()
.hooks.use()
获取 Reactive 共用的某个钩子函数定义。
// Reactive.hooks.use(defName)
// @param {string} defName - 钩子定义名称
// @returns {ContextHook} - 钩子函数定义
// @examples
Reactive.hooks.use('ENABLE_SCHEMA_ALWAYS')
Reactive.hooks.use('ENABLE_SCHEMA_DEFAULT')
Reactive.hooks.use('ENABLE_SCHEMA_VALIDATION')
Reactive.hooks.use('BLOCK_INVALID_PARENT', {accept: ['Fieldset']})
Instance Methods
#hooks.mount()
挂载钩子函数定义。
// reactive#hooks.mount(def, options)
// @param {ContextHook | ContextHook[]} - 钩子函数定义
// @param {Object} [options = {}]
// @prop {string} [options.only] - 当设定时,钩子函数只在特定 type 成员上触发
// @returns {undefined}
// @example - 添加全局钩子函数
reactive.hooks.mount({
hook: 'before-add',
handler(mutation) {
console.log('This is a global hook.')
}
})
// @example - 添加作用于特定 type 成员的钩子函数
reactive.hooks.mount([{
hook: ['before-add', 'before-del'],
handler: [function(mutation) {
console.log('This is a type specific hook function.')
}, function(mutation) {
console.log('This is another type specific hook function.')
}]
}, {
hook: 'after-add',
handler(mutation, result) {
console.log('This is still a type specific hook function.')
}
}], {only: 'MyTextInput'})
#schema.for()
获取成员所有属性或某个属性的定义。
// reactive#schema.for(params, options)
// @param {Object} params
// @prop {string} params.id - 成员唯一标识
// @prop {string} [params.prop] - 属性名称,如设定之返回该属性定义,否则返回所有属性定义。
// @prop {Object} [options = {}]
// @prop {string | Array} [options.only] - 当 params.prop 未定义时可使用属性名称正向过滤
// @prop {string | Array} [options.except] - 当 params.prop 未定义时可使用属性名称反向过滤
// @returns {ReactiveMemberPropSchema | ReactiveMemberPropSchema[]}
// @examples
reactive.schema.for({id: myMember, prop: 'value'})
reactive.schema.for({id: myMember})
reactive.schema.for({id: myMember}, {except: 'name'})
reactive.schema.for({id: myMember}, {only: ['name', 'value']})
#schema.reference.parse()
将以 .
分割的由成员 name 和属性名构成的引用路径字符串,转换为由成员 id 和属性名称构成的 DependencyProvider 对象。
// reactive#schema.reference.parse(path, options)
// @param {string} path
// @param {Object} [options]
// @prop {FamilyMember} [options.under = <root_member>] - 在家族树中查找被依赖者的起始节点(引用路径不包含起始节点)
// @prop {DependencyRelier} [options.relier = null] - 属性设定值中含有 path 的依赖者,当被依赖者类型定义的 parse 方法需要时传入
// @returns {DependencyProvider | null}
// @example
const JerryId = reactive.add({props: {name: 'Jerry'}})
const JimId = reactive.add({parentId: JerryId, props: {name: 'Jim'}})
// returns {id: JimId, prop: 'age'}
reactive.schema.reference.parse('Jerry.Jim.age')
// returns null
reactive.schema.reference.parse('NonExistedName.Jacky.age')
#schema.reference.stringify()
将由成员 id 和属性名称构成的 DependencyProvider 对象,转换为以 .
分割的由成员 name 和属性名构成的引用路径字符串。
// reactive#schema.reference.stringify(provider, options)
// @param {DependencyProvider} provider
// @param {Object} [options]
// @prop {FamilyMember} [options.under = <root_member>] - 在家族树中查找 provider 的起始节点(引用路径不包含起始节点)
// @prop {DependencyRelier} [options.relier = null] - 属性设定值依赖 provider 的依赖者,当 proider 类型定义的 stringify 方法需要时传入
// @returns {string}
// @example
const JerryId = reactive.add({props: {name: 'Jerry'}})
const JimId = reactive.add({parentId: JerryId, props: {name: 'Jim'}})
// returns "Jerry.Jim.age"
reactive.schema.reference.stringify({id: JimId, prop: 'age'})
// returns ''
reactive.schema.reference.stringify({id: 'NonExistedId', prop: 'age'})
#collect()
获取某成员及其后代成员的状态。
// reactive.collect(from, options)
// @param {FamilyMember} from
// @param {Object} [options]
// @prop {string | Array} [options.only] - 设定时,只搜集范围内的属性状态
// @prop {string | Array} [options.except] - 设定时,不搜集范围内的属性状态
// @prop {boolean} [errors = true] - 当为 false 时,返回结果不包含成员属性的错误信息
// options.only 和 options.except 不过滤错误信息
// @returns {null | Object}
// @example
const fields = reactive.add()
reactive.seed({id: fields}, {
name: 'fields',
children: [{
name: 'name',
value: 'Jerry'
}, {
name: 'nickname',
value: 'Little << fields.name.value >>'
}]
})
// returns -
// {
// errors: null
// values: {
// name: 'fields',
// children: [{
// name: 'name', value: 'Jerry', children: []
// }, {
// name: 'nickname', value: 'Little Jerry', children: []
// }]
// },
// raw: {
// errors: null
// values: {
// name: 'fields',
// children: [{
// name: 'name', value: 'Jerry', children: []
// }, {
// name: 'nickname', value: 'Little << fields.name.value >>', children: []
// }]
// }
// }
// }
reactive.collect({id: fields}, {only: ['name', 'value', 'children']})
#install()
注册一个成员类型。
// reactive#install(typeDef)
// @param {ReactiveMemberType} typeDef
// @returns {undefined}
// @example
reactive.install({
type: 'ACustomType',
hooks: [
Reactive.hooks.use('ENABLE_SCHEMA_DEFAULT'),
Reactive.hooks.use('ENABLE_SCHEMA_VALIDATION')
],
schema: {
fallback: {validation: [{presence: true}]},
props: [
{prop: 'lastName', default: 'Stone', validation: [{dataType: 'String'}]},
{prop: 'firstName', default: 'Shara', validation: [{dataType: 'String'}]}
],
reference: {
// @param {Object} provider -
// @prop {string} provider.id - 被引用的 ACustomType 成员唯一标识
// @prop {string} provider.downPath - 待解析的被引用路径
// @prop {string} provider.target - 触发解析的引用插值符内的原始路径
// @param {Object} [relier = null] -
// @prop {string} [relier.id] - 引用 provider 的成员唯一标识
// @prop {string} [relier.prop] - 引用 provider 的成员属性
// @returns {Object|null} - 如 provider.downPath 可被引用应返回由目标成员 id 和 prop 构成的对象,否则返回 null
parse(provider, relier) {
console.log(this === reactive) // true
const {id, downPath, target} = provider
// For example, ensures only the firstName property can be referenced.
return {id, prop: 'firstName'}
},
// @param {Object} provider -
// @prop {string} provider.id - 被引用的 ACustomType 成员唯一标识
// @prop {string} provider.downPath - 待解析的被引用路径
// @prop {string} provider.upPath - 由被引用成员的祖先及其自身的 name 当前值,以 . 号连接形成的字符串
// @prop {Object} provider.target - 触发解析的由成员 id 和 prop 构成的原始对象
// @param {Object} [relier = null] -
// @prop {string} [relier.id] - 引用 provider 的成员唯一标识
// @prop {string} [relier.prop] - 引用 provider 的成员属性
// @returns {string} - 如 provider.downPath 可被引用应返回完整的引用路径字符串,否则返回 null
stringify(provider, relier) {
console.log(this === reactive) // true
const {id, upPath, downPath, target} = provider
// For example, Ensures only the firstName property can be referenced.
return `${upPath}.firstName`
}
}
},
// 应返回用于创建成员、填充成员属性的 mutation 对象数组
seed(member, props) {
console.log(this === reactive) // true
let {id} = member
if ('firstName' in props) { return }
// For example, ensures only the firstName property can be setted during seed processes.
return [{action: 'set', id, prop: 'firstName', value: props.firstName}]
},
// 应返回成员属性当前值和设定值构成的对象
collect(member) {
console.log(this === reactive) // true
let {id} = member
let {value, raw} = this.get({id, prop: 'firstName'}, {include: 'raw'})
// For example, ensures only the firstName property can be collected.
return {values: {firstName: value}, raw: {values: {firstName: raw.value}}}
}
})
#seed()
从某成员开始,按照 plain object 属性填充该成员属性设定值, 并按 plain object 嵌套的 children
层级,创建其后代成员、初始化后代成员属性设定值。
// reactive#seed(from, props, options)
// @param {FamilyMember} from
// @param {Object} props - 成员属性名称和设定值构成的对象,可用 children 字段嵌套后代
// @param {Object} [options]
// @prop {boolean} [options.execute = true] - 如设定为 false,返回该操作对应的 mutation 对象数组,而不是直接执行操作。
// @returns {ContextMutation[] | undefined}
// @example
const fields = reactive.add()
reactive.seed({id: fields}, {
name: 'fields',
children: [{
name: 'name',
value: 'Jerry'
}, {
name: 'age',
value: 100
}]
})