Related
The following is affecting my second html page model.html:
If my route address is with a '/ at the end (typed on browser addr field) like so: http://localhost:3002/home/model/ then the correct html page is loaded, but no css/js is loaded.
If my route address is without a '/ at the end like so: http://localhost:3002/home/model then the correct html page is loaded, and css/js is loaded.
Without '/' at the end, css/js loads fine using statics/css/style.css and js/dynamicData.js
The weird part is that when '/' is at the end, I can use ../statics/css/style.css to load the css and "../js/chartData.js" to load js - but that means the one without '/' now longer loads the css/js.
My folder structure:
js - has other js scripts
node_modules
statics
css - has style.css
image - has images
index.html
model.html
index.js - init express server
pc_server.js - express server
Express code (pc_server.js)
Middleware setup?:
process.chdir(__dirname);
// base = '/home'
app.use(base, express.static(__dirname));
Routes:
const INDEX_PAGE = '/';
const MODEL_PAGE = '/home/model';
function setupRoutes(app) {
const BASE = app.locals.base;
app.get(INDEX_PAGE, redirectHome(app));
// BASE = '/home'
app.get(BASE, toHomePage(app));
app.get(MODEL_PAGE, toModelPage(app));
Routes functions defined:
function redirectHome(app) {
return errorWrap(async function(req, res) {
try {
res.redirect(app.locals.base);
}
catch (err) {
console.error(err);
}
});
}
function toHomePage(app) {
return errorWrap(async function(req, res) {
try {
res.sendFile(path.join(__dirname+'/statics/index.html'));
}
catch (err) {
console.error(err);
}
});
}
function toModelPage(app) {
return errorWrap(async function(req, res) {
try {
res.sendFile(path.join(__dirname+'/statics/model.html'));
}
catch (err) {
console.error(err);
}
});
}
The goal is to load the same page with css/js with either http://localhost:3002/home/model/ or http://localhost:3002/home/model
Additional: Why is it that when I type http://localhost:3002/home I get http://localhost:3002/home/ automatically on my browser addr field?
the problem
it probably occurs due to relative links in your site.
when home/model is used - relative css/style.css link will lead to home/css/style.css
when home/model/ is used, the same link will lead to home/model/css/style.css
the solution:
the easiest way to solve it is changing your link tag to:
<link rel="stylesheet" type="text/css" href="../../home/statics/css/style.css">
this link goes to the root address, and then enters your path Independently from the user's path.
why it's working?
the ../../ prefix tell the browser to go two levels up.
the browser consider the "home/model/" as a visit inside a model folder inside home folder. two levels upward lead the browser to the root level, where it has a clean start.
when the user visits "home/model", it considered as a file inside the home folder. one level upward is the root level, and the second ../ does nothing.
after achieving the root level - the browser entering "home/statics/css/style.css" and find the right file in both cases :)
If I do a
res.sendfile('public/index1.html');
then I get a server console warning
express deprecated res.sendfile: Use res.sendFile instead
but it works fine on the client side.
But when I change it to
res.sendFile('public/index1.html');
I get an error
TypeError: path must be absolute or specify root to res.sendFile
and index1.html is not rendered.
I am unable to figure out what the absolute path is. I have public directory at the same level as server.js. I am doing the res.sendFile from with server.js. I have also declared app.use(express.static(path.join(__dirname, 'public')));
Adding my directory structure:
/Users/sj/test/
....app/
........models/
....public/
........index1.html
What is the absolute path to be specified here ?
I'm using Express 4.x.
The express.static middleware is separate from res.sendFile, so initializing it with an absolute path to your public directory won't do anything to res.sendFile. You need to use an absolute path directly with res.sendFile. There are two simple ways to do it:
res.sendFile(path.join(__dirname, '../public', 'index1.html'));
res.sendFile('index1.html', { root: path.join(__dirname, '../public') });
Note: __dirname returns the directory that the currently executing script is in. In your case, it looks like server.js is in app/. So, to get to public, you'll need back out one level first: ../public/index1.html.
Note: path is a built-in module that needs to be required for the above code to work: var path = require('path');
Just try this instead:
res.sendFile('public/index1.html' , { root : __dirname});
This worked for me.
the root:__dirname will take the address where server.js is in the above example and then to get to the index1.html ( in this case) the returned path is to get to the directory where public folder is.
An alternative that hasn't been listed yet that worked for me is simply using path.resolve with either separate strings or just one with the whole path:
// comma separated
app.get('/', function(req, res) {
res.sendFile( path.resolve('src', 'app', 'index.html') );
});
Or
// just one string with the path
app.get('/', function(req, res) {
res.sendFile( path.resolve('src/app/index.html') );
});
(Node v6.10.0)
Idea sourced from https://stackoverflow.com/a/14594282/6189078
res.sendFile( __dirname + "/public/" + "index1.html" );
where __dirname will manage the name of the directory that the currently executing script ( server.js ) resides in.
Based on the other answers, this is a simple example of how to accomplish the most common requirement:
const app = express()
app.use(express.static('public')) // relative path of client-side code
app.get('*', function(req, res) {
res.sendFile('index.html', { root: __dirname })
})
app.listen(process.env.PORT)
This also doubles as a simple way to respond with index.html on every request, because I'm using a star * to catch all files that weren't found in your static (public) directory; which is the most common use case for web-apps. Change to / to return the index only in the root path.
I tried this and it worked.
app.get('/', function (req, res) {
res.sendFile('public/index.html', { root: __dirname });
});
process.cwd() returns the absolute path of your project.
Then :
res.sendFile( `${process.cwd()}/public/index1.html` );
you can use send instead of sendFile so you wont face with error!
this works will help you!
fs.readFile('public/index1.html',(err,data)=>{
if(err){
consol.log(err);
}else {
res.setHeader('Content-Type', 'application/pdf');
for telling browser that your response is type of PDF
res.setHeader('Content-Disposition', 'attachment; filename='your_file_name_for_client.pdf');
if you want that file open immediately on the same page after user download it.write 'inline' instead attachment in above code.
res.send(data)
Another way to do this by writing less code.
app.use(express.static('public'));
app.get('/', function(req, res) {
res.sendFile('index.html');
});
If you want to set this up once and use it everywhere, just configure your own middleware. When you are setting up your app, use the following to define a new function on the response object:
app.use((req, res, next) => {
res.show = (name) => {
res.sendFile(`/public/${name}`, {root: __dirname});
};
next();
});
Then use it as follows:
app.get('/demo', (req, res) => {
res.show("index1.html");
});
I use Node.Js and had the same problem... I solved just adding a '/' in the beggining of every script and link to an css static file.
Before:
<link rel="stylesheet" href="assets/css/bootstrap/bootstrap.min.css">
After:
<link rel="stylesheet" href="/assets/css/bootstrap/bootstrap.min.css">
The following worked for me
res.sendFile(path.resolve(__dirname,'./path_to_file_from_current_directory'));
Worked for me:
res.sendFile("./public/filename.ext", { root: "./" });
I am hosting an html page on localhost:8888 with a MAMP Server and I am trying to get some data from a JSON file which I am hosting on localhost:3000 on the 'categories' route. Firstly, I wanted to know is this possible ?
If it is not possible, is it possible for me to route the JSON data to another site. If it is possible, here is the script I have embedded in my HTML
<script>
$(document).ready(function(){
setInterval(test,500);
console.log("document ready");
alert('page ready');
});
function test() {
$.ajax({
url:"HTTP://localhost:3000/categories",
dataType: 'jsonp',
success: function(json){
$("#Address1").html(json[0]["id"]);;
}
});
}
</script>
Here is the JSON file :
[{"_id":"5624711f1a530785d511e747","__v":0,"name":"Beverages","description":"Soft drinks, coffees, teas, beers, and ales","created":"2015-10-19T04:27:11.649Z"}]
Currently, It doesn't display any data. I have tried pure JS instead of jquery but it doesn't help.
This is what I got in the Chrome Console : GET localhost:3000/categories?callback=jQuery1113012827125121839345_1445236000644&_=1445236000645 net::ERR_UNKNOWN_URL_SCHEME
Added http:// - does not make a difference
I am creating web page with a button to load data from the server using Rest API build through ExpressJS, NodeJs.
var express=require('express');
var mysql=require('mysql');
var app=express();
var server=app.listen(3000,function(){
console.log("Express is running on port 3000");
});
app.get('/search',function(req,res){
var mysql=require('mysql');
var connection = mysql.createConnection({
connectionLimit : 100, //important
host : 'localhost',
user : 'root',
password : '',
database : 'node-test'
});
connection.connect();
connection.query('SELECT name from users', function(err, rows, fields) {
if (err) throw err;
var data=[];
for(i=0;i<rows.length;i++){
data.push(rows[i].name);
}
res.end(JSON.stringify(data));
});
});
HTML page for this application looks like below
<button >Load from server</button>
<div></div>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$(document).on('click','button', function(){
$.ajax({
url: "http://localhost:3000/search"
}).done(function() {
$('div').append("done !!! - ");
});
});
});
</script>
When I run http://localhost:3000/search in browser it gives me output with "name" from the database. But how can I see the index.html page and make it load on button click.
Update:
OP Asks:
"my question is not what code say....my question is how to change the
code so that expressjs works as RESTful API and not rendering engine"
In order to use express as a RESTful API here, you first need to serve up a static page.
Said another way, here are the steps:
1. Get your express server to serve up a static page.
2. Then get the button on that page to make a GET request to your api endpoint at /search (when clicked).
1 is explained in the 2nd part of my answer.
2 should already work, you just need to serve the page and click the button!
I explain why this doesn't work in the first part of my answer. You can't simply navigate to /search. I think that is what you mean by "not use it as a render engine".
Original Answer:
To understand what is happening here, it might be a good idea to look at how you're handling requests in your serverside code:
When I run http://localhost:3000/search in browser it gives me output with "name" from the database.
That code is:
app.get('/search',function(req,res){
var mysql=require('mysql');
var connection = mysql.createConnection({
connectionLimit : 100, //important
host : 'localhost',
user : 'root',
password : '',
database : 'node-test'
});
connection.connect();
connection.query('SELECT name from users', function(err, rows, fields) {
if (err) throw err;
var data=[];
for(i=0;i<rows.length;i++){
data.push(rows[i].name);
}
res.end(JSON.stringify(data));
});
});
This means that whenever a GET request goes to your route (in this case, the path /search on localhost:3000), the callback function executes. Essentially, when you access localhost:3000/search, your browser sends a GET request, Express checks* the request for a match with each route, and finally going "ok, that's the GET request I need to respond to, let's start searching!".
So it's behaving as expected. Of course, that is not what you want...
But how can I see the index.html page and make it load on button click
Try something like this:
app.get('/', function(req,res) {
res.sendfile('public/index.html');
});
It might not work as is, depending on where your html is defined and what you've named it. Remember to send the right file.
A simpler way to reason about this would be to let express know you're serving up static html.**
That could be done with
app.use("/", express.static(__dirname)); But again, make sure the html defined above is in a file in the proper root folder (probably named server or something similar), with the name index.html (and that there is only one of them).
(See the links on how express middleware works, and serving static HTML, at the bottom)
To wrap up, you implement the second half this answer first, so that you can go directly to localhost:3000 to load your index page. That page will have a button. Then, you'll be able to click the button and make a request to your /search route, without redirecting. The contents of name should come back to the browser now (instead of being served as a new page).
*More on how requests get checked/processed here.
**More info on serving static html. This blog on express fundamentals may also be useful.
1-You have to add routing for index.html
app.get("/index", function(req, res) {
res.render(index.html);
});
And then in your ajax code you can redirect to /index using window.location
2- you can directly render index.html.
Something like this
app.get("/search", function(req, res) {
res.render(search.html,{});
});
app.get('/index',function(req,res){
var mysql=require('mysql');
var connection = mysql.createConnection({
connectionLimit : 100, //important
host : 'localhost',
user : 'root',
password : '',
database : 'node-test'
});
connection.connect();
connection.query('SELECT name from users', function(err, rows, fields) {
if (err) throw err;
var data=[];
for(i=0;i<rows.length;i++){
data.push(rows[i].name);
}
res.render(index.html,{data:data});
});
});
then redirect to page on /index when clicking button.
The problem you have is that you are using Express as a render FrameWork. If you want to build an app with REST/API, the framework should not render the views or templates. The webpage navigation should be separate (e.g Angular JS). In your case, when you call /search you are actually only calling something in the server without any rendering instruction. That is why you see a response JSON object instead of your html template.
So, what to do?.. You need to make a navigation app on your client side, just navigating through templates with nothing out of normal, and program your button to do its request to some api url (something like: localhost:3000/api/search) and with the contents of the response do something: like filling a table, showing them somehow or whatever..
I recommend you to give a try to Angular JS. I am sure it can help you
Cheers
Here is the code I use when I am wanting to use a simple index.html page for test some front-end code.
app.get("/", function(req, res) {
res.sendFile( __dirname + '/index.html')
});
This will map the root path to your index.html. The __dirname assumes the index.html file is in the same directory as your initial server/app file for express. If you want to make it more generic you can do something like the following but then you will need to manually add the index.html to the address bar in your browser but it also lets you load any other static files you want.
app.get(/^(.+)$/, function(req, res){
res.sendFile( __dirname + req.params[0]);
});
<button >Load from server</button>
<div></div>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$(document).on('click','button', function(){
$.ajax({
url: "http://localhost:3000/search"
}).done(function(data) {
$('div').append(data);
});
});
});
</script>
you can read the documentation about $.ajax() from jquery api documentation
http://api.jquery.com/jQuery.ajax/
I would like to remove the # hash from URLs using Angularjs' $locationProvider.html5Mode(true).
Example: The address bar displays http://localhost/shop instead of http://localhost/#/shop.
Everything works well untill I refresh a page. If i refresh, the following Laravel Route (defined in routes.php) is accesed
Route::resource('shop', 'ShoppingController')
not the AngularJS Route (defined in app.js)
$routeProvider.when('/shop', {
templateUrl: 'templates/shop.html',
controller: 'ShoppingController'
});
My Code:
routes.php (Laravel Routes)
Route::get('/', function() {
return View::make('index');
});
Route::resource('shop', 'ShoppingController');
app.js (AngularJS Routes)
var app = angular.module('shoppingApp',['ngRoute','SharedServices']);
app.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/shop', {
templateUrl: 'templates/shop.html',
controller: 'ShoppingController'
});
$routeProvider.otherwise({ redirectTo: '/' });
$locationProvider.html5Mode(true);
});
My directory structure:
Project
/app
/...
/views
-index.php (single page application file)
-routes.php (Laravel routes)
/public
/...
/js
-angular.js
-app.js
-index.php (Laravel index file)
Tried Solutions:
Rewrite the htaccess file so that all requests are redirected to index.php (the single page application file, from where AngularJS would take over the routing). Problem: In this way the Laravel route (Route::resource('shop', 'ShoppingController'); - necessary for interaction with the database) becomes inaccessible to the AngularJS $http service:
app.js
app.controller("ShoppingController", function($scope, $http) {
$http.get('/shop', { cache: true}).
success(function(data, status) {
$scope.items = data
}).
error(function(data, status) {
console.log('Status: ' + status);
});
});
Question:
How can I solve the routing problem, so that the AngularJS route, not the Laravel Route gets accessed if I refresh localhost/shop?
From what I read, it seems like Laravel is reading the modified route when you refresh the page. In this case, you should make Laravel continue to make the original view even if it would otherwise be a 404 redirect.
Try adding the following somewhere on the Laravel side (Ex. routes.php)
App::missing(function($exception)
{
return View::make('index');
});
Note: You might want to have AngularJS's routing use .otherwise to handle pages that are not found.
A better solution is to redirect this way:
'Redirect::to('/#/' . Request::path())'
When you refresh or go to the URI directly:
'Request::path()': returns the requested URI i.e.
('/shop/categories/electronics');
AngularJS in 'html5Mode' still responds to the '#/' prefix;
If angular detects the prefix when in HTML5 mode it will remove the prefix for you.
Final solution:
App::missing(function($exception) {
return Redirect::to('/#/' . Request::path());
});
If you are using Laravel 5 then go to app/Exception/Handler.php and place the code below:
public function render($request, Exception $e)
{
if($e instanceof NotFoundHttpException)
{
return Redirect::to('/#/' . Request::path());
}
return parent::render($request, $e);
}
If you wana have more than one single page application running in html5mode or just have another use for App::missing inside a Laravel app you migh use a rewrite rule like this:
#Redirect base url of AngularJS app in html5mode
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^/path/.+$
RewriteRule ^(path)/(.*) /path/#/$2 [R=301,L,NE]
I have another solution which I found quite useful. Rather than just making home page view, I pass in the URI to the home page, which will get checked by a controller and redirect accordingly (the Angular way). This means that if you are on myapp.com/about and you refresh, instead of taking you home, it takes you back to the page you were currently on.
routes.php: Notice that I have a URI wildcard that I pass in as an argument to the callback function, then as a variable to the view.
// Note that this must be on the bottom of your routes file because
// if you have any registered route with a similar pattern
// it will get caught by this route and never reach any registered routes you might have
Route::get('{slug}', function($slug){
return View::make('index', compact('slug'));
});
// These routes will never get hit, so move them above Route::get('{slug}')
Route::get('about', function(){...});
Route::get('contact', function(){...});
index.blade.php:
<html ng-app"myApp">
<head>
...
</head>
<body>
<!--Check if there is a variable named $slug that is set-->
<!--If it is, set hidden input with ng-model-->
#if(isset($slug))
<input type="hidden" value="{{slug}}" ng-model="slug" ng-controller="RedirectController">
#endif
<div ng-view></div>
</body>
</html>
app.js
angular.module('myApp', [])
.controller('RedirectController', ['$location', '$scope', function ($location, $scope) {
// If slug is set, redirect to that slug
if ($scope.slug) {
$location.path('/' + $scope.slug);
}
}]);
For Laravel 4.x or 5.x i use this simple and nice trick and there is no need to change .htaccess file. this trick is very simple and works for me. it doesn't redirect URL and user will stay on same URL when refresh page and page will refresh:
Route::get('dashboard/{all?}', function(){
return view('main.index');
});
here my dashboard app is my SPA. this way we can resolve 404 errors pages too.