完成了2-5.md,修订了1-1.md的部分内容
This commit is contained in:
parent
c3ceeff80b
commit
1d8ae7544b
|
|
@ -8,7 +8,7 @@
|
|||
- [部署](./chapter1/deploy.md)
|
||||
- [编写应用](./chapter2/def.md)
|
||||
- [编写对象](./chapter2/1.md)
|
||||
- [对象之间的关系](./chapter2/1-1.md)
|
||||
- [查询和操作对象](./chapter2/1-1.md)
|
||||
- [编写组件/页面](./chapter2/2.md)
|
||||
- [三层架构](./chapter2/2-1.md)
|
||||
- [目录文件结构](./chapter2/2-2.md)
|
||||
|
|
@ -17,4 +17,4 @@
|
|||
- [编写更新组件](./chapter2/2-3-2.md)
|
||||
- [编写列表组件](./chapter2/2-3-3.md)
|
||||
- [组织组件](./chapter2/2-4.md)
|
||||
- [定义组件对象](./chapter2/2-5.md)
|
||||
- [定义组件](./chapter2/2-5.md)
|
||||
|
|
@ -1 +1,216 @@
|
|||
# 对象之间的关系
|
||||
# 查询和操作对象
|
||||
## 编译
|
||||
在编写或更新了*Entity*定义后,都需要执行命令来编译完整的**对象数据字典**:
|
||||
```script
|
||||
npm run make:domain
|
||||
```
|
||||
|
||||
编译出来的数据字典声明在*src/oak-app-domain*目录下,同时也会编译出来一个数据的存储格式供框架引用,可以在代码中像这样去引用它们:
|
||||
```typescript
|
||||
import { EntityDict, StorageSchema } from '@project/oak-app-domain';
|
||||
```
|
||||
数据字典和存储格式是整个Oak框架最核心的内容,贯穿于使用框架的各个层面,因此需要深刻理解。本章节将使用上小节的*Address*和*Area*对象,介绍一些查询和操作的核心概念。
|
||||
|
||||
## 查询相关概念
|
||||
### Projection
|
||||
当查询对象时,通过projection可以定义要查询对象的哪些属性(projection的命名本身就借鉴了数据库中的“投影”概念)。例如,对上一节中所定义的*Address*对象,查询时可以指定投影为:
|
||||
```typescript
|
||||
{
|
||||
detail: 1,
|
||||
name: 1,
|
||||
phone: 1,
|
||||
}
|
||||
```
|
||||
可以根据对象之间的关系将*projection*扩展到多对一的父对象上,实现**级联查询**:
|
||||
```typescript
|
||||
{
|
||||
detail: 1,
|
||||
name: 1,
|
||||
phone: 1,
|
||||
area: { // 查询相关联的Area
|
||||
id: 1,
|
||||
name: 1,
|
||||
parent: { // 查询Area所相关的更高层Area
|
||||
id: 1,
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
也可以扩展到一对多的子对象上的级联查询,例如我们查询*Area*对象时,可以将之关联的*Address*一并查找出来:
|
||||
```typescript
|
||||
{
|
||||
id: 1,
|
||||
name: 1,
|
||||
address$area: { // 查询和area相关的所有address对象
|
||||
$entity: 'area',
|
||||
data: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
phone: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
通过这样的扩展,可以很方便的通过一次查询获得一个对象相关联的所有数据,查询结果会是一个复杂的数据对象(*Schema*)
|
||||
|
||||
Oak框架还支持表达式查询和函数计算,例如如果想返回一个name和phone连接的字符串,可以这样写:
|
||||
```typescript
|
||||
{
|
||||
id: 1,
|
||||
$expr: {
|
||||
$concat: [
|
||||
"姓名:",
|
||||
{
|
||||
'#attr': "name",
|
||||
},
|
||||
"手机号:",
|
||||
{
|
||||
'#attr': "phone",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Schema
|
||||
Schema是查询某对象后可能获得的数据格式。例如,在上面的例子里,查询到的*Address*数据结果就是:
|
||||
```typescript
|
||||
{
|
||||
id: 'xxx',
|
||||
name: 'xxxxx',
|
||||
phone: '139xxxxxxxx',
|
||||
areaId: 'xxxx',
|
||||
area: {
|
||||
id: '310100',
|
||||
name: '杭州市',
|
||||
parentId: '330000',
|
||||
parent: {
|
||||
id: '330000',
|
||||
name: '浙江省',
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
而查询到的*Area*数据结果就是:
|
||||
```typescript
|
||||
{
|
||||
id: '310100',
|
||||
name: '杭州市',
|
||||
address$area: [
|
||||
{
|
||||
id: 'xxx',
|
||||
name: 'xxxxx',
|
||||
phone: '139xxxxxxxx',
|
||||
},
|
||||
{
|
||||
id: 'xxx',
|
||||
name: 'xxxxx',
|
||||
phone: '139xxxxxxxx',
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Filter
|
||||
Filter代表查询某个对象的条件,例如,我们要查询*Area*为杭州市,手机号码以139开头的*Address*,就可以这样写:
|
||||
```typescript
|
||||
{
|
||||
areaId: '310100',
|
||||
phone: {
|
||||
$startsWith: '139',
|
||||
},
|
||||
}
|
||||
```
|
||||
可见Filter的算子语法比较接近MongoDB,Oak框架在此基础上做了大量的扩展,例如,如果不知道杭州市的areaId,我们也可以这样写:
|
||||
```typescript
|
||||
{
|
||||
area: { // 把filter扩展到Area父对象上
|
||||
name: '杭州市
|
||||
},
|
||||
phone: {
|
||||
$startsWith: '139',
|
||||
},
|
||||
}
|
||||
```
|
||||
同样的,Filter也可以被扩展到子对象上,比方说我们查询*Area*,条件是“该Area上至少有一条相关的Address,其手机号码以139开头”(不用考虑这个查询是否合理):
|
||||
```typescript
|
||||
{
|
||||
address$area: {
|
||||
phone: {
|
||||
$startsWith: '139',
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
甚至我们可以查询“该Area上不能有任何一条手机号以139开头的ddress”:
|
||||
```typescript
|
||||
{
|
||||
address$area: {
|
||||
phone: {
|
||||
'#sqp': 'not in',
|
||||
$startsWith: '139',
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
具体的Filter算子的语法比较丰富,我们罗列在下方,部分复杂的语法用户可以查阅*oak-domain/src/types/Demand.ts*。
|
||||
|
||||
算子 | 参数类型 | 作用
|
||||
------------|-----------------------|---------
|
||||
$gt | number \| string | 大于
|
||||
$gte | number \| string | 大于等于
|
||||
$lt | number \| string | 小于
|
||||
$lte | number \| string | 小于等于
|
||||
$eq | number \| string \| boolean | 等于
|
||||
$ne | number \| string \| boolean | 不等于
|
||||
$in | (number \| string)[] | 在……中
|
||||
$nin | (number \| string)[] | 不在……中
|
||||
$mod | [number, number] | 取模
|
||||
$between | [number, number] | 在……之间(包括等于)
|
||||
$startsWith | string | 以……开头
|
||||
$endsWith | string | 以……结尾
|
||||
$includes | string | 包含……
|
||||
$exists | boolean | 是否为空
|
||||
$search<br>$language | string <br> 'zh_CN'\|'en_US' | 全文检索(对象上必须声明了全文索引)
|
||||
$and | Filter[] | 与
|
||||
$or | Filter[] | 或
|
||||
|
||||
此外,Filter还能支持表达式计算和非结构化数据查询。todo
|
||||
|
||||
### Sorter
|
||||
Sorter表示查询时的排序。例如,我们查询*Address*时要求结果按手机号排序,可以这样写sorter:
|
||||
```typescript
|
||||
[
|
||||
{
|
||||
$attr: {
|
||||
phone: 1,
|
||||
},
|
||||
$direction: 'ASC',
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
写成数组的形式意味着我们可以按序支持多个sort条件,同时也支持将排序的属性扩展到多对一的父对象上:
|
||||
```typescript
|
||||
[
|
||||
// 先按phone升序排,再按area.name降序排
|
||||
{
|
||||
$attr: {
|
||||
phone: 1,
|
||||
},
|
||||
$direction: 'ASC',
|
||||
},
|
||||
{
|
||||
$attr: {
|
||||
area: {
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
$direction: 'DESC',
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
在查询中,如果不指定任何排序条件,则框架会自动添加一个$$createAt$$属性上的降序排序条件。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# 定义组件对象
|
||||
# 定义组件
|
||||
通过OakComponent接口可以定义一个组件对象。在前几章,我们已经看到部分重要的定义配置项,完整的配置项及格式可以参见```oak-frontend-base/src/types/Page.ts```文件。在编写代码时,typescript也会给出有效的提示。在本章节我们将归纳并进一步介绍部分配置项,以及组件本身可引用的数据项和方法。
|
||||
|
||||
|
||||
|
|
@ -160,12 +160,34 @@ options: {
|
|||
|
||||
```typescript
|
||||
const { oakId } = this.props;
|
||||
const { name } = this.state;
|
||||
const { name, oakFullpath } = this.state;
|
||||
```
|
||||
在props和state上都包含了一些框架的内置数据项,见下节介绍。state上还包含了从formData返回的数据项。
|
||||
|
||||
在小程序的渲染xml代码中,直接引用数据项名称就可以访问数据项(props和state上的所有数据项)。
|
||||
|
||||
在web和native环境下的渲染函数中,可以通过参数props中的data来引用所有的数据项。在使用前要通过typescript来声明这些数据项:
|
||||
|
||||
```typescript
|
||||
export default function Render(
|
||||
props: WebComponentProps<
|
||||
EntityDict,
|
||||
'system', // 对象
|
||||
false, // 是否list
|
||||
{
|
||||
name: string;
|
||||
description: string;
|
||||
},
|
||||
{
|
||||
setName: (name: string) => void;
|
||||
}
|
||||
>
|
||||
) {
|
||||
const { name, description, oakId } = props.data; // oakId是内置数据项
|
||||
const { setName, execute } = props.methods; // execute是内置方法
|
||||
}
|
||||
```
|
||||
|
||||
在web和native环境下的渲染函数中,可以通过参数props中的data来引用所有的数据项,在前面的[编写详情组件](2-3-1.md)中已经有相关示例代码。
|
||||
|
||||
在小程序的渲染xml代码中,直接引用数据项名称就可以访问该数据项。
|
||||
|
||||
#### 框架内置数据项
|
||||
框架有一些内置的数据项供组件使用。这些数据项都是以*oak*开头来命名的。用户应该避免使用相同的关键字来命名数据项。
|
||||
|
|
@ -228,7 +250,7 @@ features提供了前端公用的能力,比较重要的底层feature包括:
|
|||
this.features.cache.refresh(...);
|
||||
```
|
||||
|
||||
同时为了方便起见,框架也将最常用的一批接口暴露到了组件上,这样组件就可以很方便的通过*this*关键字来调用。这批接口包括:
|
||||
同时为了方便起见,框架也将最常用的一批接口暴露到了组件上,这样组件就可以很方便的通过*this*关键字来直接调用。这批接口包括:
|
||||
<table style="width: 80%">
|
||||
<tr style="background-color: var(--table-header-bg)">
|
||||
<td>feature</td>
|
||||
|
|
@ -301,4 +323,115 @@ this.features.cache.refresh(...);
|
|||
<td>subDataEvents</td>
|
||||
<td>订阅数据变化</td>
|
||||
</tr>
|
||||
</table>
|
||||
<tr>
|
||||
<td rowspan="25">runningTree</td>
|
||||
<td>refresh</td>
|
||||
<td>刷新数据</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>execute</td>
|
||||
<td>提交更新</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>create</td>
|
||||
<td>创建(非list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>update</td>
|
||||
<td>更新(非list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>remove</td>
|
||||
<td>删除(非list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>setId</td>
|
||||
<td>设置id(非list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>getId</td>
|
||||
<td>获取id(非list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>isCreation</td>
|
||||
<td>是否创建(非list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>loadMore</td>
|
||||
<td>取下一页(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>getFilters</td>
|
||||
<td>获取当前过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>setFilters</td>
|
||||
<td>设置当前过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>getFilterByName</td>
|
||||
<td>获取指定命名的过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>addNamedFilter</td>
|
||||
<td>增加命名过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>setNamedFilters</td>
|
||||
<td>设置命名过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>removeNamedFilter</td>
|
||||
<td>删除命名过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>removeNamedFilterByName</td>
|
||||
<td>根据命名删除命名过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>setNamedSorters</td>
|
||||
<td>设置命名排序条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>getSorters</td>
|
||||
<td>获取过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>getSorterByName</td>
|
||||
<td>获取命名排序条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>addNamedSorter</td>
|
||||
<td>增加命名过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>removeNamedSorter</td>
|
||||
<td>移除命名排序条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>removeNamedSorterByName</td>
|
||||
<td>根据命名移除过滤条件(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>getPagination</td>
|
||||
<td>获取分页设置(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>setPageSize</td>
|
||||
<td>设置分页长度(list页面)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>setCurrentPage</td>
|
||||
<td>设置当前分页(list页面)</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#### 如何使用方法
|
||||
|
||||
如上所述,在*formData/生命周期函数/自定义方法*中,都可以通过*this*关键字来调用这些方法,或者通过*this.features*调用更多features上的方法。
|
||||
|
||||
- 要注意,在生命周期的*create/attach*函数中,runningTree上的许多方法是不能调用的。因为runningTree是在组件开始构建后才进行初始化的,更多细节可以参见todo。
|
||||
|
||||
在小程序环境下,可以直接在xml文件中调用自定义方法。
|
||||
|
||||
在react/react-native环境下,可以在props.methods中直接调用所有的自定义方法,同时也包括一部分公共方法。所提供的公共方法通过typescript声明可以获得,而自定义方法需要在props的声明中显式加以定义(参看上面「如何使用数据」小节的例子)。
|
||||
|
|
|
|||
Loading…
Reference in New Issue