9

I'm using the material-table module here [1] with remote data from a web service. I've included the search parameter, but it's not working and there are no error messages in the console.

Here is the code:

import React from 'react';
import ReactDOM from "react-dom";
import MaterialTable from 'material-table';


import { forwardRef } from 'react';

import AddBox from '@material-ui/icons/AddBox';
import ArrowUpward from '@material-ui/icons/ArrowUpward';
import Check from '@material-ui/icons/Check';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import DeleteOutline from '@material-ui/icons/DeleteOutline';
import Edit from '@material-ui/icons/Edit';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Remove from '@material-ui/icons/Remove';
import SaveAlt from '@material-ui/icons/SaveAlt';
import Search from '@material-ui/icons/Search';
import ViewColumn from '@material-ui/icons/ViewColumn';

const tableIcons = {
    Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
    Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
    Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
    Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
    DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
    Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
    Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref} />),
    Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
    FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
    LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
    NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
    PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref} />),
    ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
    Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
    SortArrow: forwardRef((props, ref) => <ArrowUpward {...props} ref={ref} />),
    ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
    ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />)
};

const GET_PUBLICATIONS_URL = 'http://193.62.54.159/backend/v1/publications?';


class App extends React.Component {

    render() {
        return (
            <MaterialTable
                icons={tableIcons}
                title="Remote Data Preview"
                columns={[
                    { title: 'Publication ID', field: 'publicationId' },
                    { title: 'PMID', field: 'pmid' },
                    { title: 'First author', field: 'firstAuthor' },
                    { title: 'Publication', field: 'title' },
                    { title: 'Journal', field: 'journal' },
                    { title: 'Status', field: 'status' },
                ]}
                data={query =>
                    new Promise((resolve, reject) => {
                        // let url = 'https://reqres.in/api/users?'
                        let url = GET_PUBLICATIONS_URL
                        url += 'size=' + query.pageSize
                        url += '&page=' + (query.page)
                        fetch(url)
                            .then(response => response.json())
                            .then(result => {
                                resolve({
                                    data: result._embedded.publications,
                                    page: result.page.number,
                                    totalCount: result.page.totalElements,
                                })
                            })
                    })
                }
                options={{
                    search: true
                }}
            />
        )
    }
}

// export default DemoMUITable_RemoteData;
ReactDOM.render(<App />, document.getElementById("root"));

Here is an example of the output from the web service call used above:

{
    _embedded: {
        publications: [{
                publicationId: "5d2dbec2483e4bcddc82c61c",
                pmid: "jqfWof0a6N",
                title: "hDoClFK1xW",
                journal: "bsmw70kDBz",
                firstAuthor: "NUed57buOd",
                publicationDate: "2019-07-16T12:07:01.937Z",
                correspondingAuthor: {
                    authorName: "VRbSDd72mC",
                    email: "YgH4UoELBp"
                },
                status: "ELIGIBLE",
                _links: {
                    self: {
                        href: "http://193.62.54.159/backend/v1/publications/5d2dbec2483e4bcddc82c61c?pmid=false"
                    }
                }
            },
            {
                publicationId: "5d2dbeff483e4bcddc82c61d",
                pmid: "WlQsCFYg3b",
                title: "bsthfcP7zY",
                journal: "sUULVFkYJQ",
                firstAuthor: "NUed57buOd",
                publicationDate: "2019-07-16T12:07:01.937Z",
                correspondingAuthor: {
                    authorName: "UOTBdYbsRh",
                    email: "ZUmHQ7evjl"
                },
                status: "ELIGIBLE",
                _links: {
                    self: {
                        href: "http://193.62.54.159/backend/v1/publications/5d2dbeff483e4bcddc82c61d?pmid=false"
                    }
                }
            },
            {
                publicationId: "5d2dbf05483e4bcddc82c61e",
                pmid: "V1KphgdwaG",
                title: "KoXVQJjoGp",
                journal: "1bO9QNNDCM",
                firstAuthor: "NUed57buOd",
                publicationDate: "2019-07-16T12:07:01.937Z",
                correspondingAuthor: {
                    authorName: "KsfpbpEAGc",
                    email: "p69YIXvYEq"
                },
                status: "ELIGIBLE",
                _links: {
                    self: {
                        href: "http://193.62.54.159/backend/v1/publications/5d2dbf05483e4bcddc82c61e?pmid=false"
                    }
                }
            },
            {
                publicationId: "5d2dbf0a483e4bcddc82c61f",
                pmid: "JEmARsAgWM",
                title: "CiOTlRIeD4",
                journal: "l1ofJObwtJ",
                firstAuthor: "NUed57buOd",
                publicationDate: "2019-07-16T12:07:01.937Z",
                correspondingAuthor: {
                    authorName: "rGdQGN5oPh",
                    email: "6PI3NfrnmV"
                },
                status: "ELIGIBLE",
                _links: {
                    self: {
                        href: "http://193.62.54.159/backend/v1/publications/5d2dbf0a483e4bcddc82c61f?pmid=false"
                    }
                }
            },
            {
                publicationId: "5d346e129bb39d8c6e33faa4",
                pmid: "cNUmFPonBy",
                title: "0LwQYP0fUK",
                journal: "C3gQOWPZ2C",
                firstAuthor: "q1LwUyYpgO",
                publicationDate: "2019-07-16T12:07:01.937Z",
                correspondingAuthor: {
                    authorName: "oJdt6ae7sp",
                    email: "jhdOF23b9m"
                },
                status: "ELIGIBLE",
                _links: {
                    self: {
                        href: "http://193.62.54.159/backend/v1/publications/5d346e129bb39d8c6e33faa4?pmid=false"
                    }
                }
            }
        ]
    },
    _links: {
        first: {
            href: "http://193.62.54.159/backend/v1/publications?page=0&size=5"
        },
        self: {
            href: "http://193.62.54.159/backend/v1/publications"
        },
        next: {
            href: "http://193.62.54.159/backend/v1/publications?page=1&size=5"
        },
        last: {
            href: "http://193.62.54.159/backend/v1/publications?page=5&size=5"
        }
    },
    page: {
        size: 5,
        totalElements: 26,
        totalPages: 6,
        number: 0
    }
}

Here are my app's dependencies:

"dependencies": {
    "@material-ui/icons": "^4.2.1",
    "material-table": "^1.40.1",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-scripts": "3.0.1"
  },

[1] https://github.com/mbrn/material-table

tw1742
  • 1,424
  • 4
  • 18
  • 37
  • Seems to work fine with the example output you posted. You can check it out here: https://wdre6.csb.app/. The problem probably lies within your resolved data. – Isaac Pak Jul 24 '19 at 16:04
  • @IsaacPak Yes, I see it is working there (https://wdre6.csb.app/) Do you have any suggestions what the issue could be with the resolved data, how to debug and solve the issue? – tw1742 Jul 24 '19 at 16:07
  • `data: result._embedded.publications` vs `data: result.data._embedded.publications` – Isaac Pak Jul 24 '19 at 16:20
  • you can play around with it here: https://codesandbox.io/s/search-remote-data-wdre6 – Isaac Pak Jul 24 '19 at 16:28

3 Answers3

6

The query object contains a search field, which is the current search value. You have to pass that to your online query and filter it within the backend or filter it within the frontend like this:

 resolve({
       data: result._embedded.publications.filter(pub => pub.firstAuthor.contains(query.search)),
       page: result.page.number,
       totalCount: result.page.totalElements,
 })

if you filter it in the frontend, you should write your custom filter function to filter all relevant fields like name, id etc.

Domino987
  • 8,475
  • 2
  • 15
  • 38
  • Not sure if that's the issue. Check out my sandbox, it requires no search property. – Isaac Pak Jul 24 '19 at 16:21
  • because that uses the local data, if it uses remote data, the filtering should be done within the backend – Domino987 Jul 24 '19 at 16:44
  • @Domino987 can you expand on "the query object contains a search field"? Does that mean that `query.search` is provided by the module? And I'm still getting used to the arrow notation, where do I add the call to the custom filter function? – tw1742 Jul 24 '19 at 16:51
  • 2
    yes query.search is the text in your search field. Add that as a parameter to the url like this: url += '&search=' + (query.search), extract that in your backend and return only the filtered data to the api call – Domino987 Jul 24 '19 at 16:54
  • Thanks, that makes sense. ATM, I don't have that parameter in a backend call. Do you suggest for scalability it is better to have the web service do the filtering? – tw1742 Jul 24 '19 at 16:58
  • material-table already has [filters](https://material-table.com/#/docs/features/filtering). This kind of backend optimization is probably worthwhile with large datasets. – Isaac Pak Jul 24 '19 at 17:10
  • It depends, if you have many publications, it will be better to download only the ones you currently display. If you need to have them locally anyway, just pass them from parent props into the data prop if the table and everything will work fine. But if that is the only place, yes it will be better to filter them in the Backend – Domino987 Jul 24 '19 at 17:13
  • @Isaac Pak the filtering does not work for remote data – Domino987 Jul 24 '19 at 17:18
  • I estimate 5-10K publications in the list. Users _should_ filter by PMID and that will return 1 publication. @Domino987 do you have an example of how the frontend filtering can be set-up? I'm just a few weeks into React and still sorting out a lot :) – tw1742 Jul 24 '19 at 17:20
  • Like this in the return of data after the fetch API : data: result._embedded.publications.filter(pub => pub.firstAuthor.contains(query.search)). You can change that firstauthor to what you need to filter – Domino987 Jul 24 '19 at 17:36
  • Oh, I see. I got an error before and thought more was needed. I needed to use `inlcudes` for my data object, e.g. `data: result._embedded.publications.filter(pub => pub.pmid.includes(query.search) || pub.firstAuthor.includes(query.search))` However, this only filters the rows currently viewable. – tw1742 Jul 24 '19 at 18:45
  • 1
    Because these are the publications you get when calling the function. These are only the once currently shown without filter but that does not have to correspond with the filtered data. That's why filtering in the Backend would be beneficial. – Domino987 Jul 24 '19 at 21:06
5

For anyone who came here looking for solution how to do server search in material-table using hooks.

There was a bug (https://github.com/mbrn/material-table/pull/1611 which was breaking search field, so make sure to have material-table@1.60.0

codesandbox.io/s/material-table-server-search-on-hooks-tnniz

import React, { useEffect, useState } from "react";
import MaterialTable from "material-table";
import useAxios from "axios-hooks";

const MaterialTableSearchDemo = () => {
  const [query, setQuery] = useState("");
  const [items, setItems] = useState([]);
  const [{ data = {}, loading }, runSearch] = useAxios(
    `https://api.github.com/search/repositories?q=${query}&sort=stars&order=desc&type=Repositories`,
    { manual: true }
  );

  const columns = [
    { title: "Name", field: "name" },
    { title: "Stars", field: "stargazers_count" }
  ];

  useEffect(() => {
    if (query) {
      runSearch();
    } else {
      setItems([]);
    }
  }, [query, runSearch]);

  useEffect(() => {
    if (!loading && Array.isArray(data.items)) {
      setItems(data.items);
    }
  }, [loading, setItems, data.items]);

  return (
    <MaterialTable
      title="Github repositories"
      data={items}
      columns={columns}
      onSearchChange={setQuery}
      options={{
        debounceInterval: 500,
        paging: false,
        //searchAutoFocus: true
      }}
      localization={{
        toolbar: {
          searchPlaceholder: "e.g. React"
        }
      }}
      isLoading={loading}
    />
  );
};

export default MaterialTableSearchDemo;
keemor
  • 1,149
  • 15
  • 16
0

If you want to search inside all the rows and columns of the table, you can use the following function.

resolve({
           data: result._embedded.publications.filter(function(obj) {
          return Object.keys(obj).some(function(key) {
            return obj[key] ? (obj[key]).toString().includes(query.search) : false;
          })
        }),
           page: result.page.number,
           totalCount: result.page.totalElements,
     })

remoteSearch=(arr, searchKey)=>{
    return arr.filter(function(obj) {
      return Object.keys(obj).some(function(key) {
        return obj[key] ? (obj[key]).toString().includes(searchKey) : false;
      })
    });
  }
  
  
  console.log(remoteSearch([{a:1},{b:"1"},{c:"test1"}],1))