I'm making a single MERN app for training and i though use Office Frabric UI could be a good idea.
I'm using server side rendering for my app but i am getting this error when i use a single <Fabric> component:
/home/salahaddin/Proyectos/Tutorials/full-stack-js-lynda/node_modules/#microsoft/load-themed-styles/lib/index.js:277
var head = document.getElementsByTagName('head')[0];
^
ReferenceError: document is not defined
at registerStyles (/home/salahaddin/Proyectos/Tutorials/full-stack-js-lynda/node_modules/#microsoft/load-themed-styles/lib/..\src/index.ts:390:33)
at applyThemableStyles (/home/salahaddin/Proyectos/Tutorials/full-stack-js-lynda/node_modules/#microsoft/load-themed-styles/lib/..\src/index.ts:243:7)
at /home/salahaddin/Proyectos/Tutorials/full-stack-js-lynda/node_modules/#microsoft/load-themed-styles/lib/..\src/index.ts:183:7
at measure (/home/salahaddin/Proyectos/Tutorials/full-stack-js-lynda/node_modules/#microsoft/load-themed-styles/lib/..\src/index.ts:121:3)
at Object.loadStyles (/home/salahaddin/Proyectos/Tutorials/full-stack-js-lynda/node_modules/#microsoft/load-themed-styles/lib/..\src/index.ts:167:3)
at Object.<anonymous> (/home/salahaddin/Proyectos/Tutorials/full-stack-js-lynda/node_modules/#uifabric/utilities/src/scroll.scss.ts:3:1)
at Module._compile (module.js:641:30)
at Module._extensions..js (module.js:652:10)
at Object.require.extensions.(anonymous function) [as .js] (/home/salahaddin/Proyectos/Tutorials/full-stack-js-lynda/node_modules/babel-register/lib/node.js:152:7)
at Module.load (module.js:560:32)
[nodemon] app crashed - waiting for file changes before starting...
This is the problem.
Ok, i see in the "documentation" steps for server-side rendering:
I put this in my serverRender file:
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import axios from 'axios';
import { configureLoadStyles } from '#microsoft/load-themed-styles';
import App from './src/components/app';
import config from './config';
const getApiUrl = contestId => {
if (contestId) {
return ${config.serverUrl}/api/contests/${contestId};
}
return ${config.serverUrl/api/contests};
};
const getInitialData = (contestId, apiData) => {
if (contestId) {
return {
currentContestId: apiData.id,
contests: {
[apiData.id]: apiData
}
};
}
return {
contests: apiData.contests
};
};
const serverRender = (contestId) =>
axios.get(getApiUrl(contestId))
.then(resp => {
let _allStyles = '';
const initialData = getInitialData(contestId, resp.data);
configureLoadStyles((styles: string) => {
_allStyles += styles;
});
return {
initialMarkup: ReactDOMServer.renderToString(
<App initialData={initialData} />
),
initialData,
styles: _allStyles
};
});
export default serverRender;
And this for my server file:
import config from './config';
import apiRouter from './api';
import serverRender from './serverRender';
// import sassMiddleware from 'node-sass-middleware';
import path from 'path';
import express from 'express';
const server = express();
/*
server.use(sassMiddleware({
src: path.join(__dirname, 'sass'),
dest: path.join(__dirname, 'public')
}));
*/
server.set('view engine', 'ejs');
server.get(['/', '/contests/:contestId'], (req, res) => {
serverRender(req.params.contestId)
.then(({ initialMarkup, initialData, styles }) => {
res.render('index', {
initialMarkup, initialData
});
red.render('header', {
styles
});
})
.catch(console.error);
});
server.use('/api', apiRouter);
server.use(express.static('public'));
server.listen(config.port, config.host, () => {
console.info('Express listening on port: ', config.port);
});
And finally in my header file:
Naming Contests
<%- styles -%>
And it still doesn't works.
Which is the properly from to SSR with Office Fabric UI?
Related
I am trying to run my express server in node that I have used in the past and I am getting the follow error in VSCode:
node:internal/modules/cjs/loader:1063
throw err;
^
Error: Cannot find module '/Users/valemich/Desktop/code/src/vscode/calls_sales/start'
at Module._resolveFilename (node:internal/modules/cjs/loader:1060:15)
at Module._load (node:internal/modules/cjs/loader:905:27)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12)
at node:internal/main/run_main_module:23:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
I am on a MACOS Ventura using Node version v.19.5.0 and here is my package.json. Should I uninstall node completely and reinstall? Do I have node installed in the wrong folder? I am completely at a loss for what the issue is.
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.2.5",
"express": "^4.18.2"
},
"devDependencies": {
"ngrok": "^4.3.3"
}
and my server file is as follows:
/* eslint-disable object-shorthand */
/* eslint-disable comma-dangle */
/* eslint-disable semi */
require('dotenv').config();
const express = require('express');
const axios = require('axios').default;
// const Airtable = require('airtable');
// const base = new Airtable({ apiKey: `${process.env.AIRTABLE_TOKEN}` }).base(
// `${process.env.AIRTABLE_CRM_ID}`
// );
const app = express();
const port = 3000;
app.use(express.json());
app.get('/', (req, res) =>
res.send(`
<html>
<head><title>Success!</title></head>
<body>
<h1>You did it!</h1>
<img src="https://media.giphy.com/media/XreQmk7ETCak0/giphy.gif" alt="Cool kid doing thumbs up" />
</body>
</html>
`)
);
app.post(`${process.env.AIRTABLE1_WEBHOOKS_URL}`, (req, res) => {
// TODO: Update the base to show call date, call time, and questions
// TODO: Create API to Calendly to find out who is assigned the call and the event name. Then post back again with information
// TODO: duplicate this information to post to Slack as well (depending on event name to which channel it posts to.)
const eventUrl = req.body.payload.event;
const event = eventUrl.split('/')[4];
console.log(event);
const questions = req.body.payload.questions_and_answers;
const question = [];
// eslint-disable-next-line no-undef
for (i = 0; i < questions.length; i++) {
// eslint-disable-next-line no-undef
question.push(questions[i].question, questions[i].answer);
console.log(question);
}
// const content = `:wave: ${username} just starred ${repoName} :taco: :rocket:`;
// const avatarUrl = req.body.sender.avatar_url;
axios
.post(process.env.AIRTABLE2_WEBHOOKS_URL, {
content: content,
embeds: [
{
image: {
url: avatarUrl,
},
},
],
})
.then((airTableResponse) => {
console.log('Success!');
res.status(204).send();
})
.catch((err) => console.error(`Error sending to AirTable: ${err}`));
});
app.use((error, req, res, next) => {
res.status(500);
res.send({ error: error });
console.error(error.stack);
next(error);
});
app.listen(port, () =>
console.log(`Example app listening at http://localhost:${port}`)
);
I have recently become interested in the subject of server side rendering and have written an application through articles and tutorials that works, but does not show the images that are in the application. I would like my server rendering the App component to render it together with the image in it, but I can't find a way to do it.
Server.js
import express from 'express'
import path from 'path'
import fs from 'fs'
import React from 'react';
import ReactDOMServer from 'react-dom/server'
import App from '../src/App'
const app = express()
const PORT = process.env.PORT || 3001
app.get('/', (req, res) => {
fs.readFile(path.resolve('./build/index.html'), 'utf-8', (err, data) => {
if(err) {
console.log('something went wrong ', err)
return res.status(500).send('better luck next time')
}
return res.send(
data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
)
})
})
app.use(express.static(path.resolve(__dirname, '..', 'build')))
app.listen(PORT, () => {
console.log(`App running on ${PORT}`)
})
App.js
import React, { useState } from 'react'
import './App.css';
import p1 from './images/p1.jpg'
const App = (context) => {
const [count, setCount] = useState(0);
const handleIncrament = () => {
setCount(count+1)
}
const handleDecrament = () => {
setCount(count -1)
}
return (
<div className="App">
<p>{count}</p>
<button onClick={handleIncrament}>Incrament</button>
<button onClick={handleDecrament}>Decrament</button>
<img src={p1} />
</div>
);
}
export default App;
Doesn't this line of code provide access to all the files in the build folder along with the media files?
app.use(express.static(path.resolve(__dirname, '..', 'build')))
I also saw in one of the topics a method to work around this problem by eliminating the imports for images and replacing them with the absolute path to the file. However, before reaching for this solution, I would like to ask if there is any other way? Without removing the import syntax?
I'm setting up a single-page App with React router using Express router on the server side (for serving the first index page with the react router and then standing by for API requests). Currently I only have 2 routes: '/' and '/history'
-clicking on "history" link from the home page gets me there without any problems.
-Manually typing "/history" route makes the browser stay on loading forever.
-Reloading the page while being on "/history" makes the browser stay on loading forever.
how can i go about solving this?
I tried rearranging the routes on express, After this I tried setting the app.all('*') method to handle all requests. Finally I tried changing app.all with app.get...,but neither of these help me.
my index.js (for express router):
/*Express Server*/
const express = require('express');
const app = express();
const morgan = require('morgan');
const path = require('path');
const bodyParser = require('body-parser');
const { mongoose } = require('./database');
//settings
app.set('port', process.env.PORT || 3000);
//middleware
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//routes
app.use('/api/month',require('./routes/monthly.routes'));
app.use('/history',require('./routes/history.routes'));
//static files
app.use(express.static(path.join(__dirname, 'public')))
app.get(async (req,res)=>{
await res.sendFile(path.join(__dirname, '/public/index'), (err)=> {
if (err) {
res.status(500).send(err)
}
})
});
//Start server
app.listen(app.get('port'), ()=>{
console.log(`Server listening on ${app.get('port')}`);
});
router part of my app.js (for react router):
import React, { Component } from 'react';
import ReactDom from 'react-dom';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Axios from 'axios';
import Home from './Home'
import Latest from './Latest'
class App extends Component {
constructor(props) {
super(props);
this.state = {
RecentExpenses: [],
monthState: null,
Total: 0,
Today: null
}
};
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path='/' render={(props) => <Home {...props} Today={this.state.Today} Total={this.state.Total} RecentExpenses={this.state.RecentExpenses} />} />
<Route exact path='/history' render={(props) => <Latest {...props} Recent={this.state.RecentExpenses} month={this.state.monthState} />} />
</Switch>
</BrowserRouter>
)
}
}
ReactDom.render(<App/>, document.getElementById('app'));
What I expect is to always receive the same file from the server, so react router can take on as the navigation router. but it stays loading forever with this being thrown into the node console:
(node:8756) UnhandledPromiseRejectionWarning: Error: Cannot find module 'html'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:571:15)
at Function.Module._load (internal/modules/cjs/loader.js:497:25)
at Module.require (internal/modules/cjs/loader.js:626:17)
at require (internal/modules/cjs/helpers.js:20:18)
On your index.js you could replace that:
app.use(express.static(path.join(__dirname, 'public')))
app.get(async (req,res)=>{
await res.sendFile(path.join(__dirname, '/public/index'), (err)=> {
if (err) {
res.status(500).send(err)
}
})
});
For that:
app.use(express.static(`${process.cwd()}/public/`))
app.get('*', async (req, res) => {
await res.sendFile(`${process.cwd()}/public/index.html`, (err) => {
if (err) {
res.status(500).send(err)
}
})
});
You should be able to access /history without any problem.
I was able to fix it, turns out that it was a routing error. as my history.routes.js was handling the /history requests (with a deprecated part of my code) and it never got to call app.use('*'....).
Bypassing this specific route allowed to get me to serve the index.js and continue the react routing from there. it is a matter of understanding how and when both routings work.
PS: #aquilesb 's answer helped with setting the proper path, for anyone that might have an issue with this.
I am trying to separate responsibilities, dividing in different files and modules the different actions that I will carry out in my application on the server side.
I ran into a problem, which I can not understand. I try to export from the file which starts the server, the variable app, in which I store express in the following way:
server.js
import express from 'express';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackConfig from '../webpack.config';
import path from 'path';
const app = express();
app.set('port', process.env.PORT || 3000);
app.use(webpackDevMiddleware(webpack(webpackConfig)));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'public', 'index.html'));
});
app.get('/api', (req, res) => {
res.json({api: "Woks Fine"});
});
app.listen(app.get('port'), () => {
console.log("App Start in Port", app.get('port'));
});
export default app;
apiGoogleMaps.js
import app from '../server.js';
export function respuestaMensaje(apiUrl, app) {
console.log(apiUrl);
app.post(apiUrl, (req, res) => {
console.log(req.body);
});
}
adressjs
import React, { Component } from 'react';
import { render } from 'react-dom';
import request from 'superagent';
import { respuestaMensaje } from '../../../src/handlers/apiGoogleMap.js';
class AddressInput extends Component{
constructor(){
super();
this.state = {
address: "",
api:"http://maps.google.com/maps/api/geocode/json?address=",
direccion: "",
latitud: "",
longitud:""
};
}
render(){
return(
<div>
<form>
<input type="text" value={this.state.address} onChange={this.updateAdress.bind(this)}/>
<button onClick={this.getAddressGeo.bind(this)}>Consultar</button>
</form>
<ul>
<li><label>Direccion:</label>{this.state.direccion}</li>
<li><label>Latitud:{this.state.latitud}</label></li>
<li><label>Longitud:{this.state.longitud}</label></li>
</ul>
</div>
)
}
updateAdress(event){
this.setState({
address: event.target.value
});
}
getAddressGeo(e){
e.preventDefault();
const apiUrl = this.state.api + this.state.address;
respuestaMensaje(apiUrl);
}
}
export default AddressInput;
Project Structure
Many errors like this appear:
[1] WARNING in ./node_modules/uglifyjs-webpack-plugin/node_modules/uglify-es/tools/node.js
[1] 18:11-32 Critical dependency: the request of a dependency is an expression
[1] # ./node_modules/uglifyjs-webpack-plugin/node_modules/uglify-es/tools/node.js
[1] # ./node_modules/uglifyjs-webpack-plugin/dist/uglify/minify.js
[1] # ./node_modules/uglifyjs-webpack-plugin/dist/uglify/index.js
[1] # ./node_modules/uglifyjs-webpack-plugin/dist/index.js
[1] # ./node_modules/uglifyjs-webpack-plugin/dist/cjs.js
[1] # (webpack)/lib/WebpackOptionsDefaulter.js
[1] # (webpack)/lib/webpack.js
[1] # ./src/server.js
[1] # ./src/handlers/apiGoogleMap.js
[1] # ./src/ui/components/address.js
[1] # ./src/ui/app.js
[1] # ./src/ui/index.js
[1]
Full error log
This error is caused by the webpack import in server.js, used as middleware:
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackConfig from '../webpack.config';
import path from 'path';
const app = express();
app.set('port', process.env.PORT || 3000);
app.use(webpackDevMiddleware(webpack(webpackConfig)));
The telling frames from the stack trace are:
[1] # (webpack)/lib/WebpackOptionsDefaulter.js
[1] # (webpack)/lib/webpack.js
[1] # ./src/server.js
It appears you can't include webpack in a webpack bundle, at least not by importing it, thanks to the dynamic imports that webpack uses.
The same errors appear with express (specifically express.view), explained in this issue:
There's a dynamical requirement in ExpressJS: https://github.com/expressjs/express/blob/master/lib/view.js#L78
You can mark express (and/or webpack) as an external dependency to prevent them from being included in the bundle (I have not used this library, it is linked from the issue).
Instead of exporting your app, which the main backbone of your entire server, use a router in your apiGoogleMaps.js file and export it.
Use the router in your app.js file successfully! :D
birds.route.js
var express = require('express')
var router = express.Router()
router.get('/', function (req, res) {
res.send('Birds home page')
})
router.get('/:id', function (req, res) {
res.send(req.params.id);
})
module.exports = router
app.js
const myBirds = require('./birds');
...
...
app.use('/birds', myBirds);
I followed the tutorial here to implement a server-side rendering Angular 4 app. However, I don't know how to solve the problem related to "document is not defined" in Node.js. I know that Node.js doesn't have a DOM since it is not a "browser". However, I seriously need to render the app in server for SEO, yet I don't know how to solve these problem. I can't change the code as it is a third party code, hmmm... any suggestion?
/var/www/nodejs/example.com/node_modules/custom-event/index.js:24
'function' === typeof document.createEvent ? function CustomEvent (type, params) {
^
ReferenceError: document is not defined at Object.<anonymous> (/var/www/nodejs/example.com/node_modules/custom-event/index.js:24:23)
at Module._compile (module.js:569:30)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:503:32)
at tryModuleLoad (module.js:466:12)
at Function.Module._load (module.js:458:3)
at Module.require (module.js:513:17)
at require (internal/module.js:11:18)
at Object.<anonymous> (/var/www/nodejs/example.com/node_modules/crossvent/src/crossvent.js:3:19)
at Module._compile (module.js:569:30)
I search quite a few posts related to "document is not defined", and most of them just explain that node.js doesn't have a DOM, but without any solution provided.
If you need my server.ts, then here is it.
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { platformServer, renderModuleFactory } from '#angular/platform-server';
import { enableProdMode } from '#angular/core';
import { AppServerModuleNgFactory } from '../dist/ngfactory/src/app/app.server.module.ngfactory';
import * as express from 'express';
import { readFileSync } from 'fs';
import { join } from 'path';
const PORT = 4000;
enableProdMode();
const app = express();
let template = readFileSync(join(__dirname, '..', 'dist', 'index.html')).toString();
app.engine('html', (_, options, callback) => {
const opts = { document: template, url: options.req.url };
renderModuleFactory(AppServerModuleNgFactory, opts)
.then(html => callback(null, html));
});
app.set('view engine', 'html');
app.set('views', 'src')
app.get('*.*', express.static(join(__dirname, '..', 'dist')));
app.get('*', (req, res) => {
res.render('index', { req });
});
app.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}!`);
});
Thanks in advance.
NodeJs does not support window, document, navigator types. So, the solution would be to copy the folder into another directory for example in src/server-mocks/ and replace all document types with global in the index.js file and configure webpack.server.config.js by adding the following code..
const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin');
new NormalModuleReplacementPlugin(
/crossvent(\\|\/)src(\\|\/)crossvent.js/,
path.join(__dirname, 'src/server-mocks/crossvent/src/crossvent.js')
),