react table - passing row cell value to a function - react-table

I would like to call function and pass json element id value (column ID) to a function onBookAppointment().
I receive error message, id is not defined. I understand error message, but i do not know how to define it.
Data is coming from async function getData().
I would appreciate some guidance here.
PS! Learning react :)
const columns = React.useMemo(
() => [
{
Header: "ID",
accessor: "id", // accessor is the "key" in the data
},
{
Header: "Reserve",
accessor: "reserveButton",
Cell: row => (
<div>
<Button onClick={() => onBookAppointment(id)} variant="dark">{"product.add-appointment"}</Button>
</div>
),
},
],
[]
);

Related

React table - Dropdown filter

I have implemented Global Filer using setGlobalFilter
But I want to Set filter column wise like:
Outside the table, I need a dropdown with some values which should filter the react-table based on the opted value from the dropdown.
this dropdown filter should filter out the whole table based on the opted value from the dropdown.
Can anyone give me link of demo or any help regarding this will be appreciated.
Ok, let's do it on react-table v8, yes, it is the new version of react-table.
First, make sure you import the required items from #tanstack/react-table
import {
createColumnHelper,
flexRender,
getCoreRowModel,
getFilteredRowModel,
useReactTable,
} from "#tanstack/react-table";
Here, we use simple data.
const defaultData = [
{
firstName: "tanner",
lastName: "linsley",
age: 24,
visits: 100,
status: "In Relationship",
progress: 50,
},
{
firstName: "tandy",
lastName: "miller",
age: 40,
visits: 40,
status: "Single",
progress: 80,
},
{
firstName: "joe",
lastName: "dirte",
age: 45,
visits: 20,
status: "Complicated",
progress: 10,
},
];
In the new version of react-table, we do not have to memoize the columns. We can define it with the help of columnHelper. You can read the details here.
const columnHelper = createColumnHelper();
const columns = [
columnHelper.accessor("firstName", {
header: "First Name",
}),
columnHelper.accessor("lastName", {
header: "Last Name",
}),
columnHelper.accessor("age", {
header: "Age",
}),
columnHelper.accessor("visits", {
header: "Visits",
}),
columnHelper.accessor("status", {
header: "Status",
}),
columnHelper.accessor("progress", {
header: "Profile Progress",
}),
];
Next, we define required states at our component.
const [data] = useState(() => [...defaultData]);
// to keep the selected column field
const [field, setField] = useState();
// to keep the input search value
const [searchValue, setSearchValue] = useState("");
// required by react-table for filtering purposes
const [columnFilters, setColumnFilters] = useState();
In the previous version, we use useTable hooks to create our table instance, here, in the new version, we use useReactTable instead. We pass these configurations to make our filter run correctly.
const table = useReactTable({
data,
columns,
enableFilters: true,
enableColumnFilters: true,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
state: {
columnFilters,
},
onColumnFiltersChange: setColumnFilters,
});
Next, we create our select option tag where we bind the select option value to field state and the onChange event handler to handleSelectChange. For the input tag, we bind the value to searchValue and and the onChange event handler to handleInputChange method.
Inside the select change handler, we need to reset both columnFilters and searchValue states.
...
const handleSelectChange = (e) => {
setColumnFilters([]);
setSearchValue("");
setField(e.target.value);
};
const handleInputChange = (e) => {
setSearchValue(e.target.value);
};
return (
...
<select value={field} onChange={handleSelectChange}>
<option value="">Select Field</option>
{table.getAllLeafColumns().map((column, index) => {
return (
<option value={column.id} key={index}>
{column.columnDef.header}
</option>
);
})}
</select>
<input
value={searchValue}
onChange={handleInputChange}
className="p-2 font-lg shadow border border-block"
placeholder={
field ? `Search ${field} column...` : "Please select a field"
}
/>
...
)
Here, we got the select options list from table.getAllLeafColumns().
Since columns age, visits, and progress value is number, we need to modify our columns configurations by using custom filterFn options.
const columns = [
...
columnHelper.accessor("age", {
header: "Age",
filterFn: (row, _columnId, value) => {
return row.original.age === parseInt(value);
},
}),
columnHelper.accessor("visits", {
header: "Visits",
filterFn: (row, _columnId, value) => {
return row.original.visits === parseInt(value);
},
}),
...
columnHelper.accessor("progress", {
header: "Profile Progress",
filterFn: (row, _columnId, value) => {
return row.original.progress === parseInt(value);
},
}),
];
And as the documentation said that we need to remember this:
Every filter function receives:
The row to filter
The columnId to use to retrieve the row's value
The filter value
and should return true if the row should be included in the filtered rows, and false if it should be removed.
Finally, it is time to render the table:
return (
<div className="p-2">
...
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
And here is the working code:
I hope it helps.

Why doesn't my fetch POST function work in Svelte?

I have a Svelte application that is supposed to perform CRUD operations with a local JSON file via the Fetch API.
The "GET" operations works as intended but when I tried to create the "POST" function, I got the below error message:
Uncaught (in promise) Error: {#each} only iterates over array-like objects.
at validate_each_argument (index.mjs:1977)
at Object.update [as p] (index.svelte? [sm]:33)
at update (index.mjs:1057)
at flush (index.mjs:1025)
Below is the code in index.svelte:
<script>
import { onMount } from 'svelte';
let data1 = '';
onMount(async function () {
const data = await (await fetch('http://localhost:5000/data1')).json();
data1 = data;
console.log(data);
});
const createData1 = async () => {
const data =
(await fetch('http://localhost:5000/data1'),
{
method: 'Post',
body: JSON.stringify({
id: data1.length + 1,
text: '',
}),
headers: {
'Content-Type': 'application/json'
}
});
data1 = data;
console.log(data);
};
</script>
<div style="display: grid; place-items:center;">
<div class="horizontal">
{#each data1 as d1}
<div contenteditable="true">{d1.text}</div>
{/each}
<button type="submit" on:click|preventDefault={createData1}>+</button>
</div>
</div>
And below is the contents of the JSON file:
{
"data1": [
{
"id": 1,
"text": "blabla",
},
{
"id": 2,
"text": "bla bla",
}
]
}
Why isn't the the object being created? It is inside an array after all.
As your log shows, data is an object, not an array. data.captions is the array you want to iterate over.
So you'll want to slightly modify your createData method near the end:
const createData1 = async () => {
...
// data1 = data;
data1 = data.data1;
console.log(data);
};
It looks to me like you need to adjust your code syntax a bit and make sure you convert your response to JSON and you will be in business. This should work:
const data = (await fetch('http://localhost:5000/data1', {
method: "POST",
body: JSON.stringify({
id: data1.length + 1,
text: ''
}),
headers: {
"content-type": "application/json"
}
})).json();
data1 = data;
The notable adjustments are:
Use the fetch overload that takes first argument as URL and the second argument as the request options object (Looks like this is what you were after and an extra "(" character was throwing it of)
Make sure to convert the result of the POST call to JSON. Your "GET" request does this conversion and you get what you are expecting.

Vue 3: Cannot read property 'id' of null

I want to show list product from api but it shows the error:
Uncaught (in promise) TypeError: Cannot read property 'id' of null
at eval (Home.vue?bb51:103)
at renderList (runtime-core.esm-bundler.js?5c40:6635)
at Proxy.render (Home.vue?bb51:2)
at renderComponentRoot (runtime-core.esm-bundler.js?5c40:1166)
at componentEffect (runtime-core.esm-bundler.js?5c40:5265)......
my product like :
[
{
"id": 1,
"name": "chair",
"categoryId": 12,
"unitId": 2,
"price": 66000000,
"salePrice": 0,
"material": "wood",
"size": "x"
},
]
My code here:
Home.vue file
<ProductCard v-for="product in products" :key="product.id" :product="product" />
ProductCard.vue file
<script>
export default {
name: "ProductCard",
props: {
product: {
type: Object,
required: true,
},
},
};
</script>
ProductService.js file
const apiClient = axios.create({
baseURL: 'http://localhost:8888/api/v1',
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
export default {
getProducts() {
return apiClient.get('/product/get-list-product-by-subcategory')
},
}
When I print out list product in console. It still work.
Does anyone know where is the bug in my code?
Updated:
I try to fix my bug "Cannot read property 'id' of null", Steve's answer although remove my red warning in devtool but not deal my data: my data still not showing up. And I find out my code work by using this.products = response.data.data
ProductService.getProducts()
.then((response) => (this.products = response.data.data))
.catch((error) => console.log("error: " + error));
Explain by myself is:
When console.log(this.products = response)
And I need to use this.products = response.data.data to enter to array
apiClient.get(...)
returns a promise not the actual data from the API call.
You need to add a then. like so
apiClient.get(...).then(response => (this.products = response))
Then when the apiClient.get completes this.products will be populated with the data from the API.
Try this
<ProductCard v-for="product in products" :key="product._id" :product="product" />

How to generate filtered list using reselect redux based on static filtered values?

I am fetching news data from an API, in the app I need to show 3 lists. today news, yesterday news, article news.
I think I should use redux reselect. However, all the examples I am visiting has a dynamic filter value (state filter) while I need data to be fileted statically (no state changes these filters)
my state at the moment is
{news : [] }
How can I generate something like below using reselect
{news: [], todayNews:[], yesterdayNews:[], articleNews: []}
should I use reselect or I should just filter inside a component? I think reselect is memorized so I prefer to use reselect for performance
You can do something like the following:
const { createSelector } = Reselect;
const state = {
news: [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 3, name: 'three' },
],
};
const selectNews = (state) => state.news;
const selectOdds = createSelector(selectNews, (news) =>
news.filter(({ id }) => id % 2 !== 0)
);
const selectEvens = createSelector(selectNews, (news) =>
news.filter(({ id }) => id % 2 === 0)
);
const selectFilteredNews = createSelector(
selectNews,
selectEvens,
selectOdds,
(news, even, odd) => ({ news, even, odd })
);
const news = selectFilteredNews(state);
console.log('news:', JSON.stringify(news, undefined, 2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<div id="root"></div>
You use selectors when you need to calculate values based on state such as the total of a list or filtered things from a list. This way you don't need to duplicate the data in your state.

dgrid JsonRest store not working

I have the following:
require([
"dojo/dom",
"dojo/on",
"dojo/store/Observable",
"dojo/store/JsonRest",
"dojo/store/Memory",
"dgrid/OnDemandGrid"
], function (dom, on, Observable, JsonRest, Memory, OnDemandGrid) {
var store = new JsonRest({
target: 'client/list',
idProperty: 'id'
});
var grid = new OnDemandGrid({
columns: {
"id": "ID",
"number": "Name",
"description": "Description"
},
sort: "lastName",
store: store
}, "grid");
});
client/list is a rest url returning a json object {data:[...]}, but the content of the list never shows up :/
I think the problem is caused by the async data loading, because with a json hard coded object the content show up
EDIT :
I've succeeded in achieving this by using a dojo/request, but the JsonRest shouldn't normally act the same way ? Can someone point me to the right direction ?
require([
'dojo/dom',
'dojo/on',
'dojo/store/Memory',
'dojo/request',
'dgrid/OnDemandGrid'
], function (dom, on, Memory, request, OnDemandGrid) {
request('client/list', {
handleAs: 'json'
}).then(function (response) {
// Once the response is received, build an in-memory store with the data
var store = new Memory({ data: response });
// Create an instance of OnDemandGrid referencing the store
var grid = new OnDemandGrid({
store: store,
sort: 'id', // Initialize sort on id, ascending
columns: {
'id': 'ID',
'number': 'Name',
'description': 'Description'
}
}, 'grid');
console.log(store);
on(dom.byId('queryForm'), 'input', function (event) {
event.preventDefault();
grid.set('query', {
// Pass a RegExp to Memory's SimpleQueryEngine
// Note: this code does not go out of its way to escape
// characters that have special meaning in RegExps
description: new RegExp(this.elements.last.value, 'i')
});
});
on(dom.byId('queryForm'), 'reset', function () {
// Reset the query when the form is reset
grid.set('query', {});
});
});
});
Ok problem found :/
My "client/list" url was returning a json object like this:
{data: [{id:"1", label: "test"}, {id:"2", label: "test"}]}
Turns out that the JsonRest object is already encapsulating data in a data node, so by returning a json like this:
{[{id:"1", label: "test"}, {id:"2", label: "test"}]}
everything worked fine :)

Resources