Unable to Manipulate DOM with cheerio on nodejs - javascript

I'm running a simple program with the server side on Node with cheerio. Given below are the codes:
Server Side:
/**
* Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, request = require ('request')
, cheerio = require ('cheerio')
, $;
var app = express();
//console.log($('[class = "orange"]').attr('id'));
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/', routes.index);
app.get('/users', user.list);
request('http://localhost:3000', function (error, response, html) {
if (!error && response.statusCode == 200) {
$ = cheerio.load(html);
}
});
app.listen(3000);
var temp2=9;
app.get('/data2', function(req, res){
//var $ = cheerio.load('<body>');
//var temp = $('[class="orange"]').attr('id');
console.log(temp2);
res.send(temp2); //replace with your data here
});
app.get('/data', function(req, res){
//var $ = cheerio.load('<body>');
var temp = $('[class="orange"]').attr('id');
console.log(temp);
res.send(temp); //replace with your data here
});
index.ejs (Express Template)
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js" type="text/javascript"></script>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<input type="button" id="stopButton" value="Button1"/>
<input type="button" id="stopButton2" value="Button2"/>
<p>Welcome to <%= title %></p>
<ul id="fruits">
<li id= "1" class="apple">Apple</li>
<li id = "2" class="orange">Orange</li>
<li id = "3" class="pear">Pear</li>
</ul>
<script type="text/javascript">
$(document).ready(function () {
$('#stopButton').click(function () {
$.get('http://localhost:3000/data', {}, function (data) {
$('[id="2"]').html(data);
});
});
$('#stopButton2').click(function () {
$.get('http://localhost:3000/data2', {}, function (data2) {
console.log(data2);
$('[id="2"]').text(data2);
});
});
});
</script>
</body>
</html>
The template creates a list of items and displays it on the HTML.
Apple
Orange
Pear
Also displayed on the HTML are 2 buttons: Button1 and Button2.
When I press Button1, 'Orange' changes to number 2.
When I press Button2, ideally Orange should change to number 9, but it doesn't. Is there something wrong?
The console.log() for both buttons work perfectly well numbers 2 and 9 shown in the console.
Any help would be appreciated.

There are some misunderstandings here. This code
request('http://localhost:3000', function (error, response, html) {
if (!error && response.statusCode == 200) {
$ = cheerio.load(html);
}
});
app.listen(3000);
doesn't make sense--you're making a request to your own server in order to create the $ object to use Cheerio to parse the form. Then, when a request comes through, you try to pull the user's form data from that HTML that you parsed long ago when the server started using var temp = $('[class="orange"]').attr('id');.
Cheerio isn't able to dynamically track all of the served HTML form values on various clients, so it'll never have any of the dynamic data you may expect it to have, just the same raw HTML you're serving to the client.
Cheerio isn't useful here at all as far as I can tell. It's primarily a web scraping tool for extracting data from other websites' HTML. Data submitted by your clients to the server is taken from the payload, parameters and query string of an HTTP request using request.body, request.params and request.query. POST body payloads are commonly JSON or URL-encoded form data.
Here's a simple example of how you can send data to the server using a GET request's query string and respond with a value that the client can insert into the document. You can adapt this to your use case.
views/index.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= title %></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
</head>
<body>
<h1><%= title %></h1>
<input type="button" id="stop-button" value="Button1" />
<p>Welcome to <%= title %></p>
<ul id="fruits">
<li id="1" class="apple">Apple</li>
<li id="2" class="orange">Orange</li>
<li id="3" class="pear">Pear</li>
</ul>
<script>
$(document).ready(() => {
$("#stop-button").click(() => {
const url = "http://localhost:3000/data";
const word = $("#1").text();
$.get(url, {word}, ({data}) => $("#2").html(data));
});
});
</script>
</body>
</html>
server.js:
const express = require("express");
const path = require("path");
const app = express();
app.set("port", process.env.PORT || 3000);
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "public")));
app.get("/", (req, res) => res.render("index.ejs", {title: "foo"}));
app.get("/data", (req, res) => {
res.json({data: (req.query.word || "") + Math.random()});
});
app.listen(
app.get("port"),
() => console.log(`running on port ${app.get("port")}`)
);

Related

Why am I getting a Cannot POST /api/ error when trying to post to a form?

I have a basic website and am running into a problem. I am extremely new to this and can not seem to fix it.
When I run the server and browse to http://localhost:3000/class/create everything works fine. However, when I try to add a student I receive a message on the browser that says
" Cannot POST /api/create " and a 404 error in the console.
What is supposed to happen: The console logs "creating a student entry"
index.js
const express = require('express')
const app = express()
// loading body-parser
const bodyParser = require('body-parser')
// loading our routers
const mainRouter = require('./mainRoutes.js')
const classRouter = require('./classRoutes.js')
// tell Express to use bodyParser for JSON and URL encoded form bodies
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
// mounting the routers
app.use('/', mainRouter)
app.use('/class', classRouter)
app.listen(3000)
console.log('Express server running on port 3000')
classRoutes.js
const path = require('path')
const express = require('express')
const router = express.Router()
const classList = [] // our class list array
router.get('/', function (req, res) {
res.sendFile(path.join(__dirname, 'views', 'class', 'index.html'))
})
router.get('/create', function (req, res) {
res.sendFile(path.join(__dirname, 'views', 'class', 'create.html'))
})
// RESTful api
router.get('/api/list', function (req, res) {
res.json(classList) // Respond with JSON
})
router.get('/api/get/:id', function (req, res) {
res.json(classList[req.params.id]) // Notice the wildcard in the URL?
// Try browsing to /api/get/0 once you've added some entries
})
router.post('/api/create', function (req, res) {
console.log('creating a student entry')
})
module.exports = router
create.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Class List: Create</title>
<meta charset="utf-8" />
</head>
<body>
<form action="/api/create" method="post">
<div>
<label for="studentName">Enter The Student's Name:</label>
<input type="text" id="student" name="student">
</div>
<div class="button">
<button type="submit">Add</button>
</div>
</form>
</body>
</html>
Chrome Dev Tools output (network tab)
because you set app.use('/class', classRouter); /class must precede /api/create so the correct route is/class/api/create
use the correct absolute path: action="/class/api/create"

Handlebar Issues

I am currently trying to run a GET operation but my code doesn't seem to work. I already have the simple set up done but when I click the button I created to request the data from a simple .JSON file I made I keep getting "0 documents retrieved". Could anyone assist with this? here is my code attached in screenshots. Any and all help is appreciated. Thank you.
Handlebars -
<!DOCTYPE html>
<html>
<head>
<title>Get Request</title>
</head>
<body>
<h2>GET Request</h2S>
<div class="results">
{{#each data}}
<p> {{first_name}} {{last_name}} </p>
{{/each}}
</div>
</body>
</html>
HTML -
<h2>GET Request</h2>
<p>
<a href="/api/user"
data-selector="results">Load user data</a>
<form action="/api/user" method="GET"></form>
</p>
<div class="results"></div>
JavaScript -
const express = require("express")
const app = express();
const mongo = require('mongodb').MongoClient;
const assert = require('assert');
const url = 'mongodb://localhost:27017/test';
var bodyParser = require('body-parser');
var exphbs = require('express-handlebars');
app.set('view engine', 'handlebars');
app.engine('handlebars', exphbs());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.get('/api/user', function(req, res) {
mongo.connect(url,{ useUnifiedTopology: true }, function(err, database) {
const db = database.db('test');
db.collection('users').find()
.toArray(function(err, result) {
if (err) {
throw err;
}
console.log(result.length + ' documents retrieved.');
//res.json(result);
res.render('user', { data:result, layout:false})
database.close();
});
});
});
app.get('*', function (request, response) {
response.sendFile('./public/hello.html',{root: __dirname});
});
const port = 8900;
app.listen(port);
console.log(`Listening to server: http://localhost:${port}`)
JSON -
{
"id": 1,
"name": {
"first_name": "Robert",
"last_name": "Swanson"
},
"title": "Some Dude"
}

Redirect to another url after post request (AJAX, EXPRESS)

As a part of learning node.js servers I'm working on a little log-in website. There's a site you can open and enter your username which will then be sent through an ajax post request to the server and saved into an array of all users. I wanna make it so that after you submit your username, you will be redirected to another page, unique for every username, where you will be able to see the information about you username. Sort of a 'manage your account' site.
However, I can't seem to figure out a way to redirect me to this page after I have submitted an username.
Say for example you submit a username 'kokot' and it's the 3rd username that's been submitted so far. Thus, in the 'players' array, your user object will look something like this {id: 2, username: 'kokot'}.
Now I want to redirect you to the url localhost:2000/players/2 to see the info about your specific username.
NODE JS
const express = require('express');
const server = express();
const bodyParser = require('body-parser');
server.use(bodyParser.urlencoded({extended: false}));
server.use(bodyParser.json());
let players = [];
//loads the home page
server.get('/', (req, res) =>{
res.sendFile(__dirname + '/home.html');
});
//loads the page with the list of players
server.get('/players', (req, res) =>{
res.send(players);
});
server.get('/player/:id', (req, res) =>{
res.send(players[req.params.id]);
});
//takes a new username and saves it to players array
server.post('/', (req, res) =>{
console.log('NEW PLAYER: ' + req.body.username);
players.push({
id: players.length,
username: req.body.username
});
});
/////////////////////////////////////////////////////////////////////////////
server.listen(2000, () => console.log('LISTENING ON PORT 2000'));
HTML
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<title>Home</title>
</head>
<body>
<h1>PISKVOREC</h1>
<form id="userForm">
username
<input type="text" name="text" id="userFormInput">
<input type="submit" name="Submit" id="userFormSubmit">
</form>
<script>
$(document).ready(()=>{
let homeUrl = 'http://localhost:2000';
let $userForm = $('#userForm');
let $userFormSubmit = $('#userFormSubmit');
//submits a new username to the server
$userFormSubmit.click(() =>{
$.post(homeUrl, {
username: $('#userFormInput').val()
}, function(){
console.log('USERNAME SUBMITTED TO SERVER');
});
$.
});
////////////////////
});
</script>
</body>
</html>
Thank you for you responses and ideas
Have a nice day
Sure, see below:
server.post('/', (req, res) =>{
console.log('NEW PLAYER: ' + req.body.username);
players.push({
id: players.length,
username: req.body.username
});
res.redirect(`/player/${req.body.username}`);
});
UPDATE
Demo with vanilla Express.js app
app.js
var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
app.use(bodyParser.urlencoded({extended: false}));
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
app.post('/this', (req, res, next) => {
res.redirect(`/that/${req.body.username}`);
});
app.get('/that/:id', (req, res, next) => {
res.send(req.params);
});
module.exports = app;
index.hbs
<form method="post" action="/this">
<input id="username" name="username" value="vader" type="text" />
<button type="submit">Submit</button>
</form>
results in a redirect to: http://localhost:3000/that/vader

Unable to render JSON in html - node.js

This is probably a simple solution, however I cannot figure out how to carry my JSON object over to my view. Normally you'd throw JSON into res.render() inside your app.get .. unfortunately I am having trouble. A problem arises because i need to be able to send form data from html to my API request, then grab that JSON object and display it in my view. I can see it in my console, but unable to carry it over to html. Would love some help or guidance on how to improve my code or find a solution. Any help is appreciated! - View code below:
Server.js
var path = require('path');
var express = require('express');
var exphbs = require('express-handlebars');
var bodyParser = require('body-parser');
var Bls2 = require('bls2');
var app = express();
app.engine('html', exphbs({ extname: '.html' }));
app.set('view engine', 'html');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
var blsRequest = function(req, res, inputContent){
const API_KEY = //bls API key here
let bls = new Bls2(API_KEY);
console.log('this is seriesID =', inputContent);
let options = {
'seriesid': [inputContent],
'startyear': '2008',
'endyear': '2018',
// ...
};
bls.fetch(options).then(function (response) {
var data = JSON.stringify(response)
console.log(data);
console.log('API Call Complete')
res.send({data : data}); //need to render home view and JSON HERE
});
}
app.get('/', function (req, res) {
res.render('home');
});
app.post('/', function(req, res){
let inputContent = req.body.seriesID;
blsRequest(req, res, inputContent);
});
app.listen(8000);
html
<!DOCTYPE html>
<html>
<head>
<title>LBS</title>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="crossorigin="anonymous"></script>
</head>
<body>
<div class="container-fluid">
<form id="seriesID">
<div class="form-group" method="post" action='/'>
<label for="seriesID">Input Series ID</label>
<input type="text" class="form-control" name="seriesID" placeholder="Series ID">
</div>
<button class="btn btn-primary" type="submit" >Search</button>
</form><br>
uhh JSON should be here: {{data}}
</div>
</body>
<script type="text/javascript">
$("#seriesID").submit(function (event) {
$.post('/', $("#seriesID").serialize(), function (data) {
//data is the response from the backend
});
event.preventDefault();
});
</script>
</html>
use res.render('home',{data});
because your{{data}} is undefined that's why it is not printing anything
it is even better to use {{JSON.stringify(data)}}

NodeJS app: error 404 for scripts/main.js when trying to link

When i go to posts index route (http://localhost:3000/posts), the main.js file served in /public/scripts folder is found, but when i go to the posts show route (...localhost:3000/posts/:id) or any other posts route, chrome's console throws an 404 error for main.js file. Apparently, it's trying to find /scripts at the /views/posts directory.
http://imgur.com/2uNAgBw
Here's my code
app.js:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
methodOverride = require("method-override");
var postRoutes = require("./routes/posts");
app.use(bodyParser.urlencoded({extended: true}));
app.set("view engine", "ejs");
app.use(express.static(__dirname + "/public"));
app.use(methodOverride("_method"));
app.use("/posts",postRoutes);
app.listen(3000, function(){
console.log("application server is running")
});
routes/posts.js
var Post = require("../models/post");
var express = require("express");
var router = express.Router();
var middleware = require("../middleware");
//INDEX - show all colectives
router.get("/", function(req, res){
console.log("get request at /posts");
Post.find({}, function(err, allPosts){
if(err){
console.log(err);
} else {
res.render("posts/index", {posts: allPosts});
} //if
}); // Post.find
}); //router.get
//NEW - show form for creating new post
router.get("/new", middleware.isLoggedIn, function(req, res){
console.log("get request at /posts/new");
res.render("posts/new");
}); //router.get
//SHOW - show more info about a post
router.get("/:id", function(req, res){
console.log("get request at /posts/" + req.params.id);
//find the post with provided id
Post.findById(req.params.id).populate("comments").exec(function(err, foundPost){
if(err){
console.log(err);
} else {
res.render("posts/show", {post: foundPost});
} // if
}); //Post.findByID
}); //router.get
//EDIT - show edit post view
router.get("/:id/edit", middleware.checkPostOwnership, function(req, res){
console.log("get request at /posts/" + req.params.id + "/edit");
Post.findById(req.params.id, function(err, foundPost){
res.render("posts/edit", {post: foundPost});
}); //Post.findById
}); //router.get
module.exports = router;
views/posts/show.ejs
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<div id="masterContainer">
<div class="container">
...
</div>
</div>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<!-- Bootstrap's JavaScript-->
<script type="text/javascript" src="http://ricostacruz.com/jquery.transit/jquery.transit.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="scripts/main.js"></script>
</body>
</html>
Sorry if i misused some programming terms, i'm new to web developing and not a native english speaker. Thanks ahead.
Add a / to the front of the src of your script tag.
<script src="/scripts/main.js"></script>
This way, that script tag always requests the same URL, no matter which page it's on. If you omit the leading /, the actual URL requested is relative to the current URL. For example, if you're currently at localhost:3000/foo/bar, that script tag without the leading / would send a request to /foo/scripts/main.js.
I would add a static express middleware to app.js to avoid problems like that.
app.use('/scripts', express.static(_dirname + '/path/to/scripts'));
Basically, what that does is make sure that whichever level you are on your app, express will always know where to look for the folder scripts.
You should to check the availability of the file "script/main.js".
Look to views/posts/show.ejs:
<script src="scripts/main.js"></script>
It looks like the server-side code works, but browser can't load this file on the path relative to the current route.

Categories