So my web application requires authentication, and I have a signup page where if the person tries to sign up with an email that is already in the database, I want to show them an error message. I'm trying to do this using this code on the html side:
<% if (message.length > 0) { %>
<div class="alert alert-danger"><%= message %></div>
<% } %>
And doing this in my routes:
router.get('/signup', function(req, res) {
res.render('/signup', { message: req.flash('signupMessage') });
});
I've tried setting up the config with something along the lines of:
app.configure(function() {
app.use(express.session({ secret : 'keyboard cat' }));
});
But this gives me a TypeError:
12:11:38 web.1 | app.configure(function() {
12:11:38 web.1 | ^
12:11:38 web.1 | TypeError: Object function (req, res, next) {
I'm really confused, because I know I need sessions to be working for flash to work, but sessions don't seem to be working for me. I've also tried using only sessions and no flash, by using req.session.messages, but since I don't have sessions working this obviously did not work.
Any insights?
I'm using Express 4.0.0
Thanks
This Gist should answer your question:
https://gist.github.com/raddeus/11061808
in your application setup file:
app.use(flash());
Put that right after you set up your session and cookie parser. That's really all you should need to use flash.
You are using:
req.flash('signupMessage', anyValue);
before redirecting to /signup right?
Here's a fun little tidbit that I currently use for a personal site(in my main application file):
app.use(function(req, res, next){
res.locals.success_messages = req.flash('success_messages');
res.locals.error_messages = req.flash('error_messages');
next();
});
Now every view will have access to any error or success messages that you flash. Works well for me.
One final thing (this is nitpicky but you may gain some knowledge). If you change:
<% if (message.length > 0) { %>
to:
<% if (message) { %>
It will work the same way but will not fail if message is undefined. undefined and empty strings are both considered "falsy" values in javascript.
EDIT: My cookie/session/flash setup goes as follows:
app.use(cookieParser('secretString'));
app.use(session({cookie: { maxAge: 60000 }}));
app.use(flash());
Maybe seeing your application setup code would help. Also note that using app.configure is no longer necessary in Express 4.
Final edit: https://gist.github.com/raddeus/11061808
That is a working example. Go to localhost:3000 after running that app and you should see ['it worked'] on your screen.
https://gist.github.com/brianmacarthur/a4e3e0093d368aa8e423
I, too, was initially confused by flash messages in Express 4. The confusion for me arose partly from the distinction between the concept of flash messaging, a temporary message available to a template, and the various implementations of flash messaging, which include express-flash, other modules, and custom middleware.
Just to expand on the excellent response from Thad Blankenship above, I created a Gist for the beginner that includes two approaches to flash messages--the express-flash module and custom middleware--rendered in jade, ejs, or handlebars.
The readme includes details about the getter--req.flash(type)--and setter--req.flash(type, message)--methods exposed by express-flash and how they differ from the exploitation of the res.locals and req.session objects exposed by express-session in the custom middleware.
To show flash message you have to install flash module in your project using cmd.
npm install express-session --save
npm install cookie-parser --save
npm install connect-flash --save
Now you have to add some code to the app.js file to access those modules. Let’s add these code.
var session = require('express-session');
var cookieParser = require('cookie-parser');
var flash = require('connect-flash');
var app = express();
app.use(cookieParser('secret'));
app.use(session({cookie: { maxAge: 60000 }}));
app.use(flash());
Now generate flash message
req.flash('success', 'Registration successfully');
res.locals.message = req.flash();
To show the flash message in view file use the code
<% if(locals.message){ %>
<div class="alert alert-success" role="alert">
<strong>Well done!</strong> <%=message.success%>
</div>
<% } %>
After researching for two days and wanting to give up A LOT I've finally found out how to use connect-flash (u do not need cookie-parser) a few main things use (return res.redirect) instead of res.render it does not like render for callbacks I don't know why. Take a look at my code to get a visual.
app.js
var express = require("express"),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
passport = require("passport"),
LocalStratagy = require("passport-local"),
User = require("./user"),
passportLocalMongoose = require("passport-local-mongoose"),
flash = require('connect-flash'),
app = express();
//using express-session
app.use(require("express-session")({
secret:"The milk would do that",
resave: false,
saveUninitialized: false
}));
app.use(flash());
app.use(function(req, res, next){
res.locals.message = req.flash();
next();
});
//connectiong to a specific database
mongoose.connect("mongodb://localhost/LoginApp");
//so body-parser works
app.use(bodyParser.urlencoded({extended: true}));
//making it so express uses the public dir
app.use(express.static("public"));
//setting the view engine to ejs
app.set("view engine", "ejs");
// so passport works
app.use(passport.initialize());
app.use(passport.session());
//authenticated data from the login form
passport.use(new LocalStratagy(User.authenticate()));
//reading the data and encoding it
passport.serializeUser(User.serializeUser());
//reading the data and unencoding it
passport.deserializeUser(User.deserializeUser());
//ROUTES
app.get("/", function(req, res){
res.render("index");
});
// AUTH ROUTES
//show login
app.get("/login", function(req, res){
req.flash("error", "")
res.render("Login");
});
//handle login form data
app.post("/login", passport.authenticate("local",{
failureRedirect: "/login",
failureFlash: true,
}) ,function(req, res){
req.flash("success", "Logged in");
return res.redirect("/");
});
//Show signup form
app.get("/signup", function(req, res){
res.render("Signup");
});
//handle signup form data
app.post("/signup", function(req, res){
User.register(new User({username: req.body.username}), req.body.password, function(err, user){
if(err){
req.flash("error", err.message);
return res.redirect("/signup");
}
passport.authenticate("local")(req, res, function(){
req.flash("success", "successfuly Signed up");
return res.redirect("/");
});
});
});
app.listen(3000, function(){
console.log("server started");
});
Header.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Fully responsive project with a backend">
<link rel="stylesheet" href="main.css">
<script src="https://code.jquery.com/jquery-3.4.0.min.js"></script>
<!-- animated css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.css">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
<title>wire frame chal</title>
</head>
<body>
<h1><%= message.error %></h1>
<h1><%= message.success %></h1>
Login.ejs
<% include ../partials/header %>
<form method="POST" action="/login">
<input type="text" name="username" placeholder="username">
<input type="password" name="password" placeholder="password">
<button>Submit</button>
</form>
<% include ../partials/footer %>
Signup.ejs
<% include ../partials/header %>
<form method="POST" action="/signup">
<input type="text" name="username" placeholder="username">
<input type="password" name="password" placeholder="password">
<button>Submit</button>
</form>
<% include ../partials/footer %>
This might be an old post guys but I just became aware of express-flash-2. It appears to be less buggy with Express 4 seems to have solved all my problems.
npm link
In my case if i put the message display part in another EJS file and import it in my layout, i get the success and error both displayed initially without any text since the pager just loaded
_message.ejs
<% if(success_msg) { %>
<div class="alert alert-success">
<%= success_msg %>
</div>
<% } %>
<% if(error_msg) { %>
<div class="alert alert-danger">
<%= error_msg %>
</div>
<% } %>
The easy-to-use, although unmaintained, "express-flash-messages" package worked for me. See code here: https://github.com/s2t2/express-app-prep-2021/pull/1/files
App Config:
var session = require('express-session')
var flash = require('express-flash-messages')
var SESSION_SECRET = process.env.SESSION_SECRET || "super secret"
app.use(session({
cookie: { maxAge: 60000},
secret: SESSION_SECRET,
name: 'stocks-app-session',
resave: true,
saveUninitialized: true
}));
app.use(flash())
Route:
req.flash("danger", "OOPS, Please check your inputs and try again.")
Here with Bootstrap styling:
<!--
FLASH MESSAGES
https://github.com/visionmedia/express-messages#rendering-messages
-->
<div class="flash-container">
<% const messages = getMessages() %>
<% if (messages) { %>
<% Object.entries(messages).forEach((obj) => { %>
<% var category = obj[0] %>
<% var message = obj[1] %>
<!--
BOOTSTRAP ALERTS
https://getbootstrap.com/docs/5.0/components/alerts/#dismissing
-->
<div class="alert alert-<%= category %> alert-dismissible fade show" role="alert" style="margin-bottom:0;">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<%= message %>
</div>
<% }) %>
<% } %>
</div>
Related
I have a body that I want to connect to the main template. But unfortunately <%- body %> is not working, and returning undefined. I've tried include(), include file.ejs, directly path. However, that still didn't work, so I'm not sure what to do. Here's my code:
> app.js
var path = require('path');
var express = require('express');
var app = express();
var expressLayouts = require('express-ejs-layouts');
console.clear();
// EJS //
app.set("view engine", "ejs");
app.use(expressLayouts);
app.set("views", path.join(__dirname, "/mvc/views"));
// SRC FOLDER PERMISSION //
app.use('/src', express.static(path.join(__dirname, 'src')));
// MODULES //
var controller = require(path.join(__dirname, "/mvc/controller/controller"));
// USING CONTROLLER MODULE //
app.use("/", controller);
app.listen(8080);
> mvc/controller/controller.js
var express = require('express');
var router = express.Router();
module.exports.index = function(req, res) {
res.render("template");
}
router.get("/", module.exports.index);
module.exports = router;
> mvc/views/template.ejs
<body>
<div class="container-fluid px-0 h-100">
<%-body%>
</div>
</body>
> mvc/views/home.ejs
<nav class="h-100 d-flex justify-content-center align-items-center text-center">
<div class="line w-100">
<span class="d-block"></span>
<span class="d-block"></span>
</div>
</nav>
> result
ReferenceError: C:\laragon\www\galeri-uygulamasi\mvc\views\template.ejs:18
16| <body>
17| <div class="container-fluid px-0 h-100">
>> 18| <%-body%>
19| </div>
20| </body>
21|
body is not defined
at eval (C:\laragon\www\galeri-uygulamasi\mvc\views\template.ejs:10:16)
at template (C:\laragon\www\galeri-uygulamasi\node_modules\ejs\lib\ejs.js:691:17)
at tryHandleCache (C:\laragon\www\galeri-uygulamasi\node_modules\ejs\lib\ejs.js:272:36)
at View.exports.renderFile [as engine] (C:\laragon\www\galeri-uygulamasi\node_modules\ejs\lib\ejs.js:489:10)
at View.render (C:\laragon\www\galeri-uygulamasi\node_modules\express\lib\view.js:135:8)
at tryRender (C:\laragon\www\galeri-uygulamasi\node_modules\express\lib\application.js:640:10)
at Function.render (C:\laragon\www\galeri-uygulamasi\node_modules\express\lib\application.js:592:3)
at ServerResponse.render (C:\laragon\www\galeri-uygulamasi\node_modules\express\lib\response.js:1012:7)
at ServerResponse.res.render (C:\laragon\www\galeri-uygulamasi\node_modules\express-ejs-layouts\lib\express-layouts.js:77:18)
at module.exports.index (C:\laragon\www\galeri-uygulamasi\mvc\controller\controller.js:5:9)
Does anyone know what's going wrong? Any help would definitely be appreciated. Thanks in advance!
You aren't passing a body param into your template
res.render("template", { body: "Some text" })
I have a better solution for you.
Try this :
First change the 'template.ejs' file name to 'layout.ejs'. Because express-ejs-layouts will look for layout.ejs inside youre views folder.
Inside home.ejs add <%- contentFor('body') %> at top of youre code.
Now you can render youre home.ejs with it's layout.
// >mvc/controller/controller.js
module.exports.index = function(req, res) {
res.render("home");
}
I hope I could help😊.
I suggest you take a look here for more information: https://www.npmjs.com/package/express-ejs-layouts
I found the solution!
Firstly I have changed the file name. template.ejs to layout.ejs.
I have changed the res.render("template") as res.render("layout") in controller.js. Thus, the actual layout was written on the screen.
I have wanted write home.ejs to in layout.ejs. Therefore I have changed <%-body%> to <%- include('home.ejs')%>
The problem showing up when I click the button submit.The error coming out 404 not found and "Cannot POST/Index" in the website. Am I have logical problems on the code or problem that occur on the syntax. My program is doing without any http request, it's just a normal import express engine and integrate with html which I'm trying to do a basic post back function to express(server) and post the answer back to my angular html.
I'm trying to post the number back to the server for the calculation and doesn't know where is the error that make me could not doing Post Function. Please get the requirement file from me if the file that I uploaded is not completed.
app.component.html
In my HTML file do I need to add somethings for link the server.ts? Is there still any issue that I have to check on it?
<!doctype html>
<html lang="en">
<head>
<Title>Calculator</Title>
<meta charset="utf-8">
<title>Myapp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
</head>
<body>
<h1 align="center">Angular Calculator</h1>
<div class="container">
<div class="card">
<div class="card-body">
<form action="index" method="POST">
<input type="number" name="num1" class="form-control" placeholder="Number">
<input type="number" name="num2" class="form-control" placeholder="Number">
<select ng-model="operator" name="operator">
<option>+</option>
<option>*</option>
<option>-</option>
<option>/</option>
</select>
<button type="submit">Submit</button>
</form>
<p>Calculation Of the number is :{{ result }} </p>
</div>
</div>
</div>
</body>
</html>
server.ts file
This is the by default server file that generate by npm which I'm not sure the syntax of code any problems for my first testing of addition functions.
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import * as bodyParser from 'body-parser';
import * as express from 'express';
import {join} from 'path';
// Express server
const app = express();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist/browser');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap} = require('./dist/server/main');
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
app.set('view engine', 'html');
app.set('views', DIST_FOLDER);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express.static(DIST_FOLDER, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('/app.component.html', { req });
});
app.get('*', (req, res) => {
res.status(404).send('data requests are not supported');
});
//have body-parser is a piece of express middleware that reads a form's
//input and stores it as a javascript object accessible through
//can not to include body-parser but not to get the raw request, your body and header are not in the root object of request parameter
app.post('/',(req, res) => {
//var num1 = req.body.operator
var result=req.body;
console.log(req.body);
var operator = req.body.operator
if (operator == "+")
{
res.render('/app.component.html', {
result: parseInt(req.body.num1) + parseInt(req.body.num2),
});
}
})
// Start up the Node server
app.listen(PORT, () => {
console.log(`Node Express server listening on http://localhost:${PORT}`);
});
The following line implies you are posting to /index:
<form action="index" method="POST">
For posting to your express service (which is hosted on port 4000 and hence the URL is http://localhost:4000), you have to define a submit behavior on your button. Check this for reference.
I think the problem is you don't include
'angular-route.js'
in your client-side code
you can't post to index <form action="index" method="POST">.
you need a server side file. like: <form action="form_process.php" method="POST">
I am a complete beginner to handlebars and am trying to modify the simple handlebars template taken from the example on glitch.com
I would like to be able to link between .hbs files as I would link between .html files but when I try however I am given the message cannot GET followed by whatever file I give to it.
Here is a grab of my overall structure for ref;
Here is the index.hbs files I am working with
<!DOCTYPE html>
<html>
{{> head }}
<body>
Link to second page
</body>
</html>
Which I would like to link to (for example) this secondpage.hbs file;
<!DOCTYPE html>
<html>
{{> head }}
<body>
Link back to index
</body>
</html>
Here is the code in my server.js file
// Generic node.js express init:
const express = require('express');
const app = express();
app.use(express.static('public'));
const hbs = require('hbs');
hbs.registerPartials(__dirname + '/views/partials/');
app.set('view engine', 'hbs');
app.set('views', __dirname + '/views');
app.get("/", (request, response) => {
let dt = new Date();
let data = {
projectName: process.env.PROJECT_DOMAIN,
luckyNumber: Math.floor(Math.random()*1000),
serverTime: new Date(),
ip: (request.headers["x-forwarded-for"]||"").split(",")[0]
};
data.json = JSON.stringify(data, null, 2);
response.render('index', data);
});
let listener = app.listen(process.env.PORT, () => {
console.log('Your app is listening on port ' + listener.address().port);
});
and the code in my watch.json
{
"install": {
"include": [
"^package\\.json$",
"^\\.env$"
]
},
"restart": {
"exclude": [
"^public/",
"^dist/"
],
"include": [
"\\.js$",
"\\.hbs$",
"\\.json"
]
},
"throttle": 100
}
If any of the details of the other files is necessary to assist let me know and I can provide.
I appreciate I am probably thinking about this in the wrong way, I have looked at handlebars in more detail and experimented with helpers etc. but it seems overly complicated for what I am trying to achieve, I thought you could write basic html within an hbs file? I am looking for the most straightforward, generic solution to the problem of linking between views in handlebars.
FWIW I want to use handlebars in a pretty simple fashion, basically just looking to have the equivalent of php includes using partials instead, so if there is a better way to approach the creation of the app with that in mind I would be grateful for advice.
Your code looks alright. What is the problem exactly? When you add {{> head}} partial to the index.hbs doesn't it render properly?
EDIT:
Okay, you have mainly 2 problems with your code:
You have no route defined on express linking to your /secondpage endpoint.
You are trying to link to a file Link instead of linking to an URL endpoint Link.
To fix your code you would have to define the endpoint linking to the handlebars file, so you need to change your server.js file to something like this.
const express = require('express');
const hbs = require('hbs');
const app = express();
app.use(express.static('public'));
app.set('view engine', 'hbs');
app.set('views', __dirname + '/views');
hbs.registerPartials(__dirname + '/views/partials/');
// 1st Page Route (URL Endpoint)
app.get('/', (request, response) => {
const data = {
projectName: process.env.PROJECT_DOMAIN,
luckyNumber: Math.floor(Math.random() * 1000),
serverTime: new Date(),
ip: (request.headers['x-forwarded-for'] || '').split(',')[0],
};
data.json = JSON.stringify(data, null, 2);
response.render('index', data);
});
// 2nd Page Route (URL Endpoint)
app.get('/secondpage', (request, response) => {
response.render('secondpage');
});
const listener = app.listen(process.env.PORT, () => {
console.log('Your app is listening on port ' + listener.address().port);
});
And then you need to fix your HTML links to this on index.hbs:
<!DOCTYPE html>
<html>
{{> head }}
<body>
Link to second page
</body>
</html>
And this on secondpage.hbs:
<!DOCTYPE html>
<html>
{{> head }}
<body>
Link back to index
</body>
</html>
Hope this helps you.
Instead of using handlebars I used express-handlebars
Terminal: npm i express-handlebars
Handlebars is a Middleware and functions as a Twig (Template Engine) so for your server I'd suggest:
// Generic node.js express init:
const express = require('express');
const app = express();
app.use(express.static('public'));
const exphbs = require('express-handlebars');
app.set('views', __dirname + '/views');
// added this part
app.engine('.hbs', exphbs ({
defaultLayout: 'main',
layoutsDir: ('views', __dirname + 'layouts'),
partialsDir: ('views', __dirname 'partials'),
extname: '.hbs'
}));
app.set('view engine', 'hbs')
app.get("/", (request, response) => {
let dt = new Date();
let data = {
projectName: process.env.PROJECT_DOMAIN,
luckyNumber: Math.floor(Math.random()*1000),
serverTime: new Date(),
ip: (request.headers["x-forwarded-for"]||"").split(",")[0]
};
data.json = JSON.stringify(data, null, 2);
response.render('index', data);
});
let listener = app.listen(process.env.PORT, () => {
console.log('Your app is listening on port ' + listener.address().port);
});
By doing this, you should have a file in your layouts folder named main.hbs where you will have that dynamic approach you're looking for. Something that stays the same for all pages. I will insert here a suggestion, feel free to adapt for your code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- CUSTOM CSS -->
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
{{> navigation }}
<main class="container p-5">
{{{ body }}}
</main>
</body>
</html>
Now when you create a navigation.hbs in your partials folder you will have the same frontend in all pages in the navigation. This is because we defined in the server.js our default template to be main.hbs. Whilst for your body, the triple hash ({{{}}}) inserts the components of the other .hbs files that you define. Don't forget to create a index.hbs file inside the views folder.
I learned the basics of hbs by following this tutorial (Note it's in Spanish). The tutorial produces this open-source project (which I am including in case it is useful).
I've launched a server with Express and attached MongoDB to it. And I put link to app.js file that is used in HTML webpage, but Idk why, but server wants to load js file from localhost:3000, but not from my folder. Here's my project folder:
app
|_node_modules
|_www
|_app.js
|_index.html
|_index.js
|_user.js
|_package.json
|_package-lock.json
app.js file is empty, but index.html isn't
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" charset="utf-8"></script>
<script src="app.js" charset="utf-8"></script>
<meta charset="utf-8">
<style>
input {font-size: 30px}
</style>
<title></title>
</head>
<body>
<button onclick=hashGen()>GENERATE HASH</button>
<form action="/post" method="post">
<input type=text name=pass id=hash>
<input type=number name=pin>
<input type=submit value=submit />
</form>
</body>
</html>
And finally server script (index.js):
// Import modules
exp = require('express');
path = require('path');
parser = require('body-parser');
db = require('mongoose');
User = require('./user.js').User;
app = exp();
// Express & Mongoose params
app.listen(3000);
app.use(exp.static('public'));
app.use(parser.urlencoded({ extended: false }));
app.use(parser.json());
db.Promise = global.Promise;
db.connect("mongodb://localhost:27017/node-demo",{ useNewUrlParser: true });
// Load page
app.get('/',(req,res)=>{
res.sendFile(path.join(__dirname + '/www/main.html'));
})
// Take input & send to db
app.post('/post',(req,res)=>{
let data = new User(req.body);
data.save().then(item =>{
res.send(req.body.user + ' added.')
}).catch(err => {
res.status(400).send("unable to save to database");
});
})
Then I launched my server with nodemon index and it loaded, but with two errors:
GET http://localhost:3000/app.js 404 (Not Found)
localhost/:1 Refused to execute script from 'http://localhost:3000/app.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
Do u guys know what's wrong?
You mapped wrong root directory for your public/static files:
app.use(exp.static('public'));
Should be:
app.use(exp.static('www'));
I've been building an isomorphic react app using node-jsx, browserify, reactify, etc. My code runs fine on the server, and components are mounted and rendered correctly. However, the react function doesn't seem to work, for instance, the handleClick function does not recognize alert(), or console.log() never prints out expected result on neither the server nor the client side console. Can anyone identify what's going on here?
UPDATE: Another thing I want to point out is, when I run the server and go to the browser, in the browser console(used chrome dev tool) I typed "window.React", it actually returned the React object. But console.log still doesn't do anything for click handler function.
views/index.ejs
<!doctype html>
<html>
<head>
<title>Shortened URL Generator</title>
<link href='/style.css' rel="stylesheet">
<link href="css/griddle.css" rel="stylesheet" />
</head>
<body>
<h1 id="main-title">Welcome to Shortened URL Generator</h1>
<div id="react-main-mount">
<%- reactOutput %>
</div>
<!-- comment out main.js to see server side only rendering -->
<script src="https://fb.me/react-with-addons-0.14.3.min.js"></script>
<script src="https://fb.me/react-dom-0.14.3.min.js"></script>
<script src="/main.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//fb.me/JSXTransformer-0.12.0.js"></script>
<script type="text/javascript" src="scripts/griddle.js"></script>
</body>
</html>
routes/routes.js
var React = require('react/addons'),
ReactApp = React.createFactory(require('../components/ReactApp'));
module.exports = function(app) {
var storeUrls = {
"fb.com": "facebook.com"
};
app.get('/', function(req, res){
// React.renderToString takes your component
// and generates the markup
var reactHtml = React.renderToString(ReactApp({}));
// Output html rendered by react
// console.log(myAppHtml);
res.render('index.ejs', {reactOutput: reactHtml});
});
app.get('/:routeParam', function(req, res){
});
};
app/components/ReactApp.js
var TableComponent = require('./TableComponent');
var React = require('react/addons');
var urls = require('./url');
var Griddle = React.createFactory(require('griddle-react'));
var ReactApp = React.createClass({
componentDidMount: function () {
console.log("yes");
},
handleClick: function() {
// this.setState({liked: !this.state.liked});
var longUrl = this.refs.inputUrl;
urls.push({
"original url": longUrl,
"shortened url": "/"
})
console.log(longurl);
},
render: function () {
return (
<div>
<div id="form">
<form>
<section>Paste your long url here</section>
<input ref="inputUrl" value={this.props.value} type="text" placeholder="http://...." />
<button onclick={this.handleClick} type="submit" value="Submit">Shorten URL</button>
</form>
</div>
<div id="table-area">
<TableComponent />
</div>
</div>
)
}
});
module.exports = ReactApp;
app/main.js
var React = require('react/addons');
var ReactApp = require('./components/ReactApp');
var TableComponent = require('./components/TableComponent');
var mountNode = document.getElementById('react-main-mount');
var mountTable= document.getElementById('table-area');
React.render(new ReactApp({}), mountNode);
React.render(new TableComponent({}), mountTable);
server.js
var express = require('express'),
path = require('path'),
app = express(),
port = 5000,
bodyParser = require('body-parser');
require('node-jsx').install();
// Include static assets. Not advised for production
app.use(express.static(path.join(__dirname, 'public')));
// Set view path
app.set('views', path.join(__dirname, 'views'));
// set up ejs for templating. You can use whatever
app.set('view engine', 'ejs');
// Set up Routes for the application
require('./app/routes/routes.js')(app);
//Route not found -- Set 404
app.get('*', function(req, res) {
res.json({
'route': 'Sorry this page does not exist!'
});
});
app.listen(port);
console.log('Server is Up and Running at Port : ' + port);
Gulpfile.js
var gulp = require('gulp');
var source = require('vinyl-source-stream'),
browserify = require('browserify');
gulp.task('scripts', function(){
return browserify({
transform: [ 'reactify' ],
entries: 'app/main.js'
})
.bundle()
.pipe(source('main.js'))
.pipe(gulp.dest('./public/'));
});
gulp.task('default', ['scripts']);
document.getElementById('react-main-mount'); returns null if your script runs before that element is loaded in html.
What you should do is either include your script before the closing tag </body>, or run ReactDOM.render after DOMContentLoaded.
It can be a simple modification to your existing code:
if (typeof window !== 'undefined) {
window.React = require('react');
}
Do that for whatever you want make available globally, usually you'll want to do the same for ReactDOM too so you can call render directly in a html file. It's not usually recommended though. You might want to use the standalone downloads from React Downloads.
So I realized it has something to do with React.render in the main.js file. instead of
React.render(new ReactApp({}), mountNode);
it should be
React.render(<ReactApp/>, document.getElementById('react-main-mount'));
In this case, TableComponent does not need to be rendered, also require React without using the addon.
It works now. Thanks for everyone's help!