当列是 JSON 数组而不是字符串时如何过滤 ANTD 表

Posted

技术标签:

【中文标题】当列是 JSON 数组而不是字符串时如何过滤 ANTD 表【英文标题】:How to filter on ANTD table when the column is a JSON array instead of a string 【发布时间】:2019-07-30 10:29:11 【问题描述】:

我有一个包含 2 列的 antd 表,我需要在第一列进行过滤,并在第二列搜索文本。

从我的代码中,应用程序渲染得很好。请注意 tags 字段是 json 数组,而不是文本字段,所以我猜这与错误有关。

更新了 1 个代码。

import React,  Component  from 'react';
import   Table, Tag, Button, Icon, Input from 'antd';
import  adalApiFetch  from '../../adalConfig';
import Notification from '../../components/notification';
import Highlighter from 'react-highlight-words';

class ListPageTemplatesWithSelection extends Component 

    constructor(props) 
        super(props);
        this.state = 
            data: [],
            filteredInfo: null,
            sortedInfo: null,
            searchText: ''
        ;
        this.handleChange= this.handleChange.bind(this);
        this.clearFilters= this.clearFilters.bind(this);
        this.clearAll= this.clearAll.bind(this);
        this.getColumnSearchProps= this.getColumnSearchProps.bind(this);
        this.handleSearch= this.handleSearch.bind(this);
        this.handleReset= this.handleReset.bind(this);

    

    handleSearch (selectedKeys, confirm)
      confirm();
      this.setState( searchText: selectedKeys[0] );
    

    handleReset(clearFilters)
      clearFilters();
      this.setState( searchText: '' );
    

    getColumnSearchProps = (dataIndex) => (
        filterDropdown: (
        setSelectedKeys, selectedKeys, confirm, clearFilters,
      ) => (
        <div style= padding: 8 >
          <Input
            ref=node =>  this.searchInput = node; 
            placeholder=`Search $dataIndex`
            value=selectedKeys[0]
            onChange=e => setSelectedKeys(e.target.value ? [e.target.value] : [])
            onPressEnter=() => this.handleSearch(selectedKeys, confirm)
            style= width: 188, marginBottom: 8, display: 'block' 
          />
          <Button
            type="primary"
            onClick=() => this.handleSearch(selectedKeys, confirm)
            icon="search"
            size="small"
            style= width: 90, marginRight: 8 
          >
            Search
          </Button>
          <Button
            onClick=() => this.handleReset(clearFilters)
            size="small"
            style= width: 90 
          >
            Reset
          </Button>
        </div>
      ),
      filterIcon: filtered => <Icon type="search" style= color: filtered ? '#1890ff' : undefined  />,
      onFilter: (value, record) =>
      record[dataIndex]
        ? record[dataIndex]
            .toString()
            .toLowerCase()
            .includes(value.toLowerCase())
        : false,
      onFilterDropdownVisibleChange: (visible) => 
        if (visible) 
          setTimeout(() => this.searchInput.select());
        
      
    )

    handleChange(pagination, filters, sorter)
      console.log('Various parameters', pagination, filters, sorter);
      this.setState(
        filteredInfo: filters,
        sortedInfo: sorter,
      );
    

    clearFilters()
      this.setState( filteredInfo: null );
    

    clearAll()
      this.setState(
        filteredInfo: null,
        sortedInfo: null,
      );
    

    fetchData = () => 
        adalApiFetch(fetch, "/PageTemplates", )
          .then(response => response.json())
          .then(responseJson => 
            if (!this.isCancelled) 
                const results= responseJson.map(row => (
                    key: row.Id,
                    Name: row.Name,
                    SiteType: row.SiteType,
                    Tags: row.Tags
                  ))
              this.setState( data: results );
            
          )
          .catch(error => 
            console.error(error);
          );
      ;


    componentDidMount()
        this.fetchData();
    

    render()
          let  sortedInfo, filteredInfo  = this.state;
        sortedInfo = sortedInfo || ;
        filteredInfo = filteredInfo || ;

        const columns = [
                
                    title: 'Id',
                    dataIndex: 'key',
                    key: 'key',
                , 
                
                    title: 'Name',
                    dataIndex: 'Name',
                    key: 'Name',
                , 
                
                    title: 'Site Type',
                    dataIndex: 'SiteType',
                    key: 'SiteType',
                    filters: [
                       text: 'Modern Team Site', value: 'Modern Team Site' ,
                       text: 'CommunicationSite', value: 'CommunicationSite' ,
                    ],
                    filteredValue: filteredInfo.SiteType || null,
                    onFilter: (value, record) => record.SiteType.includes(value),
                ,
                  title: 'Tags',
                  key: 'Tags',
                  dataIndex: 'Tags',
                  ...this.getColumnSearchProps('Tags'),
                  render: Tags => (
                    <span>
                    Tags && Tags.map(tag => 
                      let color = tag.length > 5 ? 'geekblue' : 'green';
                      if (tag === 'loser') 
                        color = 'volcano';
                      
                      return <Tag color=color key=tag>tag.toUpperCase()</Tag>;
                    )
                  </span>)

                
        ];

        const rowSelection = 
            selectedRowKeys: this.props.selectedRows,
            onChange: (selectedRowKeys) => 
              this.props.onRowSelect(selectedRowKeys);
            
          ;


        return (
          <div>
            <Button onClick=this.clearFilters>Clear filters</Button>
            <Button onClick=this.clearAll>Clear filters and sorters</Button>
            <Table rowSelection=rowSelection  columns=columns dataSource=this.state.data onChange=this.handleChange />
          </div>
        );
    


export default ListPageTemplatesWithSelection;

但是,当我添加这一行时: ...this.getColumnSearchProps('Tags'),

然后我得到这个错误

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
▶ 23 stack frames were collapsed.
AsyncFunc._callee$
src/helpers/AsyncFunc.js:26
  23 | const  default: Component  = await importComponent();
  24 | Nprogress.done();
  25 | if (this.mounted) 
> 26 |   this.setState(
  27 |     component: <Component ...this.props />
  28 |   );
  29 | 

更新 2

这是容器组件

import React,  Component  from 'react';
import  Input from 'antd';
import Form from '../../components/uielements/form';
import Button from '../../components/uielements/button';
import Notification from '../../components/notification';
import  adalApiFetch  from '../../adalConfig';
import   ListPageTemplatesWithSelection  from './ListPageTemplatesWithSelection';

const FormItem = Form.Item;

class CreateCommunicationSiteCollectionForm extends Component 
    constructor(props) 
        super(props);
        this.state = Title:'',Url:'', SiteDesign:'', Description:'',Owner:'',Lcid:'', PageTemplateIds : [];
        this.handleChangeTitle = this.handleChangeTitle.bind(this);
        this.handleValidationCommunicationSiteUrl = this.handleValidationCommunicationSiteUrl.bind(this);
        this.handleChangeCommunicationSiteUrl = this.handleChangeCommunicationSiteUrl.bind(this);
        this.handleChangeSiteDesign = this.handleChangeSiteDesign.bind(this);
        this.handleChangeDescription = this.handleChangeDescription.bind(this);
        this.handleChangeOwner = this.handleChangeOwner.bind(this);
        this.handleChangelcid = this.handleChangelcid.bind(this);

        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleRowSelect = this.handleRowSelect.bind(this);
    

    handleRowSelect(ids) 
        this.setState( PageTemplateIds: ids );
    

    handleChangeTitle(event)
        this.setState(Title: event.target.value);
    

    handleValidationCommunicationSiteUrl(rule, value, callback)
        const form = this.props.form;
        const str = form.getFieldValue('communicationsiteurl');        
        var re = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/i;
        if (str && !str.match(re)) 
            callback('Communication site url is not correctly formated.');
         
        else 
            callback();
        
    

    handleChangeCommunicationSiteUrl(event)
        this.setState(Url: event.target.value);
    

    handleChangeSiteDesign(event)
        this.setState(SiteDesign: event.target.value);
    

    handleChangeDescription(event)
        this.setState(Description: event.target.value);
    

    handleChangeOwner(event)
        this.setState(Owner: event.target.value);
    

    handleChangelcid(event)
        this.setState(Lcid: event.target.value);
    

    handleSubmit(e)
        e.preventDefault();
        this.props.form.validateFieldsAndScroll((err, values) => 
            if (!err) 
                let data = new FormData();
                //Append files to form data
                //data.append(

                const options = 
                  method: 'post',
                  body: JSON.stringify(
                    
                        "Title": this.state.Title,
                        "Url": this.state.Url, 
                        "SiteDesign": this.state.SiteDesign,
                        "Description": this.state.Description,
                        "Owner": this.state.Owner,
                        "Lcid": this.state.Lcid,
                        "PageTemplateIds": this.state.PageTemplateIds
                    ),
                    headers: 
                            'Content-Type': 'application/json; charset=utf-8'
                                        
                ;

                adalApiFetch(fetch, "/SiteCollection/CreateCommunicationSite", options)
                  .then(response =>
                    if(response.status === 201)
                        Notification(
                            'success',
                            'Communication Site created',
                            ''
                            );
                     else
                        throw "error";
                     
                  )
                  .catch(error => 
                    Notification(
                        'error',
                        'Site collection not created',
                        error
                        );
                    console.error(error);
                );
            
        );      
    

    render() 
        const  getFieldDecorator  = this.props.form;
        const formItemLayout = 
        labelCol: 
            xs:  span: 24 ,
            sm:  span: 6 ,
        ,
        wrapperCol: 
            xs:  span: 24 ,
            sm:  span: 14 ,
        ,
        ;
        const tailFormItemLayout = 
        wrapperCol: 
            xs: 
            span: 24,
            offset: 0,
            ,
            sm: 
            span: 14,
            offset: 6,
            ,
        ,
        ;
        return (
            <Form onSubmit=this.handleSubmit>
                <FormItem ...formItemLayout label="Title" hasFeedback>
                getFieldDecorator('Title', 
                    rules: [
                        
                            required: true,
                            message: 'Please input your communication site title',
                        
                    ]
                )(<Input name="title" id="title" onChange=this.handleChangeTitle />)
                </FormItem>
                <FormItem ...formItemLayout label="Communication Site Url" hasFeedback>
                getFieldDecorator('communicationSiteUrl', 
                    rules: [
                        
                            required: true,
                            message: 'CommunicationSite site collection url',
                        ,
                        
                            validator: this.handleValidationCommunicationSiteUrl
                        
                    ]
                )(<Input name="communicationsSiteUrl" id="communicationsSiteUrl" onChange=this.handleChangeCommunicationSiteUrl />)
                </FormItem>
                <FormItem ...formItemLayout label="Site Design" hasFeedback>
                getFieldDecorator('sitedesign', 
                    rules: [
                        
                            required: true,
                            message: 'Please input your site design',
                        
                    ]
                )(<Input name="sitedesign" id="sitedesign" onChange=this.handleChangeSiteDesign />)
                </FormItem>
                <FormItem ...formItemLayout label="Description" hasFeedback>
                getFieldDecorator('description', 
                    rules: [
                        
                            required: true,
                            message: 'Please input your description',
                        
                    ],
                )(<Input name="description" id="description"  onChange=this.handleChangeDescription />)
                </FormItem>
                <FormItem ...formItemLayout label="Owner" hasFeedback>
                getFieldDecorator('owner', 
                    rules: [
                        
                            required: true,
                            message: 'Please input your owner',
                        
                    ],
                )(<Input name="owner" id="owner"  onChange=this.handleChangeOwner />)
                </FormItem>
                <FormItem ...formItemLayout label="Lcid" hasFeedback>
                getFieldDecorator('lcid', 
                    rules: [
                        
                            required: true,
                            message: 'Please input your lcid',
                        
                    ],
                )(<Input name="lcid" id="lcid"  onChange=this.handleChangelcid />)
                </FormItem>          

                <ListPageTemplatesWithSelection onRowSelect=this.handleRowSelect selectedRows=this.state.PageTemplateIds/>


                <FormItem ...tailFormItemLayout>
                    <Button type="primary" htmlType="submit">
                        Create communication site
                    </Button>
                </FormItem>


            </Form>



        );
    


const WrappedCreateCommunicationSiteCollectionForm = Form.create()(CreateCommunicationSiteCollectionForm);
export default WrappedCreateCommunicationSiteCollectionForm;

【问题讨论】:

能否给个codepen项目? 我看不到任何状态:任何;在你的代码中。这是在驱动类中实现的吗?您必须查找定义“状态”的位置,然后才能找到问题。 我不知道什么是 codepend,它可能不会运行,因为它有带身份验证的服务器端 api in src/helpers/AsyncFunc.js:26 23 | const default: Component = await importComponent(); 这实际上是未定义的吗?一些错误的递归导入? 【参考方案1】:

正如另一个帖子中所说,如果您尝试导入不存在的组件,则可能会出现此错误。确保您没有拼写错误,并且该组件确实以这种方式命名。如果是库,请确保使用正确的版本,因为组件在不同版本中可能有不同的名称。

有类似问题的链接:Uncaught Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function but got: objectReact.createElement: type is invalid -- expected a string or a class/function but got: undefined

【讨论】:

【参考方案2】:

很难从您提供的错误中猜测出了什么问题。所以我能做的最好的就是指出一些你应该注意的事情。

getColumnSearchProps() 的渲染方法错误。如果文本是null(由于一行没有任何标签),它将尝试将其转换为字符串并崩溃。为避免这种情况,请在渲染前检查 text 是否存在:

render: text =>
      text ? (
        <Highlighter
          highlightStyle= backgroundColor: "#ffc069", padding: 0 
          searchWords=[this.state.searchText]
          autoEscape
          textToHighlight=text.toString()
        />
      ) : null

同样适用于onFilter 方法:

onFilter: (value, record) =>
  record[dataIndex]
    ? record[dataIndex]
        .toString()
        .toLowerCase()
        .includes(value.toLowerCase())
    : false,

您有两个 render 方法用于呈现标签列。一个在getColumnSearchProps() 内部,一个在...this.getColumnSearchProps('Tags') 调用之后。这应该没问题,因为后者将覆盖前者。话说回来,贵重的东西不需要的话,为什么要申报呢?

希望这会有所帮助。

【讨论】:

highlighter 它的另一个组件,它突出显示您搜索的文本,就像我认为的 antd 文档中的那样 我明白了。您可以尝试删除 getColumnSearchProps() 的渲染方法,因为您稍后会提供自己的渲染方法并看到它有效。 我把第二个地方的render去掉了,修改了onfilter如上图,还是报同样的错误screencast.com/t/UZkm6bRBq 我复制了您的代码here,它工作正常。看起来问题可能出在其他地方。如果您可以添加与错误相关的其他部分的MCVE(例如AsyncFunc.js),这可能会有所帮助。 我稍后会在这里更新问题,基本上该组件嵌入在我编写的另一个组件中,上层组件链的其余部分来自模板,所以我希望我不必更改模板组件上的任何内容,我相信 AsyncFunc.cs 不是我的代码,但我会稍后检查并在此处更新

以上是关于当列是 JSON 数组而不是字符串时如何过滤 ANTD 表的主要内容,如果未能解决你的问题,请参考以下文章

当列是NTEXT时,SQL Server:IN('asd')不工作

PySpark:当列是列表时,将列添加到 DataFrame

当列是现有列的布尔测试时,为啥向 Pandas DataFrame 添加列会返回 SettingWithCopy 警告?

当列是整数和小数的混合时,创建表时使用哪种数据类型?

当列数据类型为列表时如何过滤熊猫数据框

Pandas groupby - 当列是某个值时