How do I serve html,css and js as a post response while using CORS? - node.js

Trying to understand the best method to send html,css, js files back to the client via a Post request.
I'm running express,react.
What I have so far is a basic post route, that returns the compiled component with data (using handlebars) as a response. However the event handlers, css and js are absent. I'm not sure how to serve these along with the HTML as a response to an AJAX POST request on another domain.
I'm using webpack for SSR and figured this would work much the same but it doesn't.
Here is what i have so far...this just returns the html from my react component.
app.post("/", async (req, res) => {
const theHtml = `
<html>
<head><title>My First SSR</title>
<link href='http://localhost:8080/app.css'></link>
<script src="http://localhost:8080/app.js" charset="utf-8"></script>
<script src="http://localhost:8080/vendor.js" charset="utf-8"></script>
</head>
<body>
<h1>My First Server Side Render</h1>
<div id="reactele">{{{reactele}}}</div>
</body>
</html>
`;
const hbsTemplate = hbs.compile(theHtml);
const reactComp = renderToString(<App />);
const htmlToSend = hbsTemplate({ reactele: reactComp });
res.send(htmlToSend);
});
The above works and is returned just without js,css event handlers etc..
here is the App component
class App extends React.Component {
constructor() {
super();
this.handleButtonClick = this.handleButtonClick.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.handleReset = this.handleReset.bind(this);
this.state = {
name: "",
msg: ""
};
}
//Handlers
handleButtonClick = e => {
const nameLen = this.state.name.length;
if (nameLen > 0) {
this.setState({
msg: `You name has ${nameLen} characters including space`
});
}
};
handleTextChange = e => {
this.setState({ name: e.target.value });
};
handleReset = () => {
this.setState({ name: "", msg: "" });
};
//End Handlers
render() {
let msg;
if (this.state.msg !== "") {
msg = <p>{this.state.msg}</p>;
} else {
msg = "I have a state of none";
}
return (
//do something here where there is a button that will replace the text
<div>
<header className="App-header">
{/* <img src={logo} className="App-logo" alt="logo" /> */}
{/* <img src={logo} className="App-logo" alt="logo" /> */}
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<label>Your name </label>
<input
type="text"
id="txtName"
name="txtName"
value={this.state.name}
onChange={this.handleTextChange}
/>
<button id="btnSubmit" onClick={this.handleButtonClick}>
Calculate Name Length
</button>
<button id="btnReset" onClick={this.handleReset}>
Reset All
</button>
<hr />
{msg}
</div>
);
}
}
export default App;
On the client side i'm just appending the html to a blank page.
A few questions,
How do I maintain the eventhandlers on the requested html?
How do send the .css and .js along as well?
For some context, I'd like to avoid having to place and maintain 'client' code on my front-end server? My hope was something like webpack would handle this for me?
Thanks for any tips/suggestions.
EDIT:: To clarify this works if I access the route directly. I get the correlating js and css. Just not via a post request from another domain. I assume I'm missing some fundamental udnerstanding how the dom is maintained and scripts run.

You just need a static directory and a view renderer.
In your app.js or where ever you have initialized your express instance, add the following
var hbs = require('hbs');
var path = require('path');
var express = require('express');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
Now you can just put your html in a hbs file in a folder called 'views' and just return it like
app.post("/", (req, res) => {
res.render('index.hbs', {variable: 'value'});
});
For the static assets add the following lines
app.use(express.static(path.join(__dirname, 'public')));
and put your js and css or images or whatever static files you want in a folder named 'public' in your project root. These will be accessible at http://localhost:8080/app.js

So my issue was on client end (my second domain). It was how i was loading the html into the DOM, my old method was inneHTML on the body. Simple answer is this doesnt work.
I had to load it in another way, I chose to use jquery's .html(), this triggers the dom to evaluate the scripts etc.
#RohithBalaji your comment helped me find my issue.
Most AJAX APIs use the browser's .innerHTML to set the content. And
that will strip the and tags, but the scripts
will execute. But, since the head is stripped I am guessing they
aren't loading?

Related

Unable to send api response data from express to react after refreshing the page

I'm learning react and node js with express framework and I'm working on a project where I need to retrieve API data from express to react.
I retrieved data from backend express js where I made a simple json value. My backend server.js code is given below.
server.js
const express = require('express')
const app = express()
const PORT = 3001;
app .get('/api/contents',(req,res)=>{
const contents=[
{
"id":0,
"heading":"Joshua Tree Overnight Adventure",
"content":"A sight in the blue sea..."
},
{
"id":1,
"heading":"Catch waves with an adventure guide",
"content":"Lorem.."
},
{
"id":2,
"heading":"Catch waves with an adventure guide",
"content":"Lorem epsum ..."
}
];
res.json(contents)
})
app.listen(PORT,()=>{
console.log("express server is running...")
})
In react app, I used axios to retrieve those values from backend and tried to pass the api values of content with id= 0 as props in "TheLatestArticles" component. I have updated proxy in package.json in react to connect backend. The below code is the mainhomepage component where it is enclosed with TheLatestArticles component with props value
MainHomePage.js
import axios from 'axios';
import {useState,useEffect} from 'react'
function MainHomePage(){
const [content,setContent]=useState([]);
useEffect(()=>{
const fetchPosts = async ()=>{
const res =await axios.get("/api/contents")
setContent(res.data)
console.log(res)
}
fetchPosts()
},[])
return (
<>
<TheLatestArticle content={content} />
</>
);
}
export default MainHomePage;
TheLatestArticle.js
import cardImage from "./../../Images/card.jpg"
import './TheLatestArticleCard.css';
const TheLatestArticleCard=(props)=>{
console.log(props)
return(
<>
<div className="card">
<div className="image">
<img src={cardImage} alt="cardimg"/>
</div>
<div className="content">
<p className="heading">{props.content.heading}</p>
<p className="body-content">{props.content.content}</p>
<div className="details">
<p className="details-info">Travel <span className="details-info-2">/ August 21 2017</span></p>
</div>
</div>
</div>
</>
)
}
export default TheLatestArticleCard;
When I run the application, It displayed all the api values in the screen given below.
I console.log the api values inside useEffect and it displayed all the api values perfectly.
But when I refresh the page, the api value became undefined and gave me this error
Can you please solve me this issue with the reason behind this error? Thanks a lot!
Try it like this
{(content && content.length > 0) && <TheLatestArticle content={content} />}
Since your API call is async there won't be any data in content initially. After a while, your API is called and data is fetched. Then you will have data. To prevent TheLatestArticle to blow up we add some conditions when to show that component. The error in the screenshot is when you try to use a property heading from content where content is empty.
Now with the condition, TheLatestArticle will not render until there is some data.
Update
You are using <TheLatestArticle content={content} /> and content is assumed to be an object. But as per your code, it's an array. If you are not already using content.map((c)=> <TheLatestArticle content={c} />) you should do that.

Rendering react component on express route

I have an application which uses the express server and the create-react-app front-end. The structure looks like this. ( Not including all the files in the structure - only the ones that matters )
Client-
build
etc
public
src-
assets
components-
landing-
landing.js
github-
github.js
steam-
steam.js
App.js
index.js
routes-
routes.js
index.js
My index.js file is starting the express server and is as following-
const express = require( 'express' );
const app = express();
const PORT = process.env.PORT || 5000;
require('./routes/routes')( app );
app.use( express.static( 'client/build' ));
app.listen( PORT, () => {
console.log( "Server is running on port 5000 " );
});
The route file on the server side is as follows-
module.exports = ( app ) => {
app.get( '/', ( req, res ) => {
console.log("Hello");
res.send( "Hello" );
});
app.get( '/steam', ( req, res ) => {
res.send( "Place for Steam!!");
});
app.get( '/github', ( req, res ) => {
res.send("Place for Github!!");
});
}
My app.js file
class App extends Component {
render() {
return (
<div className="App">
<BrowserRouter>
<div className="container">
<Route path="/" component={ Landing }/>
<Route path="/steam" exact component={ Steam } />
<Route path="/github" exact component={ Github } />
</div>
</BrowserRouter>
</div>
);
}
}
export default App;
On my client side, my main concerned file in landing.js which is as follows.
class Landing extends Component{
render(){
return(
<div className="container">
<div className="row">
<div className="col-md-6">
<div className="bg">
<img src="https://www.bleepstatic.com/content/hl-images/2016/12/23/Steam-Logo.jpg" alt="" />
<div className="overlay">
Steam Info
</div>
</div>
</div>
<div className="col-md-6">
<div className="bg">
<img src="https://linuxforlyf.files.wordpress.com/2017/10/github-universe1.jpg" alt="" />
<div className="overlay">
Github Info
</div>
</div>
</div>
</div>
</div>
)
}
}
export default Landing;
In the above component, the thing that i care about is the a tag which leads to the either /steam or /github express route, which is intentional cause i want to reload the page and on the page I am only getting the res.send data, which makes sense cause that's an express route. But I want to render my steam component on /steam route. ( same with github ). I was hoping my BrowserRouter in App.js would change the component based on the route, but It's not. I am, only getting the express data. How can I render my Steam react component on the express '/steam' route. Clearly I am mixing the server and client side in weird way.
Simply use res.render('index'); for all backend routes.
Here we are building a single-page app with react, which means there's only one entry file (only one html file, usually index.html), the page renders differently because our js code checks the url and decides what to show (which frontend route to use). They all happend after the browser receives the html file along with the js/css files included. All the backend has to do when receiving a page request, is to send the same html file (and js/css files after the html is parsed by browser). Of course for data/xhr requests and invalid requests, we need to send data and 404.html accordingly.

Checksum Invalid - SSR props to Client

I'm using the react engine react-engine on GitHub to create a node, express app with react for the views.
For the most part, my app is rendered on the server. However, on one page/express route I require the view to be rendered server-side and then for the React to be fully interactive on the client.
So far I've got the view rendering server-side and then being re-loaded/re-mounted by React on the client.
My problem is that I'm now getting the following error:
bundle.js:357 Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) <section data-reactroot="" data-reactid
(server) <section cl
Here's what my code looks like:
class FormCreate extends React.Component {
render() {
return (
<ReactBlank title="Create new application form" messages={this.props.messages} authUser={this.props.authUser}>
<script dangerouslySetInnerHTML={{
__html: 'window.PROPS=' + JSON.stringify(this.props)
}} />
<div id="app-content">
<Main {...this.props}/>
</div>
</ReactBlank>
);
}
}
FormCreate.propTypes = {
messages: React.PropTypes.array,
authUser: React.PropTypes.object,
form: React.PropTypes.object
};
module.exports = FormCreate;
The above is initially rendered on the server and then the following re-mounts it on the client:
var React = require('react');
var ReactDOM = require('react-dom');
var Main = require('./app/views/shared/builder/Main.jsx');
document.addEventListener('DOMContentLoaded', function onLoad() {
const propScript = document.getElementById('react-engine-props');
const props = window.PROPS;
ReactDOM.render(
<Main {...props} />,
document.getElementById('app-content')
);
});
Can anyone see a problem here?

How can i access a nodeJS variable in ejs views in a sails project?

My LoginController.js looks like this:
module.exports = {
getAuthorizationLink: function (req, res) {
res.send({
authorization_link: sails.config.FACEBOOK_LOGIN_URL
})
}
}
I need to redirect to the authorization_link when a button is clicked
<div class="col-md-4 text-center">
<button id="authenticate" class="btn btn-primary">Authenticate Page</button>
<script type="text/javascript">
document.getElementById("authenticate").onclick = function () {
...
};
</script>
</div>
Here you are looking to mix server-side (EJS) & client-side JS code.
It is possible, makes sense to do sometimes but it is not clean.
Once you understand you are doing this. Variable can be passed and accessed.
Using EJS, write JS code for client side e.g.
var auth_link = '<%= authorization_link %>';
this line will become something like below for client-side JS
var auth_link = 'https://fb.com/login';
Now you can use auth_link in client-side JS as required
Also, check res.view for responding with HTML page

Render a component from outside ReactJS

From here :
"The only way to get a handle to a React Component instance outside of React is by storing the return value of React.render."
I need to render a React component outside React and the reason for it I'm going to mention below.
In my node.js, expressJS app, I am using 'react-router-component' and 'react-async'.
In app.js -the file which is supposed to be run ,
var url=require('url');
var App=require('./react/App.jsx');
var app = express();
app.get('*',function(req,res){
//}); SEE EDIT 1 BELOW
var path = url.parse(req.url).pathname;
ReactAsync.renderComponentToStringWithAsyncState(App({path:path}),function(err, markup) {
res.send('<!DOCTYPE html>'+markup);
});
});
In App.jsx,
PostList = require('./components/PostList.jsx');
var App = React.createClass({
render: function() {
return (
<html>
<head lang="en">
</head>
<body>
<div id="main">
<Locations path={this.props.path}>
<Location path="/" handler={PostList} />
<Location path="/admin" handler={Admin} />
</Locations>
<script type="text/javascript" src="/scripts/react/bundle.js"></script>
<script type="text/javascript" src="/scripts/custom.js"></script>
</body>
</html>
});
bundle.js is the browserified file from all the .jsx files.
In PostList.jsx,
var PostList = React.createClass({
mixins: [ReactAsync.Mixin],
getInitialStateAsync: function(cb) {
if(typeof this.props.prods==='undefined'){
request.get('http://localhost:8000/api/cats_default', function(response) {
cb(null, {items_show:response.body});
});
}
},
setTopmostParentState: function(items_show){
this.setState({
items_show:items_show
});
},
render: function() {
return (
<div className="postList" id="postList">
**// Things go here**
<div className="click_me" >Click me</div>
</div>
}
});
PostListRender=function(cart_prods){
var renderNow=function(){
//return <PostList cart_prods={cart_prods}></PostList>
React.renderComponent(<PostList cart_prods={cart_prods}></PostList>,document.getElementById('postList') );
};
return {
renderNow:renderNow
}
};
module.exports=PostList;
In custom.js:
$('.click_me').click(function(){
PostListRenderObj=PostListRender(products_cart_found);
PostListRenderObj.renderNow();
$('odometer').html('price');// price variable is calculated anyhow
});
The page shows well.
EDIT 3 Starts
Now I want to render the PostList component on clicking the click_me div .
EDIT 3 Ends
But when I click on the click_me element, the browser shows script busy, console shows
ReactJS - ReactMount: Root element has been removed from its original container. New container
And the Firebug log limit exceeds.
So why I want to render on click from outside react.js:
I have to run the jQuery Odomoeter plugin on clicking the click_me div. The plugin was not developed as a node middleware although it can be installed the way a middleware is installed and the plugin codebase is saved inside node_modules folder.
Edit2 Starts:
As the plugin is not a node middleware, I cannot require it from inside node. However I can perform the click event (code not shown ) from inside node and run the following code there as well :
$('odometer').html('price');// price variable is calculated anyhow
In this case I include the plugin in the browser with <script /> tag and the browserified bundle.js comes after the plugin script . But the animation is not properly shown. So I take to the client side click event in the custom.js.
If I do not require the plugin to be a middleware from inside node
and just include it in the page before the browserified JS file and
perform the click event inside React, then the odometer animation is
not properly shown.
Edit2 Ends:
So what is the way to render the PostList React component outside React ?
EDIT 1 The }); was quite mistakenly placed there
I cannot understand your question description, but this answers the title question:
How you render React components outside of react?
MyComponent = require('MyComponent')
element = document.getElementById('postList');
renderedComp = ReactDOM.render(MyComponent,{...someProps},element);
// => render returns the component instance.
$(document).on('something, function(){
renderedComp.setState({thingClicked: true})
})
Inside of react you can just call the component.

Resources