I'm using Node + Express. On page load, the app calls a remote database, gets data and sends it to a handlebars template. All this is done server side. But I'd like to be able to have this same JSON data be available for the client to interact with. How do I do that?
Example, server displays a table of ten records. I want the client to be able to click on one record and get a details view of just that one record. Thanks.
Here's the code:
app.get('/', function(req, res) {
getDataFromDatabase(function(data) {
data = JSON.parse(data);
res.render('index', {
stuff: data
});
});
});
function getDataFromDatabase(callback) {
var options = {
hostname: this.hostname,
path: this.path,
port: 80,
method: 'GET'
}
http.request(url, function(res) {
var data = '';
res.on('data', function(chunk) {
data += chunk;
}).on('end', function() {
callback(data);
}).on('error', function() {
console.log("error");
})
}).end()
}
How do you get the data as a variable accessible to the client, on
page load, when the server is doing all of the work? Do you have to
make a redundant call from the client and get a "copy" of the data?
- June March
If you don't want to make an AJAX call after the page loads to get at the data, you can send it along with the rest of the page inside of a script tag. Not sure which template engine you're using so the following is sudo code (might be Jade, I don't know). Do something like this in your template:
script var data = JSON.stringify(stuff); // <- horrible variable name btw
If you can successfully create a script tag in your template, and initialize a variable with the data you want to pass to the client, you shouldn't have to make another call to the server.
Related
I create an Ajax request (below) which passes data to the server with the information I need.
function verify(key) {
$.ajax({
async: true,
type: 'GET',
url: '/zonepass/'+key,
data: {
'zone_key': key
},
success: function(res){
//alert('Sent a text message successfully to ' + res);
}
});
}
I handle the Ajax request on the server side where I use the passed in data to query my Firebase DB to get other relevant information.
I then try to render the view page that I want to navigate to using res.render('verify',{zone: obj, key: zone_key}) where verify is another .ejs file that I want to navigate the user to and the JSON object is the data that I want to pass to that template.
My code is not rendering the view page and I'm not sure why. I console logged all the data on the server and all the data is being pulled properly but then my view page never navigates to the verify ejs file...
app.get('/zonepass/:id', function(req,res) {
var zone_key = req.param('zone_key');
var zone_obj = firebase.database().ref('zones').child(zone_key).once('value').then((snap) => {
obj = snap.val();
res.render('verify',{zone: obj, key: zone_key});
});
});
res.render will not work with an ajax request, response from ajax call is returned and accessible inside the success function, but res.render will not work also res.redirect will not work with ajax request.
So you need to submit your request using a form or redirecting on frontend to that route, which is technically also a get request but without ajax example:
Using only HTML:
Verify
Using javascript:
function verify(key) {
window.location.href= "/zonepass/"+ <your id> + "?zone_key=<your zone key>"
}
Also in your NodeJS route you can access id using req.params.id and zone_key using req.query.zone_key, so your server code will be:
app.get('/zonepass/:id', function(req,res) {
var id = req.params.id;
var zone_key = req.query.zone_key;
var zone_obj = firebase.database().ref('zones').child(zone_key).once('value').then((snap) => {
obj = snap.val();
res.render('verify',{zone: obj, key: zone_key});
});
});
BTW you will need to handle inside the verify view, if the key is not verified, example you show an error or message in verify view, that the key is not correct ... or any message related to your logic
I have a working NodeJS API using Express. I am confused how to properly pass in a parameter from NodeJS to AngularJS in order to retrieve the data for the page.
Note: My question is an extension (not a repeat) of this question:
How do I pass node.js server variables into my angular/html view?
I have set up two routes, one to render the page and one to return the JSON object based on the Book ID (the unique ID of the entity). When the page loads, Angular uses $http to send a GET request to get the data from a JSON endpoint.
This is working, but I have hard-coded an example Book ID in Angular.
Question: How can I pass the Book ID to the Angular controller?
/*EXPRESS ROUTES*/
//Render page
router
.route('/detail/:book_id')
.get(ctrlRequest.detailPage);
//Return JSON data
router
.route('/detail/json/:book_id')
.get(ctrlRequest.detailJSON);
/*NODEJS CONTROLLERS*/
//Render page
module.exports.detailPage = function(req, res) {
res.render('transfer_request_detail.pug', {
title: 'Transfer Request Detail'
});
};
//Return JSON data
module.exports.detailJSON = function(req, res) {
getModel().read(req.params.book_id, (err, entity) => {
if (err) {
console.log('Request Detail JSON unable to return data results. Error message: ', err);
return;
} else {
res.json(entity);
}
});
};
/*ANGULAR WITH HARD-CODED BOOK ID*/
//QUESTION: HOW TO PASS THE BOOK ID IN DYNAMICALLY?
function DetailPageController($http) {
var vm = this;
$http({
url: '/detail/json/5761233819297931', //HARD-CODED ID HOW TO PASS IN DYNAMICALLY?
method: 'GET'
}).then(function (response){
vm.book = response.data;
console.log('API worked', response);
},function (error){
console.log('API error: ', error);
});
}
UPDATE:
"This probably depends on how your application is used. For example, the app might display a list of Books fetched from Express, and the user clicks one to see the detail page. In that case, the frontend fetches the complete list of Book ID's from Express. Can you please outline the behavior of your application?"
Use Case 1:
After the user submits a the New Book form, the Node app will redirect to the URL that renders the detail page. '/detail/:book_id'. I want to use Angular to build a datatable to display the details of the book that was created.
Use Case 2:
Another use case in the future will be to display all books that were created by the logged-in user. In this case, I want to use Angular to display all the books in a datatable. The route will be something like '/mybooks/:username'. Also, the user should also be able to click on a link that brings them to the details page (use case 1) for single book.
UPDATE 2:
I tried using routeParams to extract the ID out of the URL. It appears to be exactly what I need however it isn't working. Note that the routes were created on the server side with Express, not Angular.
$routeParams //Object
$routeParams.book_id //undefined
https://docs.angularjs.org/api/ngRoute/service/$routeParams
UPDATE 3:
I was able to solve this with a bit of a hacky workaround. I'm not super satisfied with this solution since it is quite hacky but at least it works for the time being.
I used this code in the Angular controller to extract the ID from the URL.
var relpath = $location.path().split('/');
var book_id = relpath[3];
UPDATE 4:
So it looks like I am back to square one. This solution broke the express routing. I am now having the same issue as described in this thread because locationProvider HTML mode is enabled to get this solution to work. Pages aren't loading when clicking on menu links, they are only loading when typed in directly.
Angular routing doesn't work when URL is directly visited in browser but works when clicked to?
You can use template literals: in your url string.
function DetailPageController($http, id = 5761233819297931) {
var vm = this;
$http({
url: `/detail/json/${id}`, //HARD-CODED ID HOW TO PASS IN DYNAMICALLY?
method: 'GET'
}).then(function (response){
vm.book = response.data;
console.log('API worked', response);
},function (error){
console.log('API error: ', error);
});
}
I know how to send an http request to a server using angular js. With the promise returned, I know how to listen for a response and manipulate the ui thereafter. But this approach cannot be used for what I have in mind.
However, what I cannot figure out, is how to send a request to a website.
I have a server localhost:800/receiveData which receives a POST request and then manipulate the UI and DoM on the angularjs site
app.get('/', function(req,res){
res.sendFile(__dirname+'/index.html')
})
app.post('/receiveData', function(req,res){
var data = req.body.data
// assume data is a boolean
if(data){
//show a view in index.html using angular js or anything else
}else {
//show a different view in index.html
}
});
Any help will be greatly appreciated. I have a need for angular js. Having a SPA is imperative. I am completely open to adding additional stacks if neccessary.
EDIT:
As pointed out by MarcoS, manipulation of dom should ideally not happen from the server side. I am combining IPFS with node js and angular js to develop a single page application. The swarm of nodes set up using IPFS has an open line of communication with my server (by design). Based on packets of data sent via the comm line to my server, I need to convey messages to the user via the index.html.
I think your approach is wrong: on server-side, you should NOT manipulate the UI and DOM...
You should just do server activity (update a database, send an email, ..., return a static page).
Then you can output a result (JSON/XML/... format) for your client-side calling script to read.
Following OP edit, what I do understand is he wants server push to the client.
To get serve side pushes, you should poll on the client.
In a controller:
function getServerState(changeState) {
return $http.get("/receiveData").then(function(res) {
changeState(res.data); // notify the watcher
}).catch(function(e) {
/* handle errors here */
}).then(function() {
return getServerState(changeState); // poll again when done call
});
}
Consuming it this way:
getServerState(function(status) {
$scope.foo = status; // changes to `foo` $scope variable will reflect instantly on the client
});
And, server side:
app.post('/receiveData', function(req, res) {
var data = req.body.data; // assume data is a boolean
res.end(JSON.stringify(data);
});
I want to use nodeJS as tool for website scrapping. I have already implemented a script which logs me in on the system and parse some data from the page.
The steps are defined like:
Open login page
Enter login data
Submit login form
Go to desired page
Grab and parse values from the page
Save data to file
Exit
Obviously, the problem is that every time my script has to login, and I want to eliminate that. I want to implement some kind of cookie management system, where I can save cookies to .txt file, and then during next request I can load cookies from file and send it in request headers.
This kind of cookie management system is not hard to implement, but the problem is how to access cookies in nodejs? The only way I found it is using request response object, where you can use something like this:
request.get({headers:requestHeaders,uri: user.getLoginUrl(),followRedirect: true,jar:jar,maxRedirects: 10,},function(err, res, body) {
if(err) {
console.log('GET request failed here is error');
console.log(res);
}
//Get cookies from response
var responseCookies = res.headers['set-cookie'];
var requestCookies='';
for(var i=0; i<responseCookies.length; i++){
var oneCookie = responseCookies[i];
oneCookie = oneCookie.split(';');
requestCookies= requestCookies + oneCookie[0]+';';
}
}
);
Now content of variable requestCookies can be saved to the .txt file and can loaded next time when script is executed, and this way you can avoid process of logging in user every time when script is executed.
Is this the right way, or there is a method which returns cookies?
NOTE: If you want to setup your request object to automatically resend received cookies on every subsequent request, use the following line during object creation:
var request = require("request");
request = request.defaults({jar: true});//Send cookies on every subsequent requests
In my case, i've used 'http'library like the following:
http.get(url, function(response) {
variable = response.headers['set-cookie'];
})
This function gets a specific cookie value from a server response (in Typescript):
function getResponseCookieValue(res: Response, param: string) {
const setCookieHeader = res.headers.get('Set-Cookie');
const parts = setCookieHeader?.match(new RegExp(`(^|, )${param}=([^;]+); `));
const value = parts ? parts[2] : undefined;
return value;
}
I use Axios personally.
axios.request(options).then(function (response) {
console.log(response.config.headers.Cookie)
}).catch(function (error) {
console.error(error)
});
I am new to Node.js and this is my first project with it.
I have made a node.js file named test.js. It has an array say a.
Now I want to make a HTML file that calls this test.js on button click event. Then get the data from that file and publish it on a table in the HTML file.
I have already written the node.js file and I can see the results on console.log(a). But I cant understand how to send this array to HTML when it will ask for it.
Meanwhile, I googled and made up some code. The request reaches the server but I always get error response from server. Why so?
Client Side -
function fetch() {
$.ajax({
type: 'POST',
url: "http://127.0.0.1:8888",
data: 'China',
datatype: 'json',
success: function (data) {
alert("hi");
var ret = jQuery.parseJSON(data);
$('#q').html(ret.msg);
},
error: function (xhr, status, error) {
alert("hii");
}
});
Server side :
http.createServer(function(request, response) {
console.log("Request received");
response.writeHeader(200, {"Content-Type": "application/json"});
request.on('data', function (chunk) {
console.log(chunk.toString('utf8'));
consol.log(result);
response.write(JSON.stringify({data : result}));
});
response.end();
}).listen(8888);
I can see China on the console.But I dont get back the result array back to the client. Here result is an array and I get its value on the console. Just that I dont get it back to the client. Any help ?
You should start by setting up a server to serve requests. I use expressjs for this - http://expressjs.com/
This will allow you to run nodejs as a web application.
Setup a route in express JS to serve your data - http://expressjs.com/api.html#express
var express = require('express');
var app = express();
app.get('/data', function(req, res){
res.send('hello world'); //replace with your data here
});
app.listen(3000);
Open up a browser, and type in http://MY_SERVER_ADDR:3000/data and you should see your output there.
Next, you'll need to attach an event handler to your HTML file that will trigger a $.get() request when it is triggered. Add the previous url to your data in your $.get call and do something with it.
$('.my_selector').click(function(){
$.get('http://MY_SERVER_ADDR:3000/data', {}, function(data){
console.log(data)
});
});
That should get you going.
After wrestling with the same question, i found that this is exactly where a template engine comes into the node-picture.
EJS solved it for me, but there are many more available.
This article compares 10 template engines.