I developed a table with reactjs and I want to get the data and appears it on this table.
The data appears on the console but it doesn't appears on the table.
My code is :
constructor(props){
super(props);
this.state = {
categories: [],
file:''};
this.onchange = this.onchange.bind(this)
this.handleAdd = this.handleAdd.bind(this);
}
componentDidMount() {
axios({
method: 'get',
url: 'http://172.16.234.24:8000/api/allCategories',
headers: {
'Cors-Allow-Credentials':true,
'Access-Control-Allow-Credentials':true,
'Access-Control-Allow-Origin': '172.16.234.24:8000',
'Access-Control-Allow-Headers':'Content-Type,
Authorisation',
'Content-Type': 'multipart/form-data'
}
}).then(response => {
try { if (response && response.data) {
this.setState({ categories: response.data});
console.log(response)
}
console.log(this.state.categories)
}
catch(error)
{console.log('noo')}
}).catch(error => console.log(error));
}
handleAdd() {
try {
console.log("Add Categorie")
this.props.history.push('/addcategorie');
}
catch (error) {
this.setState({ error });
}
}
render() {
let {
categories
} = this.state;
return (
<div>
<Table><thead><tr>
<th scope="col">Name</th>
<th scope="col">Photo</th>
</tr>
</thead>
<tbody>{categories.length && this.state.categories.map(categorie => (
<tr key={categorie.id}>
<td>{categorie.name}</td>
<td>{categorie.file}</td>
</tr>
))} </tbody>
</Table>
</div>
when I run it I get index.js:1446 Warning: validateDOMNesting(...): Text nodes cannot appear as a child of <tbody>. and I get the data on the console but my table is empty.
How to fix it ?
You have some white spaces between tbody starting and ending tag
Just replace below code as it is and try
<tbody>{categories.length && categories.map(categorie => (
<tr key={categorie.id}>
<td>{categorie.name}</td>
<td>{categorie.file}</td>
</tr>
))}</tbody>
It might seem silly, but you're actually rendering the tbody and some whitespace (i.e. text).
To be more clear
Change
))} </tbody>
To
))}</tbody>
try to change your JSX expression (i added {}):
{categories.length && this.state.categories.map(categorie => { return (
<tr key={categorie.id}>
<td>{categorie.name}</td>
<td>{categorie.file}</td>
</tr>
))}}
You need to move your code logic from the tbody to the tr for iteration or remove the tbody tag.
Related
In my code I am trying to do when I select row and click on button then this url open http://codeskulptor-assets.commondatastorage.googleapis.com/assets_clock_minute_arrow.png.
But right now in my code when I select row and click on button then url is not open.
How can we do that to open this url when I select row and click button the open url http://codeskulptor-assets.commondatastorage.googleapis.com/assets_clock_minute_arrow.png.
My code here https://codesandbox.io/embed/react-example-forked-o8tu5?codemirror=1
Anyone plz suggest any idea and help me out. I m stuck on that.
import React from 'react';
import axios from 'axios';
class ProvFileRptSearchResult extends React.Component {
constructor(props) {
super();
this.state = {
pymtDetails:[],
data: [],
rowDetails:[],
checkbox: false
};
// this.handleFile=this.handleFile.bind(this);
this.handleClick=this.handleClick.bind(this);
}
handleClick() {
const apiUrl = "http://localhost:9090/PrvFileRpt/getPrvFileData";
if (this.state.checkbox) {
fetch(apiUrl)
.then((response) => response.json())
.then((data) => {
this.setState({ data: data });
console.log("This is your data", data);
window.open("https://example.com", "_blank");
})
} else {
alert("Data not fetched!");
}
// console.log('click');
}
// handleClick(e) {
// e.preventDefault();
// console.log("The link was clicked");
// }
// handleFile()
// {
// //fetch birt report from here
// console.log("inside file ",this.state.rowDetails);
// }
rowSelected(j) {
// e.preventDefault();
console.log(j)
const rownum=j;
console.log("rownum=",rownum)
console.log(this.props.customerDetails[rownum] )
this.setState({rowDetails:this.props.customerDetails[rownum]}, () => {
});
}
render()
{
return(
<div>
<div className="table-employee" style={{ marginTop:"20px",border:" 1.5px solid darkgray" }}>
<table className="table table-hover table-bordered table-sm">
<thead>
<tr >
<th scope="col">Select</th>
<th scope="col"> LOAD DATE</th>
<th scope="col"> FILE DATE</th>
<th scope="col"> SERVICE</th>
<th scope="col"> PROVISIONER CODE </th>
<th scope="col"> DESCRIPTION</th>
</tr>
</thead>
<tbody>
{
this.props.customerDetails.map((type,j)=>{
return(
<tr>
<td ><input type="radio" preventDefault name="select" key={j} onClick={(e) =>this.rowSelected(j)} value={this.state.checkbox}
onChange={(e) =>
this.setState({ checkbox: !this.state.checkbox })
}/></td>
<td> {type.provis_file_stamp}</td>
<td> {type.provis_file_hdrdt}</td>
<td> {type.service_code}</td>
<td>{type.provisioner_code}</td>
<td>{type.provisioner_desc}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
<div className="btn-submit" >
<button className="btn btn-primary" style={{marginRight:"30px"}} type="submit" onClick={this.handleClick}>FILE</button>
</div>
</div>
)
}
}
export default ProvFileRptSearchResult;
Call the openInNewTab with the URL. It will open the URL in a new browser tab. Remove '_blank', if you want to open it in the same tab.
const openInNewTab = (url) => {
const newWindow = window.open(url, '_blank', 'noopener,noreferrer')
if (newWindow) newWindow.opener = null
}
Well, if I understand correctly, you're trying to open a URL on button click?
If that's right, using window.open('https://example.com', '_blank') in your click handler will allow you to open any URL in a new tab.
First you need to find correct object using find method and then you can open url with window.open
Try following code:-
handleClick = () => {
const apiUrl = "https://mocki.io/v1/b512f8b8-64ab-46e4-9e0c-9db538a0ad9e";
if (this.state.checkbox) {
fetch(apiUrl)
.then((response) => response.json())
.then((data) => {
this.setState({ data: data });
const urlData = data.find(element => element.id === 3); // filter data with id
window.open(urlData.url, '_blank'); // open url here
});
} else {
alert("check the radio!");
}
};
I have the following component where I fetch data from a table, display them and have the ability to perform a search.
I want to add an additional filter to the fetched element. The filter is already coded and working in Laravel as following :
public function search(Request $request){
$search = $request ->get('q');
$Patient = Patient::where('name','Like','%'.$search.'%');
if ($request->has('gender')) {
$Patient->where('gender', $request->gender);
}
return $Patient->get();
}
As you can see in the controller you can perform a search and a filtering. So an example of directly accessing it will be :
http://localhost:8000/api/patients/search?q=John&gender=male
For now I'm able to display data and search it in react, but I have no idea on how to add additional filters to the link to fetch from. How to do so ?
Component :
class Patient extends React.Component {
constructor(props) {
super(props)
this.state = {
patients : [],
};
this.handleSearchChange = this.handleSearchChange.bind(this);
}
handleSearchChange(key){
console.log(key);
fetch('http://localhost:8000/api/patients/search?q='+key)
.then(response => response.json())
.then(response => {
this.setState({
patients: response})
console.log(response);
})
.catch(err => console.log(err));
}
componentDidMount() {
axios.get('api/patients')
.then(response => {
this.setState({
patients: response.data})
})
.catch(err => console.log(err));
}
render() {
return (
<div>
<Container>
<div>
<input
type="text"
className="form-control"
placeholder="Search..."
onChange={e => this.handleSearchChange(e.target.value)}
/>
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown button
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#">Male</a>
<a class="dropdown-item" href="#">Female</a>
</div>
</div>
</div>
<Table>
<Thead>
<Tr>
<Th>ID</Th>
<Th>FIRST NAME</Th>
<Th>LAST NAME</Th>
</Tr>
</Thead>
<Tbody>
{this.state.patients.reverse().map((patient) => (
<Tr>
<Td>
{patient.id}
</Td>
<Td>
{patient.firstname}
</Td>
<Td>
{patient.lastname}
</Td>
</Tr>
))}
</Tbody>
</Table>
</Container>
</div>
);
}
}
export default Patient;
You are quite close to it.
Your current implementation generates the search url as soon as the <input> field changes. If you want some other filters, you will need to store those filters values in a state before actually generating the url and fetching the data.
Here is how the gist would look like.
class Patient extends React.Component {
constructor(props) {
super(props)
this.state = {
patients : [],
filters: {q: '', gender: ''} // we add the filter property to the state
};
this.updateFilter = this.updateFilter.bind(this);
}
// this will fetch the new patients as soon as one of the filter has been updated
fetchPatients() {
const searchParams = new URLSearchParams();
const {q, gender} = this.state.filters
if (q) {
searchParams.set('q', q);
}
if (gender) {
searchParams.set('gender', gender);
}
const url = `http://localhost:8000/api/patients/search?${searchParams.toString()}`;
fetch(url)
.then(response => response.json())
.then(response => this.setState({patients: response}))
.catch(err => console.log(err));
}
// this will update a single filter
updateFilter(filter, value) {
const currentFilters = this.state.filters;
this.setState({filters: {...currentFilters, [filter]: value}});
this.fetchPatients()
}
render() {
return (
/*...*/
<input type="text" onChange={ev => this.updateFilter('q', ev.target.value)} />
/*...*/
<input type="text" onChange={ev => this.updateFilter('gender', ev.target.value)} />
);
}
}
check this example from pingcrm (build with laravel, inertiajs and vue), specifically when use lodash/pickBy conversion to replace a request URL
form: {
handler: throttle(function() {
let query = pickBy(this.form)
this.$inertia.replace(this.route('contacts', Object.keys(query).length ? query : { remember: 'forget' }))
}, 150),
deep: true,
},
Hope help.
EDIT: heres the console.log(data) the data is displaying fine, it's just not filtering the data properly..]
!https://imgur.com/a/SsEDAKj!
EDIT 2: this.state.items is an array.
I'm trying to implement a search function, that allows the user to search through the data brought back from the API. I keep getting the following error:
"TypeError: data.filter is not a function"
constructor(){
super();
this.state ={
items: [],
sessions: [],
loading: true,
search: '',
direction: 'asc',
filteredPosts: [],
query: ''
}
this.onSort = this.onSort.bind(this);
this.searchTerm = this.searchTerm.bind(this);
//this.filteredPosts = this.filteredPosts.bind(this);
}
searchTerm =(event) =>{
const query = event.target.value;
this.setState({query}, () => this.filterData());
}
filterData(){
let data = this.state.items;
let src = this.state.query;
data = data.filter(function(data){
return data.indexOf(src) != -1;
});
this.setState({filteredPosts: data});
console.log(this.state.filteredPosts);
}
async getTalks(){
const response = await fetch ('PRIVATE_API');
const data = await response.json();
//console.log(data);
this.setState({items: data});
}
async componentDidMount(){
this.getTalks();
}
render(){
return (
<div className="container-fluid m-0">
<div className="row h-100">
<div className="col-12 ml-0"><h2>Conference Talks</h2>
<p>You can search the talks via title, speaker or tags</p>
<input className="form-control mb-2" id="search" type="text" onChange={this.searchTerm} placeholder="search"></input></div>
<table className="table table-hover table-dark">
<thead>
</thead>
<tbody id ="list">
{this.state.items.map(function(item,index){
return(
<tr key = {index}>
<th data-title="id"scope="row">{item.id}</th>
<td data-title="title" style={{cursor: "pointer"}} title data-original-title={"Click to sort"} data-toggle="title" >{item.title}</td>
<td data-title="description" id="desc">{item.description}</td>
<td data-title ="speaker">{item.speaker}</td>
<td >{item.session}</td>
<td >{item.tags}</td>
<td >{item.time}</td>
<td >{avg}</td>
I can't figure out the right direction to go in when trying to filter through the data that is pulled. What mistakes am I making? ( I have only included relevent code)
Have you run a check to see if your data is there? It's possible the items are not available on first render, causing this problem. Maybe something like
{this.state.items.length > 0 && this.state.items.map(function(item,index){
//rest of your code here
Also, I noticed you are bringing in a loading variable. If this is working you could do
if(loading){ return (<LoadingComponent />}else{ //regular return statement
The issue was I wasn't returning the object attribute.
data = data.filter(function(data){
return **data.title**.toLowerCase().indexOf(src) !== -1;
});
I am getting "Uncaught TypeError: this.state.data.map is not a function" error even I can successfully log data to console from the API.
I have found similar questions but, haven't come up with a good solution to solve this issue yet.
I have read here that, "Objects, {}, in JavaScript does not have the method .map(), it's only for Arrays, []."
However, I can not figure out how to fix this issue, iterate over an object and retrieve data to the React front end too.
Thank you and any help would be greatly appreciated.
import React, { Component } from "react";
import axios from "axios";
export default class GetSubjects extends Component {
constructor(props) {
super(props);
this.getsubjects = this.getsubjects.bind(this);
this.onSearch = this.onSearch.bind(this);
this.state = {
keyword: "",
data: []
};
}
getsubjects(e) {
this.setState({ keyword: e.target.value });
}
onSearch(e) {
e.preventDefault();
const searchsub = {
keyword: this.state.keyword
};
axios
.get(`http://localhost:3000/api/messages/courses/${this.state.keyword}`)
.then(response => {
console.log(response);
this.setState({
data: response.data
});
});
console.log(this.state.keyword);
console.log(this.state.data);
}
componentDidMount() {}
render() {
return (
<div>
<br />
<div>
<label>Course Name</label>{" "}
<input
placeholder="Enter Course Name"
type="text"
value={this.state.keyword}
onChange={this.getsubjects}
name="keyword"
required
/>{" "}
<button className="btn btn-primary" onClick={this.onSearch}>
Get Subjects
</button>
</div>
<br />
<table className="table table-bordered">
<thead>
<tr>
<th scope="col">Course Name</th>
<th scope="col">Subjects</th>
</tr>
</thead>
<tbody>
{this.state.data.map(function(subject) {
return (
<tr>
{" "}
<td key={subject.id}>{subject.name}</td>{" "}
<td key={subject.id}>{subject.subjects}</td>{" "}
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
You've said what you're receiving is:
{
"subjects": ["Computer Architecture", "Basic Networking"],
"_id": "5cf368bfb58f8c35bc19cebc",
"name": "Software Engineering",
"passmark": 75,
"lectureIncharge": "John Smith",
"__v": 0
}
Your setState call sets data to that object, which is not an array.
Your render code expects that to be an array, though. It tries to loop through it and use it to fill in rows in a table. But you're only getting one piece of data: A single course ("Software Engineering") which covers two subjects ("Computer Architecture" and "Basic Networking").
So your render code shouldn't be trying to use that as an array (and so you may not want a table anymore). It should just use data's name and subjects properties directly.
I'll keep your table markup for now, but note that this only produces a single row (because there's only one piece of data). Prior to the return, I'd grab the course:
const course = this.state.data;
then where you're outputting your table:
<tbody>
{course && course.name
? undefined
:
<tr>
<td key={course.id}>{course.name}</td>{" "}
<td key={course.id}>{course.subjects.join(", ")}</td>{" "}
</tr>
}
</tbody>
I've used an explicit join(", ") to join the subjects rather than implicitly via toString, which wouldn't include a space after the comma.
response.data is an object which axios fills when the request is done.
It's true you're initializing data as an array, but when you do:
this.setState({ data: response.data }); You've actually changed it to an object.
Parse the object you get to an array or do something else with the returned data.
Edit: After you response: just do: this.setState({data: response.data.subject});
//yo lo solucione poniendo data.data
//porque (data) solo me devolvĂa un objeto con varios atributos y data era uno de esos
//entonces le agregue data.data.
getCategorias(){
axios.get("http://localhost:8000/api/categorias")
.then(data => {
this.setState({
data: data.data
});
}).catch((error)=>{
console.error(error)
});
}
componentDidMount(){
this.getCategorias();
}
I am dynamically rendering a table from an aws dynamo database. I want to have the newest posts rendered first. I am using the createdAt variable as the key but the table still renders randomly. I'm having trouble figuring out whats wrong and would appreciate the help. Code:
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
sort: {
column: null,
direction: 'desc',
}
};
this.onSort = this.onSort.bind(this);
}
async componentDidMount() {
if (!this.props.isAuthenticated) {
return;
}
try {
const results = await this.notes();
this.setState({ notes: results });
} catch (e) {
alert(e);
}
this.setState({ isLoading: false });
}
notes() {
return invokeApig({ path: "/notez" });
}
handleNoteClick = event => {
event.preventDefault();
this.props.history.push(event.currentTarget.getAttribute("href"));
}
onSort(column) {
return(function(e) {
let direction = this.state.sort.direction;
if (this.state.sort.column === column){
direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc';
}
const sortedData = this.state.notes.sort((a, b) => {
if(column === 'date') {
return a.createdAt - b.createdAt;
}
});
if(direction === 'desc'){
sortedData.reverse();
}
this.setState({
notes: sortedData,
sort: {
column,
direction,
}
});
}).bind(this);
}
renderNotesList(notes) {
var styles = {
progress: {
backgroundColor: "#ffff00",
color: '#000000'
},
completed: {
backgroundColor: "#66CD00",
color: '#000000'
}
}
return (
<div>
<Table responsive hover>
<thead>
<tr>
<th>Title</th>
<th>Employee</th>
<th className="sortable" onClick={this.onSort('date')}>
Created
</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{this.state.notes.map((note) =>
<tr
key={note.createdAt}
href={`/notes/${note.noteId}`}
onClick={this.handleNoteClick}
className="workOrders"
>
<td>{note.title}</td>
<td>{note.employee}</td>
<td>{new Date(note.createdAt).toLocaleString()}</td>
{note.jobStatus === 'In Progress' || note.jobStatus !== 'Completed' ? <td style={styles.progress}>{note.jobStatus}</td> : <td style={styles.completed}>{note.jobStatus}</td>}
</tr>
)}
</tbody>
</Table>
</div>
);
}
The createdAt is stored as integer value in the database:
I thought I'd be able to render the list from newest to oldest since the createdAt date that is most recent is higher in value. Is this possible?
You are not sorting the data on initial render. You only sort them after table header column "Created" is clicked. You can try adding this.onSort('date') after the this.setState({ notes: results }); in your componentDidMount function or better yet, just sort the results before passing it to the state:
const results = await this.notes();
const notes = results.sort((a, b) => {
return a.createdAt - b.createdAt;
});
this.setState({ notes });
Then of course you don't want to forget to change your default sort column in constructor from null to 'date'.