How do I parse an HTML file in React Native? - javascript

How can I get an HTML file from file system and parse specific elements from it.
For example, given the html snippet below, how can I extract the table content and render it?
<html>
<div>
<h1>header</h1>
<table id="a" border="1">
<th>Number</th>
<th>content A</th>
<th>contetn A</th>
<th>content A</th>
<tr>
<td>1</td>
<td>a</td>
<td>a</td>
<td>a</td>
</tr>
<th>Number</th>
<th>content B</th>
<th>content B</th>
<th>content B</th>
<tr>
<td>1</td>
<td>b</td>
<td>b</td>
<td>b</td>
</tr>
</table>
</div>
<br>
<footer>footer</footer>
</html>

Get html with fetch() and parse with react-native-html-parser, process and display with WebView.
import DOMParser from 'react-native-html-parser';
fetch('http://www.google.com').then((response) => {
const html = response.text();
const parser = new DOMParser.DOMParser();
const parsed = parser.parseFromString(html, 'text/html');
parsed.getElementsByAttribute('class', 'b');
});
P.S. fast-html-parser from other answers didn't work for me. I got multiple errors while installing it with react-native 0.54.

Just download HTML with fetch(), parse it using fast-html-parser, write result to state and render that state with WebView

I would recommend using this library: react-native-htmlviewer. It takes html and renders it as native views. You can also customize the way elements get rendered.
// only render the <table> nodes in your html
function renderNode(node, index, siblings, parent, defaultRenderer) {
if (node.name !== 'table'){
return (
<View key={index}>
{defaultRenderer(node.children, parent)}
</View>
)l
}
}
// your html
const htmlContent = `<html></html>`;
class App extends React.Component {
render() {
return (
<HTMLView value={htmlContent} renderNode={renderNode} />
);
}
}

I recommend this package react-native-render-html, is so famous and very simple for using

Related

How do I dynamically generate table rows without jquery?

Currently, I have a table class as follows:
import React from "react";
import "./Table.css";
export default function Table({theadData, tbodyData}) {
return (
<>
<table>
<tr>
<th></th>
<th>2017</th>
</tr>
{Array.from(theadData).forEach(heading => {
<tr>
<td class="largeHeader" key={heading}>{heading}</td>
<td class="primaryCell">{tbodyData[heading].value}</td>
</tr>;
})}
</table>
</>
);
}
When I add console.log(heading) or console.log(tbodyData[heading].value) within the loop, I can see that they give the expected values. However, none of the rows are added on. Why is that and how can I solve this problem? (I'd prefer to avoid jquery and libraries of that nature if possible, but am open to ideas.)
There are several mistakes you made:
change forEach to map
replace {} with (), or add return before <tr>
put key on the root element which is <tr>
{Array.from(theadData).map(heading => (
<tr key={heading}>
<td className="largeHeader">{heading}</td>
<td className="primaryCell">{tbodyData[heading].value}</td>
</tr>
))}

How to display data from Flask in React App?

I'm using Flask as a backend to retrieve data from MySQL database like that:
#app.route('/create', methods=['GET'])
def get_family():
cursor.execute("SELECT * FROM individual")
data = cursor.fetchall()
return render_template('index.html', data=data)
The last line sends the necessary data to the HTML file located in the templates folder and successfully displays my data in the table:
<table>
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Gender</td>
</tr>
{% for item in data %}
<tr>
{% for d in item %}
<td>{{d}}</td>
{% endfor%}
</tr>
{% endfor %}
</table>
However, I want to display this data not in the html template but in my React application. I have a whole separate folder with my React files.
I added a proxy for my Flask API to avoid CORS issues, and allow React to handle the fetch calls and proxy them to right server. But now I am stuck with how to exactly display my data in React. Here is my initial attempt:
function Test() {
const [myData, setMyData] = useState([{}])
useEffect(() => {
fetch('/create').then(
response => response.json()
).then(data => setMyData(data.myData))
}, []);
return (
<div>
<table>
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Gender</td>
</tr>
mapping here?
<tr>
mapping here?
<td>{{myData}}</td>
</tr>
</table>
</div>
);
}
I am unsure how exactly I should map in order to get my data displayed just like I did in that HTML template.
Any help would be appreciated!
You can do it in a very similar way you did on your HTML template using .map
{myData.map((item) => (
<tr>
{item.map((d) => (
<td>{d}</td>
))}
</tr>
))}
That's pretty simple and should be like this:
<table>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
myData.map((item, idx) => (
<tr key={idx}>
<td>{item.firstName}</td>
<td>{item.lastName}</td>
<td>{item.genre}</td>
</tr>
</tbody>
</table>
Or you could also map the td and have 2 map funcs.

Webcomponents based on <table>. Slot out of flow

I'm try create data-grid with "web component" based on TABLE. I'm use slot in TBODY to append new items. But TR rows rendered out of TBODY. I don't understand this behavior.
shadowDom disallow use TABLE and TR elements, possibly the reason is this, but using HTMLElement.appendChild() also doesn't work
<html>
<head>
<script type="module">
class DataGrid extends HTMLElement {
TEMPLATE_ID = '#data-grid';
constructor() {
super();
this._records = [];
}
connectedCallback() {
let shadow = this.attachShadow({mode: 'open'});
this._render(shadow);
}
_render(shadow){
let tmpl = document.querySelector(this.TEMPLATE_ID);
shadow.appendChild(tmpl.content.cloneNode(true));
//this.appendChild(tmpl.content.cloneNode(true)); //
}
}
customElements.define('data-grid', DataGrid);
</script>
</head>
<body>
<template id="data-grid">
<table border="1">
<thead>
<tr>
<th>id</th>
<th>time</th>
<th>voltage</th>
<tr>
</thead>
<tbody>
<slot></slot>
<tbody>
</table>
</template>
<data-grid>
<!-- need replaced to component
<grid-row time="" voltage="">
-->
<tr>
<td>1</td>
<td>123123123120</td>
<td>12.0</td>
</tr>
<tr>
<td>2</td>
<td>123123133324</td>
<td>12.1</td>
</tr>
<tr>
<td>1</td>
<td>123123122330</td>
<td>12.2</td>
</tr>
</data-grid>
</body>
From the <TR> documentation on MDN:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr
Permitted parents
<table> (only if the table has no child <tbody> element, and even
then only after any <caption>, <colgroup>, and <thead>
elements); otherwise, the parent must be <thead>, <tbody> or
<tfoot>
So
<data-grid>
<tr>
is not valid HTML
move (invalid) lightDOM to a <table> inside <data-grid> shadowDOM
note: attachShadow() both sets and returns this.shadowRoot for free
No need to create your own shadow variable.
psuedo code:
const table = this.shadowRoot.querySelector("TABLE");
this.querySelectorAll("TR").forEach(table.appendChild);

Programmatically create HTML table from D3 nested JSON object

I'm trying to make tables for a dashboard using React/D3. One of them is similar to this HTML hard-coded table which I'm trying to make:
table, td{
border: 1px solid black;
}
<table>
<thead>
<td>Location</td>
<td>State</td>
<td># Jobs</td>
</thead>
<tbody>
<tr>
<td rowspan="2">Cell 1 spanning if we have multiple values for this key</td>
<td>SUCCESSFUL</td>
<td>75</td>
</tr>
<tr>
<td>FAILED</td>
<td>22</td>
</tr>
</tbody>
</table>
<br />
Data
The data is retrieved from a SQL-Server database through node-mssql and express and then stored in react state, I pull 7 days worth of data and filter this down to 1 day per component in my react app as i need it.
It returns as normal and has NO nesting in it e.g.
{
"location": "Ireland",
"state": "Finished",
"finish_time": "2018-10-18T12:00:00",
"seconds_passed": 30
}
D3
With D3 I aggregated this data so that, it would be aggregated by location, then by state and then a count of the rows in that aggregation. I achieved this with nest and rollup:
dailyJobStatusCounts = () => {
return (
nest()
.key( row => row.location)
.key( row => row.state)
.rollup(function(values) { return values.length; })
.entries(this._filterTwentyFourHours())
);
}
This returns an object that looks like this:
[{"key":"Ireland","values":[{"key":"SUCCEEDED","value":14},{"key":"FAILED","value":7}]}]
Rendering to HTML in React
My issue occurs around my lack of understanding of how to render this nested JSON object as a HTML table. When attempting to render, I have tried two variations:
React
<table>
<thead>
<tr>
<th>Location</th>
<th>State</th>
<th># Jobs</th>
</tr>
</thead>
<tbody>
{data.map(row => {
return <tr>
<td>{row.key}</td>
{ row.values.map( v => {
return <React.Fragment>
<td>{v.key}</td>
<td>{v.value}</td>
</React.Fragment>
})}
</tr>
})}
</tbody>
</table>
Which renders the table like this:
HTML Output
table, td {
border: 1px solid black;
}
<table>
<thead>
<tr>
<th>Location</th>
<th>State</th>
<th>Jobs</th>
</tr>
</thead>
<tbody>
<tr>
<td>ie11</td>
<td>SUCCEEDED</td>
<td>14</td>
<td>FAILED</td>
<td>7</td>
</tr>
</tbody>
</table>
Or the other variation:
React
<table>
<thead>
<tr>
<th>Location</th>
<th>State</th>
<th># Jobs</th>
</tr>
</thead>
<tbody>
{data.map(row => {
return <tr>
<td>{row.key}</td>
{ row.values.map( v => {
return <tr>
<td>{v.key}</td>
<td>{v.value}</td>
</tr>
})}
</tr>
})}
</tbody>
</table>
HTML Output
table, td {
border: 1px solid black;
}
<table>
<thead>
<tr>
<th>Location</th>
<th>State</th>
<th># Jobs</th>
</tr>
</thead>
<tbody>
<tr>
<td>ie11</td>
<tr>
<td>SUCCEEDED</td>
<td>13</td>
</tr>
<tr>
<td>FAILED</td>
<td>7</td>
</tr>
</tr>
</tbody>
</table>
I see that some of the HTML being output for table is not valid as I have nested values, however I am struggling to get my head around how I can map this JSON object to display the HTML table with the rowspan mentioned at the beginning of this question. Thanks in advance for any answers.
Looked at a somewhat similar SO question which is close, but I'm not sure it answers my use-case here

How to create table with multiline cells in React-bootstrap?

I want to create table where some cells contain several lines.
It's work if I do it:
<Table bordered>
<thead>
<tr>
<th>Date</th>
<th>Analysed ID</th>
<th>Analysed Name</th>
<th>Solve To change</th>
</tr>
</thead>
<tbody>
<tr>
<td rowSpan="3">Date</td>
</tr>
<tr>
<td>ID</td>
<td>Name</td>
<td>Decision</td>
</tr>
<tr>
<td>ID</td>
<td>Name</td>
<td>Decision</td>
</tr>
</tbody>
</Table>
I got it:
Table with multiline cell
And now I want to add my 3 "TR" tags in one component, because after I want use for-cycle to create many such components. But components must return content in one closed tag. I tried to contain my 3 "tr" in one parent "tr", but I got error. What can I do here?
It is not possible to create a React Component that returns three elements without wrapping them in another element, such as a div. Otherwise, you'll get the following error:
A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.
Your case here is a bit special, because you cannot have div's as the immediate child of table or tbody, so that's a problem...
What you can do however, is to create a class function that returns an array. Like this:
class MyApp extends React.Component {
getTr = () => {
return [
<tr key={0}>
<td rowSpan="3">Date</td>
</tr>,
<tr key={1}>
<td>ID</td>
<td>Name</td>
<td>Decision</td>
</tr>,
<tr key={2}>
<td>ID</td>
<td>Name</td>
<td>Decision</td>
</tr>
];
}
render() {
return (
<table className="table">
<thead>
<tr>
<th>Date</th>
<th>Analysed ID</th>
<th>Analysed Name</th>
<th>Solve To change</th>
</tr>
</thead>
<tbody>
{this.getTr()}
{this.getTr()}
{this.getTr()}
</tbody>
</table>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
You need to include tr tags in one div tag.
The right way to rowSpan is this:
var MyRow = React.createClass({
render: function () {
return (
<div>
<tr>
<td rowSpan="2">{this.props.item.date}</td>
<td>{this.props.item.data[0].id}</td>
<td>{this.props.item.data[0].name}</td>
<td>{this.props.item.data[0].solve}</td>
</tr>
<tr>
<td>{this.props.item.data[1].id}</td>
<td>{this.props.item.data[1].name}</td>
<td>{this.props.item.data[1].solve}</td>
</tr>
</div>
);
}
});
This is my working example: http://jsfiddle.net/andrea689/e33pd14L/

Categories