How to hide button after pressing in material-table - material-table

I am using react material-table. I am in need of a feature like this: I use remote data mode to get a list of records, then I use the custom column rendering function to add a Material Button at the end of each row in the table, When the user presses this button I want it to be hidden. How can I do that. I look forward to receiving your help.
This is the illustration image

I made this example, on button click it gets disabled and a variable is set to to a loading state:
The key aspect here is to define something that identifies the row that is being updated. I use an extra column on which you could also display a spinner component:
{
field: "isUpdating",
render: (rowdata) =>
fetchingClient === rowdata.name
? "loading.." // Add your <Spinner />
: null
},
Since you want to render the button as a custom column (other way could be using actions), on the render attribute of that column, you can use rowdata parameter to access what you are looking for:
{
field: "join",
sorting: false,
render: (rowdata) => (
<button
disabled={fetchingClient === rowdata.name}
onClick={(event) => fetchDataFromRemote(rowdata.name)}
>
Go fetch
</button>
)
}
Here is the link to the sandbox and the complete code, I hope this works for you!
import React, { Fragment, useState } from "react";
import MaterialTable from "material-table";
export default function CustomEditComponent(props) {
const [fetchingClient, setFetchingClient] = useState("");
const fetchDataFromRemote = (clientName) => {
console.log(clientName);
setFetchingClient(clientName);
};
const tableColumns = [
{ title: "Client", field: "client" },
{ title: "Name", field: "name" },
{
field: "isUpdating",
render: (rowdata) =>
fetchingClient === rowdata.name
? "loading.." // Add your <Spinner />
: null,
},
{
field: "join",
sorting: false,
render: (rowdata) => (
<button
disabled={fetchingClient === rowdata.name}
onClick={(event) => fetchDataFromRemote(rowdata.name)}
>
Go fetch
</button>
),
},
];
const tableData = [
{
client: "client1",
name: "Jasnah",
year: "2019",
},
{
client: "client2",
name: "Dalinar",
year: "2018",
},
{
client: "client3",
name: "Kal",
year: "2019",
},
];
return (
<Fragment>
<MaterialTable
columns={tableColumns}
data={tableData}
title="Material Table - custom column "
options={{ search: false }}
/>
</Fragment>
);
}

Related

TypeError: undefined is not a function (near '...mockAchievements.map...')

I'm trying to create an individual switch for each item in the list, each item has an isEnabled that by default it has which is false, and I just want to leave it true when I click on the react native switch, but I'm getting this error whenever I try to activate or disable this switch
import {Switch} from "react-native";
const [mockAchievements, setAchievements] = useState([
{
isEnabled: false,
},
{
isEnabled: false,
}]
function toggleSwitch(value, index) {
setAchievements((prevAchievements) => {
return {
...prevAchievements,
[index]: {
...prevAchievements[index],
isEnabled: value,
},
};
});
}
<Switch
trackColor={{ false: "#E9E9E9", true: "#EEF8FC" }}
thumbColor={item.isEnabled ? "#006993" : "#CBCBCB"}
ios_backgroundColor="#CBCBCB"
onValueChange={(value) => {
toggleSwitch(value, index);
}}
value={item.isEnabled}
/>
Use this function instead
function toggleSwitch(value, index) {
let dat = [...mockAchievements];
dat[index].isEnabled = value;
setAchievements(dat)
}
and if I am not wrong, your function returns the state as an object instead of an array

React admin import button referenceManyField?

I want add an import button for my react admin application.
This import will create records related to another record. For instance, I have a list of contracts that belong to a company.
I saw this repo: https://github.com/benwinding/react-admin-import-csv
And I was wondering if there was something similar for a referenceManyField.
I found a hacky solution for this in case anyone is interested.
Instead of using a ReferenceManyField I used a List inside a Show component.
To be able to do this I had to override the props of the list to specify the resource that I wanted to reference.
I also had to pass the show item id as a prop of the list component and filter the list by the id.
let companyId = '';
const ListActions = (props) => {
const { className } = props;
return (
<TopToolbar className={className}>
<ImportButton {...props} />
</TopToolbar>
);
};
export const ContractRow = ({ contractCompanyId }) => {
companyId = contractCompanyId;
const fakeProps: ListProps = {
basePath: '/contracts',
hasCreate: false,
hasEdit: false,
hasList: true,
hasShow: false,
location: { pathname: '/', search: '', hash: '', state: undefined },
match: { path: '/', url: '/', isExact: true, params: {} },
options: {},
permissions: null,
resource: 'contracts',
};
return (
<List
{...fakeProps}
filter={{ _company_id: contractCompanyId }}
actions={<ListActions />}
>
<Datagrid className={classes.insideTable}>
<TextField source="name" />
</Datagrid>
</List>
);
};

React-Admin: implementing dependant filters

I'm working on a react-admin page where I want to display some filters depending on the values of another one. To do so, I'm using the filterValues from the ListContext and simply show/hide the dependant filters:
const PostFilter = (props) => {
const { filterValues } = useListContext();
const { mainType } = filterValues;
return (
<Filter {...props}>
<SelectInput
source="mainType"
alwaysOn
choices={[
{ id: "type_1", name: "Type 1" },
{ id: "type_2", name: "Type 2" },
{ id: "type_3", name: "Type 3" }
]}
/>
{mainType === "type_1" && (
<SelectInput
source="type1"
alwaysOn
choices={[
{ id: "type_1_a", name: "A" },
{ id: "type_1_b", name: "B" },
{ id: "type_1_c", name: "C" }
]}
/>
)}
{mainType === "type_2" && (
<SelectInput
source="type2"
alwaysOn
choices={[
{ id: "type_2_a", name: "A" },
{ id: "type_2_b", name: "B" },
{ id: "type_2_c", name: "C" }
]}
/>
)}
</Filter>
);
};
This visually works fine but if I choose a value in one of the child filter and then change the main filter, the url is still containing the filter value even though it's not on screen anymore. As the url contains a wrong filter, then the list is not filtered properly.
To workaround this, I played with a useEffect and the setFilters function but it feels super hacky:
const { filterValues, displayedFilters, setFilters } = useListContext();
const { mainType } = filterValues;
// we need this workaround because these 3 values are not stable
// and get be used as the effect dependencies. However, we still want to be sure
// to always reference their latest version
const refs = React.useRef({ filterValues, displayedFilters, setFilters });
React.useEffect(() => {
refs.current = { filterValues, displayedFilters, setFilters };
});
React.useEffect(() => {
const filters = { ...refs.current.filterValues };
if (mainType !== "type_1") {
delete filters.type1;
}
if (mainType !== "type_2") {
delete filters.type_2;
}
refs.current.setFilters(filters, refs.current.displayedFilters);
}, [mainType]);
Here's a sandbox showing the result: https://codesandbox.io/s/hidden-mountain-i62p3?file=/src/posts.js
Is there a better way to achieve what I want?

Programmatically Filter with function component, impossible to get the filter - react-bootstrap-table-2

I'm trying to implement a custom filter using react bootstrap table 2 in a function component, but when I use the getFilter function to get access to the filter, the setFilter didn't work and filter.text stay at null
const ExempleTable = () => {
const [filter, setFilter] = useState({ text: null });
const columns = [{
dataField: 'text',
text: 'Text',
filter: textFilter({
getFilter: (textFilter) => setFilter({ text: textFilter }),
})
}];
const setTextFilter = (e) => filter.text && filter.text(e.currentTarget.value);
return (
<>
<input onChange={setTextFilter} />
<BootstrapTable
filter={filterFactory()}
data={[{ text: "Je suis un test"}]}
columns={columns}
/>
</>
);
}
Here filter.text is always at null even after the setFilter. Is it possible to do it like that and to make it work ? Is there any workaround to make a programmatically filter inside a function component ?
As far as I know, getFilter is intended to gain access to the filter implemented by react-bootstrap-table-2, and not to overwrite it. So, if you do something like this:
const columns = [{
dataField: 'text',
text: 'Text',
filter: textFilter({
getFilter: (textFilter) => { this.setFilter = textFilter; },
})
}];
Then, you can later use this setFilter function to programmatically set the column filter by invoking the function like this:
invokeFilterFunction = (e) => {
this.setFilter(e.currentTarget.value);
}
return (
<>
<input onChange={invokeFilterFunction} />
<BootstrapTable
filter={filterFactory()}
data={[{ text: "Je suis un test"}]}
columns={columns}
/>
</>
);
If you want to overwrite the column filter function, I think you should use onFilter instead. According to the Storybook, you can define your own filter function, like this:
myOwnFirstFilter = (filterVal, data) => {
if (filterVal) {
return data.filter(text => text && text.indexOf(filterVal) !== -1);
}
return data;
}
And then, set the filter in the column like this:
const columns = [{
dataField: 'text',
text: 'Text',
filter: textFilter({
onFilter: this.myOwnFirstFilter,
})
}];

AsyncSelect: how to set an initial value?

I am using AsyncSelect and need to change the value of the component based on outside logic.
For instance, I have this simple component:
import { colourOptions } from '../data';
const filterColors = (inputValue: string) => {
return colourOptions.filter(i =>
i.label.toLowerCase().includes(inputValue.toLowerCase())
);
};
const promiseOptions = inputValue =>
new Promise(resolve => {
setTimeout(() => {
resolve(filterColors(inputValue));
}, 1000);
});
export default class WithPromises extends Component {
render() {
return (
<AsyncSelect cacheOptions defaultOptions loadOptions={promiseOptions} />
);
}
}
Is there a way to set the initial value of it? I thought of using props but I couldn't figure out how execute onChange event as this would load the options array and set label and value.
Try adding a value like this:
<AsyncSelect
cacheOptions
defaultOptions
value={{ label: 'yellow', value: 2 }}
loadOptions={promiseOptions} />
it worked for me.
When changing the value from the menu options and selected item not change or changed happened based on outside logic then just use the state in value={yourState} to set the initial & updated values of AsyncSelect. Selected contain object like value={value:'', label:''}
const formFields = {
name: '',
foo:'',
bar:''
}
const [inputs, setInputs] = useState(formFields);
inputs is my state which i used to set the fields values
value={inputs.name ? { label: inputs.name } : { label: 'Search customer...' }}
I used only label:'Search customer...' to make it look like place holder.
<AsyncSelect
defaultOptions
value={inputs.name ? { label: inputs.name } : { label: 'Search customer...' }}
cacheOptions
onChange={(e) => handleCustomerSelect(e)}
loadOptions={customerPromiseOptions}
/>
here is my onChange which is called when item is select from dropdown menu
function handleCustomerSelect(selectedOption) {
const { value } = selectedOption;
setInputs({ ...inputs, name:value });
}
You can set your initial value from options as well like this