# 数据的发送和提交

我们接着上一节，来介绍响应用户操作的第二个步奏：当操作触发，执行预先设定的任务

## 思考什么是任务
执行预先设定的任务，必然和数据操作有关。在RCRE中，数据操作必然和container组件密不可分。

所以预订设定的任务，我们可以把任务的配置挂载在container上面。

在设定任务的时候，要不断围绕数据进行思考：

1. 数据从哪里来
2. 数据在这里如何停留
3. 数据到哪里去

数据从哪里来非常容易解释，在RCRE中，数据只会从事件触发那里进行获取，也就是事件中的params属性。事件触发时候的时候，配置中的params属性的值会发送到这里。

数据在这里如何停留，有的时候，事件发送过来的数据和我们真正要输出的数据不一定是完全相同的，因此在数据在此停留的时候，我们依然可以对数据进行修改和操作。

数据到哪里去分2种情况，一种是一个container把数据发送到另外一个container组件上，还有一种情况是container组件把数据通过ajax，写入缓存等操作把数据发送到外部。


### dataCustomer定义

定义任务我们使用container组件的dataCustomer功能来实现。对于不同的数据输出方式，都可以使用代码来对DataCustomer进行扩展来进行实现。感兴趣的读者可以看高级教程。

`dataCustomer`属性的值一个对象，对象中会有2个属性值：

必选的是`customers`属性，它是一个数组，数组中每个元素是个对象。对应每个任务自身的定义和细节，在定义任务的时候，必须要给每个任务起个名字，这样才能够在事件触发的时候，找到对应的任务。

可选的是`groups`属性，它也是一个数组，数组中每个元素也是个对象。不过不一样的是，这个对象定义的是多个任务执行的顺序，而不是任务细节本身。

对于成组的逻辑操作，通过`groups`属性，可以让不同的逻辑操作按照一定的顺序来执行。

### 任务定义

customers属性中的每一个对象，都要遵循以下的规范：

| 字段名称   | 类型     | 是否必须 | 说明                                       |
| ------ | ------ | ---- | ---------------------------------------- |
| mode   | string | true | 任务模式，不同的模式对应不同的操作和配置                   |
| name   | string | true | 任务名称，用于在事件触发的时候，用作targetCustomer的值 |
| config | Object | true | 任务配置，详细配置请看API手册 |

### ExpressionString的应用
在dataCustomer中，需要大量使用ExpressionString来获取事件传递过来的数据。

所有事件传递过来的数据，都会存放在$trigger这个变量当中，$trigger对象中，会以每个任务名称作为Key，事件发送到任务的数据作为值。例如：

如果有下面这样的dataCustomer定义
```_raw_json
{
    "type": "container",
    "dataCustomer": {
        "customers": [{
            "mode": "pass",
            "name": "simplePass",
            "config": {
                "model": "simplePass",
                "assign": {
                    "name": "#ES{$trigger.simplePass.name}"
                }
            }
        }]
    }
}
```

事件是由当前container组件下的一个Button组件触发
```_raw_json
{
    "type": "button",
    "text": "click",
    "trigger": [{
        "event": "onClick",
        "targetCustomer": "simplePass",
        "params": {
            "name": "HelloWorld"
        }
    }]
}
```

`$trigger.simplePass.name`这段语句是获取$trigger对象中simplePass这个任务下的name属性的值。

如果无状态组件的trigger中有一个配置项的targetCustomer写的是simplePass。那么在事件触发的时候，当前container组件下的$trigger对象中会带有simplePass这个属性，而这个属性的值就是事件触发时传入的params的值。

所以最终`$trigger.simplePass.name`获取到的值是HelloWorld

## 使用样例

请到DataCustomer组件[查看](/component/dataCustomer)

## 使用自定义函数作为自定义dataCustomer

如果内置的dataCustomer都满足不了需求，可以直接写个函数来作为dataCustomer使用。

作为dataCustomer的函数和filter函数不通用，也不共享。是完全独立的，除此之外，像$data,$trigger都可以访问。

函数中第一个参数种的prev参数，是上一个dataCustomer运行的返回值。

```javascript
function syncDataCustomer(args) {
    console.log(args);
    let result = confirm('这是一个自定义dataCustomer函数, 是否继续？');
    
    if (result) {
        return args.params;
    }
    
    return null;
}

function asyncDataCustomer(args) {
    console.log(args);
    return new Promise((resolve, reject) => {
        alert('1s之后将询问你是否继续');
        setTimeout(() => {
            let result = confirm('是否继续？');
            
            if (result) {
                resolve(args.params);
            } else {
                reject(false)
            }
        }, 1000);
    });
}

window.RCRE.funcCustomer.setCustomer('syncDataCustomer', syncDataCustomer);
window.RCRE.funcCustomer.setCustomer('asyncDataCustomer', asyncDataCustomer);
```

```json
{
    "body": [{
        "type": "container",
        "model": "funcDataCustomer",
        "dataCustomer": {
            "customers": [{
                "name": "syncExample",
                "func": "#ES{syncDataCustomer}"
            }, {
                "name": "asyncExample",
                "func": "#ES{asyncDataCustomer}"
            }, {
                "mode": "message",
                "name": "popMessage",
                "config": {
                    "content": "#ES{$prev.name}"
                }
            }],
            "groups": [{
                "name": "doubleSync",
                "steps": ["syncExample", "syncExample"]
            }, {
                "name": "doubleAsync",
                "steps": ["asyncExample", "popMessage"]
            }]
        },
        "data": {
            "name": "andycall"
        },
        "children": [{
            "type": "button",
            "text": "触发同步",
            "trigger": [{
                "event": "onClick",
                "targetCustomer": "doubleSync",
                "params": {
                    "name": "#ES{$data.name}"
                }
            }]
        }, {
            "type": "button",
            "text": "触发异步",
            "trigger": [{
                "event": "onClick",
                "targetCustomer": "doubleAsync",
                "params": {
                    "name": "#ES{$data.name}"
                }
            }]
        }]
    }]
}
```

## $this

跨组件数据传递很常用，但是写很多的dataCustomer配置会很繁琐。

为了简化操作，每一个container组件，都会有一个内置的任务配置，专门用于写入自身的数据模型的值。

所以，如果一个组件的trigger属性中targetCustomer的目标是父级的container组件的话，就不需要再到container组件上进行任何定义，只需要把

targetCustomer的值赋值为`$this`就OK了。

这个内置的任务配置会吧事件传递过来的数据全部写入到当前的container组件。

我们来看一个简单的例子 —— 实现一个递增或者递减的计数器。

```json
{
    "body": [
        {
            "type": "container",
            "model": "counter",
            "data": {
                "total": 0
            },
            "children": [
                {
                    "type": "row",
                    "children": [
                        {
                            "type": "text",
                            "text": "value: #ES{$data.total}"
                        }
                    ]
                },
                {
                    "type": "row",
                    "width": 200,
                    "children": [
                        {
                            "type": "button",
                            "text": "+1",
                            "trigger": [
                                {
                                    "event": "onClick",
                                    "targetCustomer": "$this",
                                    "params": {
                                        "total": "#ES{$data.total + 1}"
                                    }
                                }
                            ]
                        },
                        {
                            "type": "button",
                            "text": "-1",
                            "trigger": [
                                {
                                    "event": "onClick",
                                    "targetCustomer": "$this",
                                    "params": {
                                        "total": "#ES{$data.total - 1}"
                                    }
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}
```

当用户点击了左边的按钮，它会把当前数据模型中total的值加1，点击了右边的按钮，就会减去1。
