Last row is missing in exported CSV when using react-csv - reactjs

I am exporting an array of objects to react-csv from a table in my UI but the last row is missing in the downloaded CSV file.
I select some rows in the table which contain the records as shown below (there are 3 selected in this example):
I confirmed that the data is attached in the CSVLink tag data property
I confirmed that the data is there via a console.log before I click the export button in the UI.
But the resulting CSV download is missing the last selected item. This happens no matter how many items are selected. The last one is always missing even though the array is correctly built and attached to the CSVLink data property.
Am I missing something here? How can I fix this issue? I cannot find any similar issues on the interweb.

The react-csv package has several open issues with download filenames and
updating state correctly. This resulted in the last item that was updated in my state to not be registered when the download was initiated. You can see one of the issues here (there are more that are specific to state with downloads): https://github.com/react-csv/react-csv/pull/137
I couldn't find any solution using the package so I abandoned it and wrote my own version that fully works. You can find it as a Gist here if anyone needs it: https://gist.github.com/cburkhardtBB/14ba0e3a37c2cef83eaea79e8c93092d
Here is the example code just in case someone runs into a similar issue with either a missing row using CSVLink or not being able to get the CSVDownload filename to work.
import { zipObject } from 'lodash';
export const CSVExport = csvData => {
const { items, headers, filename } = csvData;
// use headers if available, if not, use the key names for headers
// create same type with zipped object
const headerKeys = Object.keys(items[0]);
const rowHeader = headers || zipObject(headerKeys, headerKeys);
const rows = [rowHeader, ...items];
const csv = convertRowsToCSV(rows);
const csvFileName = filename + '.csv' || 'export.csv';
initiateDownload(csv, csvFileName);
};
const convertRowsToCSV = rowData => {
return rowData.map(data => Object.values(data).join(',')).join('\r\n');
};
// same solution others use for downloading
const initiateDownload = (csvData, csvFileName) => {
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, csvFileName); // IE 10+
} else {
const link = document.createElement('a');
if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', csvFileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
// example headers object
const headers = {
serviceStartDate: 'Service Start Date',
serviceStartTime: 'Service Start Time',
serviceEndTime: 'Service End Time',
client: 'Client',
service: 'Service',
quantity: 'Quantity',
}
// example items array
const items= [
{
"serviceStartDate": "08 Feb 2020",
"serviceStartTime": "6:30 PM",
"serviceEndTime": "7:30 PM",
"client": "TestClient1",
"service": "Programming",
"quantity": 1,
},
{
"serviceStartDate": "08 Feb 2020",
"serviceStartTime": "9:30 PM",
"serviceEndTime": "11 PM",
"client": "TestClient2",
"service": "Consultation",
"quantity": 1.5,
},
{
"serviceStartDate": "09 Feb 2020",
"serviceStartTime": "1:30 AM",
"serviceEndTime": "2 AM",
"client": "TestClient3",
"service": "Refactoring",
"quantity": 0.5,
}
]
// example filename (need to import moment.js to do below)
// like this => import moment from 'moment';
const csvDownloadDate = moment(new Date()).format('DD-MMM-YYYY'); // today
const filename = `${csvDownloadDate}-Services`;
// package up all data into object
const csvData = {
items,
headers,
filename,
}
// also works with no headers now
// const csvData = {
// items,
// filename,
// }
// call function from another file after importing CSVExport
CSVExport(csvData);

Related

Populate dropdown on React from JSON Data WebAPI

I have a react form that has several dropdowns and it was working, however, the WebAPI changed and now the data is being returned slightly different and now none of the dropdowns are populated.
The old JSON format was like:
{
"data":
[{
"id" : 1,
"name" : "Michelle Smith",
},
{
"id" : 2,
"name" : "Jenn Arnold"
}
]
}
the drop down binding is:
const [ admins, setAdmin] = useState([]);
useEffect(() => {
getAdmins();
},[]);
//calls a JS file that connects to the API using axios
const getAdmins = () => {
adminGroups.GetAllAdmins()
.then((response) => {
setAdmins(response.data.data)
}
}
return (
<select>
<option>...</option>
{admins.map(data => {
<option
value={admin.id}
>
{admins.name}
<option>
</select>
)
The new JSON format is:
[{
"id" : 1,
"name" : "Michelle Smith",
},
{
"id" : 2,
"name" : "Jenn Arnold"
}]
There is no parent "data" tag in the new format, what would've caused the drop downs to stop binding with the new format? When the page loads, I can see the API being called (under the network tab) and if I go to the URL I can see data, just not in the React App. Is there another way I should be binding the dropdown?
[I'm fairly new to React and converting an Access app over to the web using React as the UI]
If all that's changed is that the API you're using no longer wraps the array in data then just don't go looking for it in your response.data.data use,
Instead just use response.data like so
const getAdmins = () => {
adminGroups.GetAllAdmins()
.then((response) => {
setAdmins(response.data)
}
}
I got this working, just by shutting down VS and opening it back up again. Not sure if VS had something cached or not, but the API was returning data, just not the UI until I restarted it. Thanks all

Create an Electron/React Local Application which can open a local JSON FIle - Edit it and export it

I'm new in React and trying to create an Application which can open a local JSON File with a list of elements like this in a dialouge:
{
"autor": "Name",
"selftext": "Text",
"title": "Title",
"date": "2019-11-18 18:36:40",
"content": "Content",
"link": "www.web.com",
"origin": "web.com",
"filename": "XY",
"id": 1
},
The imported JSON should be expandable and editable and after that export it with the changes.
Here I need some help with guidance where to start and what I need for that.
To read the file on the frontend, you will need to:
Construct a <form onSubmit={handleSubmit}> with an <input name='json' type='file' required>
Then create a callback, handleSubmit, that extracts and parses the chosen file:
function handleSubmit(e: Event) {
e.preventDefault(); // Prevents the form from refreshing the page
const formData = new FormData(e.target); // parses the data into a map
const file = formdata.get("json");
const filereader = new FileReader();
filereader.readAsText(file);
filereader.onload = () => handleResult(filereader.result);
}
function handleResult(result: string|ArrayBuffer|null) {
if (result) {
// Handle the file as you would like here. You will likely need to
// parse the file here w/ JSON.parse. You could cache it
// or pass it to the React.Context API so that it is available through
// the application.
}
}
To write/export the file on the frontend, you can push the JSON object into a Blob and temporarily construct an <a> element to initiate the download:
function save() {
const json = // retrieve JSON from wherever you stored it
const file = new Blob([json], {endings: 'native'});
const a = document.createElement('a');
a.download='config.cfg'
a.href = URL.createObjectURL(file);
a.click();
}
Granted because the process might be slightly different because of Electron, but that is how it would work with React in the Browser.

Expo MediaLibrary.getAssetsAsync({album:album}) is not returning album - React Native

I am using Expo MediaLibrary to get videos from a specific folder and I am facing this problem.
MediaLibrary.getAssetsAsync() shows there are no media inside an album. while there are media inside the folder.
Code:
let album = await MediaLibrary.getAlbumAsync("GrabTube")
console.log(album )
let assets = await MediaLibrary.getAssetsAsync({album:album })
console.log(assets)
response:
Object {
"assetCount": 1,
"id": "876214992",
"title": "GrabTube",
}
Object {
"assets": Array [],
"endCursor": "0",
"hasNextPage": false,
"totalCount": 0,
}
In the first console.log, it shows there is one item/asset inside the folder. But as shown inside the second console.log it shows the folder is empty.
Is there any fix for this?
For some reason it's necessary to define the mediaType even when you have already defined the album...
to me the solution was change from :
let assets = await MediaLibrary.getAssetsAsync({album:album })
to:
let assets = await MediaLibrary.getAssetsAsync({album:album, mediaType:'audio' })

Get the image from the server

I uploaded an image file using axios post method and it was successfully uploaded to the server...
On return to the POST method, it returned the Image URL as:
{imageUrl: "/root/TTAppJava/images/players/Screenshot from 2020-11-24 16-38-57.png"}
Now, I want to display the image onto my screen.
How can I get that image?
I am using React.js to implement this.
The URL I used to post the image is:
http://139.59.16.180:8269/player/details/${id}
I am doing this to upload my data:
var formData = new FormData()
formData.append("file", image);
const theWrapper = {
"data": {
"name": name,
"age": age,
"email": email,
"gender": gender
}
}
formData.append("theWrapper", JSON.stringify(theWrapper))
Axios.post("http://139.59.16.180:8269/player/add",
formData,
{ headers: { Authorization: "Bearer " + localStorage.getItem("token") } }
).then(res => {
console.log(res)
alert("Player added successfully.")
history.push("/players")
})
.catch(err => alert(err.messge))
And the response I am getting is:
{id: 14, name: "shreyas", email: "asdfjka#gmail.com", gender: "Male", imageUrl: "/root/TTAppJava/images/players/Screenshot from 2020-11-24 16-38-57.png", …}
I will give you an example how its done in Node app, since the underlying concept will be the same as I am not a Java developer.
First please note your image_url/download_url should be saved as follows,
https://yourdomain/folder/image_name
example: http://localhost:3000/uploads_folder/my_image_name.jpg
and then you need a route in Java Spring which figures out what image to send to the front-end as Stream like below,
router.get("/:folder/:image_name", async (req, res) => {
const { folder, image_name } = req.params;
// find an image based on the downloadUrl in the folder where files are saved.
// Please note, after uploading file successfully generate a download url
// appropriately so that this route can help figure out which image you
// are looking for.
const file = path.join(`path/to/${folder}`, `${image_name}`);
// Figure out a way how stream works in your context.
// Providing MIME type is important!
const type = mime[path.extname(file).slice(1)] || "image/jpg";
const s = fs.createReadStream(file);
s.on("open", () => {
res.set("Content-Type", type);
s.pipe(res);
});
s.on("error", (err) => {
// Handle Error
});
});

How to render Draft-js text in React from server

I using Draft-js to build my text-editor.
What I want to do is on clicking the submit button, the submitted text should appear on right side (as you can see, the blank side)
I'm calling a handleSubmit function to post the content to server:
handleSubmit = e => {
e.preventDefault();
const query = JSON.stringify(convertToRaw(this.state.editorState.getCurrentContent()));
const payload = {
query:query
}
axios({
url:'http://localhost:9000/queries',
method:'POST',
data: payload
})
}
and to retrieve the content :
getQueries(){
axios.get("http://localhost:9000/queries")
.then(response => {
const data = response.data;
this.setState({queries:data});
})
}
I'm bit confused, on how to use convertFromRaw to convert the content to the desired text.
Where should I call the method ?
Thanks in advance
You have to understand that DraftJS is a library that converts whatever you have inside the editor into it own format.
For Example, apple is converted to,
{
"blocks": [{
"key": "f6b3g",
"text": "apple",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [{
"offset": 0,
"length": 5,
"style": "ITALIC"
}],
"entityRanges": [],
"data": {}
}],
"entityMap": {}
}
Yep, it's that big. So, in order to convert it back from this format to apple you need a component which knows how to interpret it. You cannot just use a div tag. In this case, it's the editor component itself!
So you can do the following to display the content in readonly mode.
<Editor readOnly={true}
editorState={EditorState.createWithContent(this.state.editorState.getCurrentContent())}/>
Now, say if you want to send the editor contents to an API so that you can save it in DB and then get the content from the API and display it.
Convert from Editor Format to JSON
const requestBody = JSON.stringify(convertToRaw(this.state.editorState.getCurrentContent()))
Post JSON to your API
fetch(url, {
method: 'POST',
body: requestBody
});
Get JSON from your API
const response = await fetch(url, {
method: 'GET'
});
Convert from JSON to Editor format and display using Editor (You can remove readonly if you want the user to edit the contents)
<Editor readOnly={true}
editorState={EditorState.createWithContent(convertFromRaw(JSON.parse(response))}/>

Resources