Suggest 3.0.0

输入提示组件, 根据用户的输入给出待选项并展示在输入框下方。 Suggest的内容分为两个区域, 推荐区域(recommendTmpl)会在用户输入开始前渲染, 可以用来给出一些热门推荐。 结果区域(resultTmpl)用来响应用户的输入, 根据用户的输入给出输入提示。

使用说明

引用方式

import { Suggest } from '$yo-component';

// 如果你的项目中未使用最新的 ykit-config-yo 插件,可能无法使用上面这个语法糖
// 你仍然可以通过下面这种方式来引用
import Suggest from 'yo3/component/suggest';

基础使用

Suggest一般是一个占满整个页面的组件,它也可以配合模态框系列组件一起使用。在默认条件下,Suggest的根节点会撑满整个父级容器。

Suggest 的最重要的属性是 resultsonConditionChange。使用这两个属性就可以改变结果区域的内容:

<Suggest
    onConditionChange={condition => {
        this.setState({ results });
    }}
    results={this.state.results}
/>

你应该把 results 属性和你应用的状态关联起来,这样在输入框的内容发生改变的时候会触发 conditionChange 事件,这时你可以根据新的 condition 字符串(onConditionChange的唯一参数)去改变 results ,达到更新结果区域的目的。

定制结果列表项的渲染方式

results 默认的渲染方式是 List ,如果 results 属性数组中的每一个元素都有 text 属性, 会直接使用这个属性值作为列表项的内容。或者,就像 List 一样, 你也可以传入 renderItem 属性来指定列表项的渲染方式。它接收的参数和 List 的同名属性一致。以下是一个自定义 renderItem 的例子:

<Suggest
    results={[
        {value:'beijing'},
        {value:'shanghai'},
        // ...
    ]}
    renderItem={(item,i) => {
        return <p>`第${i}项的value是${item.value}`</p>
    }}
    noDataTmpl={<div>Nothing to show.</div>}
/>

通过定义 noDataTmpl 可以定制没有 results 数据时展示的内容,它可以接收一个 JSX,在 results.length0 时会使用这个 JSX 渲染结果区域。

自定义结果区域的渲染方式

如果你不希望以默认的 List 形式渲染结果区域,可以通过定义 renderResult 属性来实现,你需要传入一个函数,这个函数可以接收到一个参数 results, 并用它返回的 JSX/DOMElement 来完全取代原来的 List

<Suggest
    results={/* ... */}
    renderResult={results => {
        return (
            <CustomResult>
                {results.map(result => {
                    return (
                        <ResultItem result={result} key={result.key}/>
                    );
                })}
            </CustomResult>
        );
    }}
/>

推荐区域

在输入框没有输入文字时,结果区域会被隐藏,取而代之的是推荐区域。换句话说,用户在打开 Suggest 时首先看到的是输入框以及推荐区域。

使用 recommendTmpl 可以指定推荐区域的渲染方式,它接收一个 JSX。

有些时候,你可能在聚焦到 input 时希望使用一个蒙层盖住下面的推荐区域,这样用户可以很方便的通过点击推荐区域来关闭键盘。使用 showMask 属性可以控制 蒙层是否在聚焦时展示蒙层。参见下面的例子:

<Suggest
    recommentTmpl={<CityGrouplist /*...*/ />}
    showMask={true}
    // ...
/>

使用输入框图标

一般来说,Suggest的输入框的最右侧都会根据当前的输入状态展示一个图标(例如输入框中有文字时会展示一个叉子用来快速清除输入)。Suggest组件提供了 四种 icon:deleteloadingrefreshstop 。通过定义 inputIcon 属性,可以指定当前展示在输入框中的是哪一个图标,如果传入 null ,就不会 显示图标。

delete 图标的点击行为是确定的,即清空当前输入框的内容。对于其他三种图标,你可以指定 onIconTap 属性来定义点击它们的回调:

<Suggest
    inputIcon={this.state.icon}
    onIconTap={icon => {
      switch (icon) {
        case 'refresh':
          // do something...
        case 'stop':
          // ...
        }
    }}
    // ...
/>

onIconTap可以接收一个参数 icon ,它会是上面四种图标的名称的字符串,这样你可以为不同的图标指定不同的点击回调。

输入事件的性能优化

每次 input 中 value 的改变都会触发 onConditionChange ,在用户输入较快时,可能会频繁导致 dom 更新,以及向服务器发送大量的请求,这对于 App 的性能十分不利。 为此我们提供了优化性能的手段——事件截流。

配置 throttleGap 属性即可开启事件截流(默认是开启的,值为 300)。这个属性值表示间隔多少毫秒触发一次 onConditionChange,这样可以有效地减少无用的输入导致的 onChange。

<Suggest
    throttleGap={500}
    // ...
/>

这样可以让 onConditionChange500 毫秒触发一次。

一个完整的城市选择器示例

以下的代码就是第一个 Demo 的源码,里面利用了 GrouplistSuggest 实现了一个完整的城市选择器。在这份代码里包含了上面所有介绍的属性的使用。

class CitySelectDemo extends Component {
  constructor() {
    super();
    this.state = {
      results: [],
      showLoadingIcon: false,
      showCancelButton: false
    };
  }

  filterCity(condition) {
    return condition ? groupListDataSource
      .filter(city=>city.groupKey !== '热门')
      .filter(city=>city.py.search(condition) !== -1 || city.text.search(condition) !== -1) : [];
  }

  render() {
    //实现一个如此复杂的组件只用了不到30行代码
    //使用React,可以很容易地像搭积木一样用小组件搭建出大组件,而不是不停地重复造轮子
    //善用组件化,开发效率可以成倍的提升
    return (
        <Page title="城市选择示例" onLeftPress={() => location.href = '../index.html'}>
          <Suggest
            showMask={true}
            ref="suggest"
            noDataTmpl={(
              <div style={{ padding: '1em' }}>
                {!this.state.loading ? 'No Result' : 'Loading...'}
              </div>
            )}
            showCancelButton={this.state.showCancelButton}
            onCancelButtonTap={()=> {
              this.refs.suggest.clearInput();
            }}
            onFocus={()=> {
              this.setState({ showCancelButton: true });
            }}
            onBlur={()=> {
              this.setState({ showCancelButton: false });
            }}
            recommendTmpl={(
              <Grouplist
                infinite={true}
                itemHeight={44}
                infiniteSize={25}
                dataSource={groupListDataSource}
                sort={(a, b) => {
                  if (a === '热门') {
                    return -1;
                  }
                  else if (b === '热门') {
                    return 1;
                  }
                  return a.charCodeAt(0) - b.charCodeAt(0);
                }}
                showIndexNavBar={true}
                onIndexNavBarItemHover={(groupKey) => Toast.show(groupKey, 1000)}
                onItemTap={item => Toast.show('选择:' + item.text, 1000)}
              />
            )}
            inputIcon={this.state.loading ? 'loading' : 'delete'}
            results={this.state.results}
            onConditionChange={value => {
              this.setState({ loading: true });
              setTimeout(() => {
                this.setState({ loading: false, results: this.filterCity(value) });
              }, 300);
            }}
            onItemTap={item => Toast.show('选择:' + item.text, 1000)}
            placeholder="输入城市名称或拼音"
            throttleGap={500}
          />
      </Page>
    );
  }
}

将Suggest和弹层结合使用

Suggest最常见的使用方式有两种: 完全使用一个新的页面展示; 或者打开一个模态框来展示。 对于后者来说,你需要做的仅仅是把 Suggest 放入一个模态框系列组件中。

在使用模态层展示时,你可能需要在输入框的右侧显示一个"取消"按钮用来关闭它,设置 showCancelButton 属性为 true 就可以展示这个取消按钮, 然后通过 onCanelButtonTap 来定义它的行为。

下面是一个完整的例子:

class UseWithPopupDemo extends Component {
    constructor() {
        super();
        this.state = {
            show: false,
            results: [],
            defaultCondition: ''
        };
    }

    render() {
        return (
            <Page title="带弹层的Suggest" onLeftPress={()=>location.href = "../index.html"}>
                <div className="container">
                    <button
                        className="yo-btn open-modal"
                        onTouchTap={()=> {
                            this.setState({ show: true });
                        }}
                    >
                        与Popup一起使用,点我打开
                    </button>
                    <Popup
                        onMaskClick={()=> {
                            this.setState({ show: false })
                        }}
                        show={this.state.show}
                        height="100%"
                    >
                        <Suggest
                            ref="suggest"
                            showMask={false}
                            showCancelButton={true}
                            onCancelButtonTap={()=> {
                                this.refs.suggest.clearInput();
                                this.setState({ show: false });
                            }}
                            onConditionChange={value=> {
                                this.setState({ results: value ? getRandomDataSource(10) : [] });
                            }}
                            onItemTap={item=>Toast.show('tapping:' + item.text)}
                            defaultCondition={this.state.defaultCondition}
                            results={this.state.results}
                            recommendTmpl={<p style={{ padding: '1em' }}>设置showCancelButton为true可以展示取消按钮</p>}
                            noDataTmpl={<div style={{ padding: '1em' }}>No Results</div>}
                        />
                    </Popup>
                </div>
            </Page>
        );
    }
}
属性

results { Array } #

渲染在结果区的数据源,数组类型,数组元素的类型可以是字符串/数字,它们会直接作为列表项的内容;

也可以是对象,这个对象必须有text属性。

默认值: null

onConditionChange { Function } #

输入框onChange事件回调,必需。

为了使组件正常工作,你必须定义这个属性,根据每次的value来更新results。

默认值: null

方法参数:

参数名 类型 描述 支持版本
value String 输入框当前的value

extraClass { String } #

附加给组件根节点的额外类名。

默认值: null

itemTouchClass { String } #

点击结果区域列表项时添加的className。

默认值: item-light

noDataTmpl { Element } #

没有suggest结果时的模板。 noDataTpl

默认值: null

recommendTmpl { Element } #

推荐区域内容,在搜索条件为空时展示。

默认值: null

onItemTap { Function } #

点击结果项时的回调。

默认值: () =>{}

方法参数:

参数名 类型 描述 支持版本
item Object 数据源中的元素
index Number item在数据源中的index

renderItem { Function } #

自定义结果项的渲染方式,返回JSX或字符串。

默认值: Suggest.renderItem

方法参数:

参数名 类型 描述 支持版本
item Object 结果项的数据对象,格式为{value,text}

renderResult { Function } #

自定义结果容器的渲染方式,返回JSX。

组件默认以List的形式渲染结果区域,如果不希望以List的形式展示结果,可以传入这个函数。组件会使用这个函数返回的JSX渲染结果区域。

默认值: null

方法参数:

参数名 类型 描述 支持版本
results 结果列表

infinite { Bool } 3.0.4 #

是否在结果区域的列表开启Infinite模式。注意:开启Infinite模式后,你需要为列表项配置key属性。

默认值: false

itemHeight { Number } 3.0.4 #

结果区域列表项的高度,只在Infinite模式下生效。

默认值: 44

infiniteSize { Number } 3.0.4 #

无穷列表模式下,保留在列表容器中列表项的个数(参见List组件无穷列表模式的说明)。

默认值: 20

showCancelButton { Bool } #

是否显示取消按钮,默认不显示。

默认值: false

cancelButtonText { String } #

取消按钮文本。

默认值: 取消

onCancelButtonClick { Function } #

点击取消按钮时的回调。

默认值: () =>{}

onSubmit { Function } #

点击键盘确定按钮时触发的回调。

默认值: ()=>{}

方法参数:

参数名 类型 描述 支持版本
condition 当前输入框的value

onFocus { Function } #

输入框聚焦时的回调。

默认值: () =>{}

方法参数:

参数名 类型 描述 支持版本
condition 当前输入框的value

onBlur { Function } #

输入框失去焦点时的回调。

默认值: () =>{}

方法参数:

参数名 类型 描述 支持版本
condition 当前输入框的value

defaultCondition { String } #

展示在输入框中的默认值。

默认值: null

placeholder { String } #

输入框的placeholder。

默认值: null

inputIcon { String } #

展示在输入框右侧的icon,有四个icon可供选择:delete,loading,refresh和stop。

delete图标点击以后会清除输入框的内容,其余的三个图标可以通过传入onIconTap属性来定制点击它们的回调。

默认值: 'delete'

onIconTap { Function } #

点击input icon触发的回调。

默认值: () =>{}

方法参数:

参数名 类型 描述 支持版本
iconName 图标名称
condition 当前输入框的value

throttleGap { Number } #

设置此属性以后,文本框的onChange事件的触发频率会降低,例如设置为300会使得onChange每300毫秒触发一次.

通过这种方式,可以控制组件结果区域的render次数,降低和服务器交互的频率。

默认值: 300

showMask { Bool } #

在弹起键盘时,是否显示遮罩层。

方法

clearInput #

清空输入框的内容