Render react component on node server, with webpack - node.js

I have a react component. <myFooter>. It is a simple footer.
import React from 'react';
import './my-footer.scss';
export default class myFooter extends React.Component {
render() {
return (
<footer className="col-xs-12">
hello !
</footer>
);
}
}
I want to render it from the server-side. On the backend, I have an express server. For that I wrote this:
import myFooter from '../components/my-footer.jsx';
app.get('/footer', function(req, res) {
var string1 = ReactDOMServer.renderToString(myFooter);
res.send(string1);
});
Now the problem is that server cannot read sass files. For client side rendering, I am using webpack. Webpack builds everything and gives a bundle file.
But i'm not sure what happens if its the server side. How can I compile using webpack. If I can, will i need to compile my app for each request on node server ?

You need 2 webpack builds:
1 to build the client
1 to build the server.
When building the server, use css-loader/locals instead of css-loader in your webpack configuration.
Read through this issue for more details: https://github.com/webpack/css-loader/issues/59

Related

How do I integrate a Polymer + Node.JS JavaScript app with React + TypeScript

I have recently been tasked with integrating React + TypeScript with an existing starter app that contains many great Polymer Components (Predix UI Starter). I understand that this may seem non-sensical to some (after all, you could simply provide app state and logic using Polymer as a framework). However, the React library and the object oriented nature of TypeScript have a lot to offer, so what are some tips for getting started here?
I will be integrating the Predix UI Starter with the create-react-app starter. Specifically, I will enable a React Component to render() a px-dropdown polymer component. This concept and method can apply to any polymer starter app. This DOES NOT completely integrate these two starters, it is ONLY a POC for rendering a polymer component in a React render() method, using TypeScript.
Clone both of these repos. Work out of the Predix UI Starter - this will contain all of the components once you run npm install and bower install.
Ensure that the package.json in the Predix UI Starter contains all of the dependencies and devDependencies that the react app contains. In the package.json file, in the scripts object, change "start" : ..., to "start" : "react-scripts-ts start". This will serve the app to localhost using TypeScript (this is where the tsconfig.json and the tslint.json files come into play).
Copy the tsconfig.json, tslint.json, and the tsconfig.test.json files into the root directory of the Predix UI Starter.
As per the naming convention for React apps, rename the server folder to src. In the .bowerrc file, append public/ before bower_components, this will enable the public/index.html file to import polymer components generated by bower. Unless configured to do so, public/index.html will be unable to load polymer components in parent folders to the public folder. (The user may need to configure the Predix Starter to initially serve the public/index.html file).
From the react starter to the Predix starter, copy the App.tsx and the index.tsx files to the src folder, and copy the index.html file to the public folder (this file will be what the app initially serves).
Add the px-dropdown component to the JSX namespace so that you can render it within a react component by adding a global.d.ts file to the root directory with the following content:
declare namespace JSX {
interface IntrinsicElements {
'px-dropdown': any
}
}
You are now ready to render() the px-dropdown component. In the public/index.html file, import the component using the following two lines (within the <head> tag):
<link rel="import" href="bower_components/polymer/polymer.html" />
<link rel="import" href="bower_components/px-dropdown/px-dropdown.html" />
Let's pass some items to the props of the App Component. In the index.tsx file, edit the line that renders the component to look like <App items={'[{"key":"1", "val": "One"},{"key" : "2", "val": "Two"}]'}/>
The following code in App.tsx will render the px-dropdown component successfully:
class App extends React.Component<any, any> {
constructor (props: any) {
super(props);
}
render() {
...
return (
<div className="App">
...
<px-dropdown
items={this.props.items}
sort-mode="key"
button-style="default"
display-value="Select"
disable-clear>
</px-dropdown>
</div>
);
}
}
You are now ready to begin rendering Polymer components using React + TypeScript.

Angular2 integration to core file system and node.js libraries

I am studying Angular2, Typescript and reactive programming.
I wrote a simple Typescript application on NodeJs which monitors a directory for changes (core OS and file system). In a single file.
Here it is https://github.com/maroshi/nodejs-directory-monitor/blob/master/app.ts
Now I want to add an interactive GUI to it using Angular2.
I want to integrate my directory monitor as service to Angular2 application.
The questions.
Is it architecturally safe/correct to allow Angular2 application to access the file system and OS (assuming it is a fancy NodeJs server)?
How to integrate NodeJs fs utility library into an Angular2 application.
I am using the Angular2 quickstart template from https://github.com/angular/quickstart.
I modified the app.component.ts
import * as FileSystem from 'fs';
import { Component, OnInit } from '#angular/core';`
#Component({
selector: 'my-app',
template: `
<h1>My First Angular 2 App</h1>
<p>Some FileSystem info: {{dirInfo}}</p>
`
})
export class AppComponent implements OnInit {
private proccesInfo: string;
ngOnInit(){
let tmpString: string[] = FileSystem.readdirSync('/home');
this.dirInfo = tmpString != null ? tmpString[0] : 'undefined';
}
}
This compiles well but fails to bootstrap with the following errors:
GET XHR http://localhost:3000/fs 404 (Not Found)
Error: patchProperty/desc.set/wrapFn#http://localhost:3000/node_moduleszone.js/dist/zone.js:769:27
Zone</ZoneDelegate</ZoneDelegate.prototype.invokeTask#http://localhost:3000/node_modules/zone.js/dist/zone.js:356:24
Zone</Zone</Zone.prototype.runTask#http://localhost:3000/node_modules/zone.js/dist/zone.js:256:29
ZoneTask/this.invoke#http://localhost:3000/node_modules/zone.js/dist/zone.js:423:29
Error loading http://localhost:3000/fs as "fs" from http://localhost:3000/app/app.component.js
Please advise about this exploration.
Note the tsconfig.json is the default tsconfig.json from the link above. Compiling for NodeJs
"module": "commonjs",
Thanks
Joy and happiness
Dudi

Angular / Express hosting

I want to run angular on a linux box without needing node or express. I've created a website but not sure what tech is what, haha. I'm assuming I have a simple web server using express server, see code below.
var express = require ('express');
var app = express();
var path = require('path');
app.use(express.static(__dirname + '/'));
app.listen(8080);
console.log('Magic happens on port 8080');
I start this using the node server command. And the rest of the code is angular-ui.
Do I need to use express (and host this on a node compatible server), or can I just run this thing on a linux box without express? If so, do i need to replace my server.js file (above) with something else? or... Currently it's not working on the linux box, but works locally just fine.
**Edit: I tested an angular 'hello world' app on my shared server, it worked fine. When I run the full angular app on the shared server I get the following error:
Uncaught Error: [$injector:modulerr] Failed to instantiate module routerApp due to:
Error: [$injector:nomod] Module 'routerApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
** edit: In answer to #RobertMoskal 's question below, the angular hello world test that's working on the shared server is basically this:
<input ng-model="name" type="text" placeholder="Type a name here">
<h1>Hello {{ name }}</h1>
And the real app is basically something like this, using ui-view and ng-repeat in the html:
var routerApp = angular.module('routerApp', ['ui.router']);
routerApp.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
$urlRouterProvider.otherwise('/home');
$locationProvider.html5Mode(false).hashPrefix("");
$stateProvider
// HOME STATES AND NESTED VIEWS ========================================
.state('home', {
url: '/home',
templateUrl: 'partial-home.html',
// onEnter: scrollContent
})
// ANIMATION AND NESTED VIEWS ========================================
.state('animation', {
url: '/animation',
templateUrl: 'partial-anim.html',
controller: function($scope) {
$scope.animations = [
{ title:'One', url:'http://yahoo.com', bg:'#f8f8f8', width:'160', height:'600', imageAsset:'assets/imgs/web/MyWebsites_1.jpg', paragraph:'some text of some description'},
{ title:'Two', url:'http://google.com', bg:'#f8f8f8', width:'160', height:'600', imageAsset:'assets/imgs/web/MyWebsites_2.jpg', paragraph:'rabbit rabbit rabbit'},
{ title:'Three', url:'http://bambam.com', bg:'#f8f8f8', width:'160', height:'600', imageAsset:'assets/imgs/web/MyWebsites_3.jpg', paragraph:'blahiblahblah'}];
}
})
// GAME VIEWS ========================================
.state('game', {
url: '/game',
templateUrl: 'partial-game.html'
})
// CONTACT VIEWS ========================================
.state('contact', {
url: '/contact',
templateUrl: 'partial-contact.html'
})
});
You need some web server to server your angular app as a "static" asset. This can be apache or nginx or any number of web servers. Most linux distributions make it easy to install them.
You can also go super lightweight with the built in python web server:
cd /var/www/
$ python -m SimpleHTTPServer
You can even host your application for free on github.
In all cases you you just need to make sure that the web server is serving your assets from the correct path. The above python example example you might have your app entry point in /var/www/index.html and it would be served as http://localhost:8000/index.html.

ES6 React server-side rendering, how to import a React Component?

I'm transpiling ES6 to ES5.
BabelJS for the NodeJS Express server files and server-side rendering output to a directory build/server/.
Browserify + babelify for the ReactComponents output to a build/client/bundle.js file
When trying to import a React Component from build/client/bundle.js to a build/server/ file the app crashes because I'm importing an untranspiled ReactComponent.
How could I import the ReactComponent without duplicating the code in the server (re-using the code from the client/bundle.js)?
You have a few solutions:
Your server code doesn't need to be pre-compiled. If you run it with babel-node, it will be compiled on-the-fly.
You could bundle your server code. I don't know any resource on how to do it with browserify, but here's a very good resource to get started with webpack for your backend.
You could build your client code alongside your server code.

How to share code between node and react?

I use React on server rendering, so the react code need to be used both in node and client, on client side I use browserify.
now suppose I have a component:
var item = React.createClass({
//code here
})
in order to use this component in node I have to require React first, so the component will be
var React = require('React');
var item = React.createClass({
//code here
})
but if I use this component in client via browserify, the React librray will be required, in this case my build js file will be too big. I know I can ignore React in browserify like
browserify -i React
but if I ignore the React then the code:
var React = require('React');
will cause an error "undefined is not a function"
any idea how to avoid this?
You can put your vendor packages in a separate bundle:
browserify -r react -r underscore > vendor.js
And then declare that those dependencies will be provided by an external bundle (or multiple):
browserify -x react -x underscore main.js > bundle.js
And include both in your page:
<script src="vendor.js"></script>
<script src="bundle.js"></script>
You don't need to rebuild the vendor bundle(s) when you build your main bundle (unless you've upgraded dependencies). Usually you don't actually need to do this, and if you're concerned about build times in development: watchify is a a good replacement/addition.
When something is -r'd you can also require it in a plain script tag. This means that there's now a require global, which may clash with other scripts on the page.
<script>
var React = require('react');
</script>
If you're already including React via a separate <script> tag, use the browserify-shim transform to rewrite your require('react') call to use the global React variable.
Add the following config to your package.json:
"browserify-shim": {
"react": "global:React"
}
Use the transform when bundling:
browserify -t browserify-shim lib/index.js -o build/index.js
The bundled version should look something like this:
var React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);
Alternatively, you can omit the need for the -t browserify-shim argument by adding some browserify transform config to your package.json
"browserify": {
"transform": [ "browserify-shim" ]
}

Resources