# 扩展数据输入源

Container组件的dataProvider提供了各种灵活的方式来让container组件支持从外界获取数据。

目前RCRE仅提供了ajax这一种数据获取的方式，肯定还不能满足一些特殊场景的要求。

本节提供了扩展数据输入源的方法。

## 概述

dataProvider通过为Container组件提供一个单一, 简单的API调用. 来对各种各样的数据获取操作进行封装

它支持控制同步的数据和异步的数据操作. 获取到目标数据之后,

DataProvider可以触发action来写入数据到redux store

## 逻辑视图
![img](https://ws1.sinaimg.cn/large/006tKfTcly1fp4be17994j31kw0lkqe3.jpg)

[查看大图](https://ws1.sinaimg.cn/large/006tKfTcly1fp4be17994j31kw0lkqe3.jpg)

## Provider抽象类
实现一个新的数据源通过实现一个dataProvider的子类来实现。每个dataProvider子类都是`BasicSyncProviderInterface`,`BasicSyncProviderInterface`这2个抽象类的不同实现。

dataProvider内部会有一个控制器来负责上图中的操作，以及负责将每个子类返回的数据写入到数据模型。

在编写子类无需关心如何跟RCRE的数据模型进行交互，只需要关系如何获取到数据即可。

DataProvider的控制器和各种Provider子类之间都通过抽象类定义的函数接口来进行通讯.

### BasicSyncProviderInterface 抽象类
如果实现的数据源是可以同步进行，不涉及任何异步操作。那么就可以基于`BasicSyncProviderInterface`这个抽象类来实现类。

抽象类有以下的一些API。

```typescript
export interface BasicSyncProviderInterface {
    /**
     * 对传入的配置进行验证
     */
    configCheck(provider: ProviderSourceConfig): boolean;
    /**
     * 实现函数来对返回值进行验证，可以基于ExpressionString来实现动态化
     */
    retCheck(ret: Object, provider: ProviderSourceConfig, props: ContainerProps, context: any): boolean;
    /*
    * 返回值解析，可以通过在配置中嵌入ExpressionString来对返回值做进一步修改
    */
    retParse(ret: Object, provider: ProviderSourceConfig, props: ContainerProps, context: any): Object;
    /*
    * 解析配置阶段，建议配合ExpressionString来使用
    */
    parse(provider: ProviderSourceConfig, config: ContainerProps, context: any): ProviderSourceConfig;
    /*
    * 调用此函数来获取数据，请实现提供的数据获取逻辑
    */
    run(provider: ProviderSourceConfig, options?: ProviderGlobalOptions): any;
    /**
     * 错误处理函数，可实现弹框逻辑弹出错误，如果retCheck返回false, configCheck返回false,或者run函数抛出错误就会触发
     */
    onError(errmsg: string, e?: Error): void;
}
```

实现例子：
```typescript
import {
    BasicSyncProviderInterface,
    ProviderGlobalOptions,
    ProviderSourceConfig,
    ContainerProps,
    getRuntimeContext,
    vm
} from 'rcre-core';
import {cloneDeep, isEmpty} from 'lodash';
import {notification} from '@native-ads/antd';

const {compileExpressionString, filterExpressionData} = vm;

export class InitDataProvider implements BasicSyncProviderInterface {
    configCheck(provider: ProviderSourceConfig) {
        // 初始化跳过检查
        return true;
    }

    parse(provider: ProviderSourceConfig, props: ContainerProps, context: any) {
        let data = provider.config;
        let runtime = getRuntimeContext(props, context);
        runtime.$data = filterExpressionData(cloneDeep(data));
        provider.config = compileExpressionString(data, runtime);

        // 初始化只初始化纯字面量
        return provider;
    }

    retCheck(ret: Object, provider: ProviderSourceConfig) {
        return !isEmpty(ret);
    }

    retParse(ret: Object, provider: ProviderSourceConfig, props: ContainerProps, context: any) {
        return ret;
    }

    run (provider: ProviderSourceConfig, options?: ProviderGlobalOptions) {
         let data = provider.config;

         return filterExpressionData(data);
    }

    onError(errmsg: string) {
        notification.error({
            message: '引擎数据初始化失败',
            description: errmsg
        });
    }
}

providerLoader.registerProvider('init', new InitDataProvider(), false);
```


### BasicAsyncProviderInterface 抽象类
如果数据源是通过异步获取的方式，就需要基于BasicAsyncProviderInterface这个抽象类来实现。

BasicAsyncProviderInterface 和BasicSyncProviderInterface只在run函数有一些区别，其他API都保持一致。

BasicAsyncProviderInterface的run函数需要返回值是一个Promise对象。
```typescript
export interface BasicAsyncProviderInterface {
    configCheck(provider: ProviderSourceConfig): boolean;
    retCheck(ret: Object, provider: ProviderSourceConfig, props: ContainerProps, context: any): boolean;
    retParse(ret: Object, provider: ProviderSourceConfig, props: ContainerProps, context: any): Object;
    parse(provider: ProviderSourceConfig, config: ContainerProps, context: any): ProviderSourceConfig;
    run(provider: ProviderSourceConfig, options?: ProviderGlobalOptions): Promise<any>;
    onError(errmsg: string, e?: Error): void;
}
```

### 导入到引擎内部
类写好了，就可以导入到引擎中试试效果了，通过RCRE中的providerLoader对象来导入写好的子类。

```typescript
import {providerLoader} from 'rcre-core';
providerLoader.registerProvider('init', new InitDataProvider(), false);
```

第一个参数是子类的名字，对应配置中的mode值，第二个是子类的实例，第三个参数判断是否是异步的子类。
