How to handle the dynamic page on Node.js - javascript

I am using the Node.js(Express.js) and EJS.
My query is how I can manage the Dynamic page. Here Dynamic means admin create the page, that page will be accessible and render according to the name of the category.
Statically I can handle with create the file.ejs on view folder, then on server file I can write the code like:
app.get('/testpage', (req, res) => {
res.render('/test.ejs')
})
So on type the url page will serve the content which are written under test.ejs file.
But I want to handle all this things Dynamically.
I have done this type of things through Jquery where I created the page and then call the data through api (ajax), then render the element under the target content.
Is there any one who can suggest this through a short and easiest manner which will be efficient and best programmig approach.

See routing in the Express.js guide.
Pull the selected category out of the URL with params, then look it up in the database, and pass the resulting data to your EJS template.
You can create a route such as:
app.get('/users/:category', function (req, res) {
const category = req.params.category;
getDataAboutCategoryFromDatabase(category).then(
data => red.render("category.ejs", data)
);
})

Related

Data VS Async Data in Nuxt

Im using vue.js with nuxt.js, I'm just still confused as when to use Data VS Async Data. Why would I need to use Async data when I just have data that just displays on the page?
I have a data object of FAQ's and just want to display the data without doing anything with it. What are the benefits of using the asyncData? Or what are the cases or best use of them?
Should I display list data such as this as async by default if using data such as this inside of my component?
Data
data:() => ({
faqs:[
{"title":"faq1"},
{"title":"faq2"},
{"title":"faq3"},
]
}),
asyncData
asyncData(context) {
return new Promise((resolve, reject) => {
resolve({
colocationFaqs:[
{"title":"faq1"},
{"title":"faq2"},
{"title":"faq3"},
]
});
})
.then(data => {
return data
})
.catch(e => {
context.error(e);
});
},
asyncData happes on the serer-side. You cant access browser things like localStorage or fetch() for example but on the ther hand you can access server-side things.
So why should you use asyncData instead of vue cycles like created?
The benefit to use asyncData is SEO and speed. There is this special context argument. It contains things like your store with context.store. Its special because asyncData happens on server-side but the store is on the client side usually. That means you can get some data and then populate your store with it and somewhere else you display it. The benefit of this is that its all server-side and that increase your SEO so for example the google crawler doesnt see a blank page
why would I need to pre render it when it is going to be displayed
anyway
Yes for us it doesnt matter if i send 1 File to the client and it renders all data like in SPA's or if its pre-rendered. But it doesnt matter for the google crawler. If you use SPA mode the crawler just sees a blank page. You can discoverd it too. Go to any SPA website and click right-click and inspect you will see thats there only 1 Div tag and few <script> tags. (Dont press F12 and inspect like this thats not what i mean).

Send file - remove parameters

So I'm navigating to a site like /subsite with a specific URL that hold the key parameter:
https://my.server.com/premiumSite?key=secretKey
or
https://my.server.com/premiumSite.html?key=secretKey
I'd like to determine if the key parameter is "secretKey". If it is I'd like to send a file (not redirect!) called premiumSite.html to the user. If the key is something different I'd like to redirect the user to the index.html site to don't give the user access to my site.
The whole routine is working absolutely fine - but (of course) theres one problem:
The function res.sendFile() (like I want it to be) does not change the URL at all - what also effects the parameters.
So how can I call sendFile() but also remove the "key" parameter from the URL at the same time (not using local js)?
let app = express();
app.use('/premiumSite(.html)?',function(req, res) {
let isPremiumUser = req.query.key === "secretKey"; // check if URL parameters are matching
if (isPremiumUser) res.sendFile("www/premiumSite.html", {root: __dirname});
else res.redirect("www/index.html");
})

How to send objects from server to client side components in react-express node app

How can I send JS objects from Express backend to client side React components?
I can send simple variables by setting them in res.locals or cookies, but for larger objects, how do I do it?
Not in favor of API as it leads to the page to load and then request makes user wait on the screen. My details are available as soon as server is started.
Code flow in brief:
Its a kraken app, and in server side, if I do,
router.get('/', (req, res) => res.render('index')),
ejs is set as view engine in express config, it will return index file to browser with the react code bundled out by webpack added to it.
In general it is recommended to expose an API so that the React interface can update data, e.g. when the user navigates the page, without reloading the whole page. React is built especially with SPAs in mind and a large part of react is dedicated to managing incremental updates to the page structure.
If however you want to supply some amount of data to your react page on load anyway, you can pass it using a script tag and JSON like so:
in your template:
<script>
window._myInitialState = JSON.parse("{initialState|js|j|s}");
<script>
(this is using the js (JSON.stringify), j (JavaScript string) and s (don't escape HTML) builtin dust filters) to render your data. E.g. with res.locals.initialState set to
{
users: [
{ name: "Test" },
{ name: "Toast" },
{ name: "Foo" },
{ name: "Bar" },
]
}
it should JSON encode that into the following script:
<script>
window._myInitialState = JSON.parse("{\"users\":[{\"name\":\"Test\"},{\"name\":\"Toast\"},{\"name\":\"Foo\"},{\"name\":\"Bar\"}]}");
<script>
With this in your template you should then be able to access window._myInitialState in your React code, e.g. to load it in your root component like so:
componentDidMount() {
this.setState(window._myInitialState);
}

Practice for Routing Handler in MEAN Stack?

I have an application on Express 4.x integrated with the Twilio API and dependent on the Input from the users' phone, I will respond with different XML files that may or may not be created dynamically.
Where am I supposed to put this theoretical route conditional? i.e.
exports.handle = function(req, res) {
if(req.body.digits == 1){
//pass to first option handler
}
if (req.body.digits == 2) {
//create xml file dynamically
//for second option
}
else {
//handle else
}
};
It seems a bit heavy to be putting into the routes file. In such MVC structure is it typical to put this conditional into a controller? Or stuff the routes? Or is there another option that I'm unaware of?
I'd much rather just have this code pass all requests to a single handler. i.e.
exports.handle = function(req, res) {
if (req.body.digits)
//send to handler
};
Where does this go? What is it called?
In this scenario, your router is your "single handler". You are channeling all input through the routing mechanism and letting it decide who the appropriate handler (or controller) is. This is commonly referred to as "front controller". It makes sense to put the logic handler in the file you are referencing if you architect your software with this in mind.

Express - Send a page AND custom data to the browser in a single request?

How simultaneously to render a page and transmit my custom data to browser. As i understood it needs to send two layers: first with template and second with JSON data. I want to handle this data by backbone.
As i understood from tutorials express and bb app interact as follows:
res.render send a page to browser
when document.ready trigger jQuery.get to app.get('/post')
app.get('/post', post.allPosts) send data to page
This is three steps and how to do it by one?
var visitCard = {
name: 'John Smit',
phone: '+78503569987'
};
exports.index = function(req, res, next){
res.render('index');
res.send({data: visitCard});
};
And how i should catch this variable on the page- document.card?
I created my own little middleware function that adds a helper method called renderWithData to the res object.
app.use(function (req, res, next) {
res.renderWithData = function (view, model, data) {
res.render(view, model, function (err, viewString) {
data.view = viewString;
res.json(data);
});
};
next();
});
It takes in the view name, the model for the view, and the custom data you want to send to the browser. It calls res.render but passes in a callback function. This instructs express to pass the compiled view markup to the callback as a string instead of immediately piping it into the response. Once I have the view string I add it onto the data object as data.view. Then I use res.json to send the data object to the browser complete with the compiled view :)
Edit:
One caveat with the above is that the request needs to be made with javascript so it can't be a full page request. You need an initial request to pull down the main page which contains the javascript that will make the ajax request.
This is great for situations where you're trying to change the browser URL and title when the user navigates to a new page via AJAX. You can send the new page's partial view back to the browser along with some data for the page title. Then your client-side script can put the partial view where it belongs on the page, update the page title bar, and update the URL if needed as well.
If you are wanting to send a fully complete HTML document to the browser along with some initial JavaScript data then you need to compile that JavaScript code into the view itself. It's definitely possible to do that but I've never found a way that doesn't involve some string magic.
For example:
// controller.js
var someData = { message: 'hi' };
res.render('someView', { data: JSON.stringify(someData) });
// someView.jade
script.
var someData = !{data};
Note: !{data} is used instead of #{data} because jade escapes HTML by default which would turn all the quotation marks into " placeholders.
It looks REALLY strange at first but it works. Basically you're taking a JS object on the server, turning it into a string, rendering that string into the compiled view and then sending it to the browser. When the document finally reaches the browser it should look like this:
// someSite.com/someView
<script type="text/javascript">
var someData = { "message": "hi" };
</script>
Hopefully that makes sense. If I was to re-create my original helper method to ease the pain of this second scenario then it would look something like this:
app.use(function (req, res, next) {
res.renderWithData = function (view, model, data) {
model.data = JSON.stringify(data);
res.render(view, model);
};
next();
});
All this one does is take your custom data object, stringifies it for you, adds it to the model for the view, then renders the view as normal. Now you can call res.renderWithData('someView', {}, { message: 'hi' });; you just have to make sure somewhere in your view you grab that data string and render it into a variable assignment statement.
html
head
title Some Page
script.
var data = !{data};
Not gonna lie, this whole thing feels kind of gross but if it saves you an extra trip to the server and that's what you're after then that's how you'll need to do it. Maybe someone can think of something a little more clever but I just don't see how else you'll get data to already be present in a full HTML document that is being rendered for the first time.
Edit2:
Here is a working example: https://c9.io/chevex/test
You need to have a (free) Cloud9 account in order to run the project. Sign in, open app.js, and click the green run button at the top.
My approach is to send a cookie with the information, and then use it from the client.
server.js
const visitCard = {
name: 'John Smit',
phone: '+78503569987'
};
router.get('/route', (req, res) => {
res.cookie('data', JSON.stringify(pollsObj));
res.render('index');
});
client.js
const getCookie = (name) => {
const value = "; " + document.cookie;
const parts = value.split("; " + name + "=");
if (parts.length === 2) return parts.pop().split(";").shift();
};
const deleteCookie = (name) => {
document.cookie = name + '=; max-age=0;';
};
const parseObjectFromCookie = (cookie) => {
const decodedCookie = decodeURIComponent(cookie);
return JSON.parse(decodedCookie);
};
window.onload = () => {
let dataCookie = getCookie('data');
deleteCookie('data');
if (dataCookie) {
const data = parseObjectFromCookie(dataCookie);
// work with data. `data` is equal to `visitCard` from the server
} else {
// handle data not found
}
Walkthrough
From the server, you send the cookie before rendering the page, so the cookie is available when the page is loaded.
Then, from the client, you get the cookie with the solution I found here and delete it. The content of the cookie is stored in our constant. If the cookie exists, you parse it as an object and use it. Note that inside the parseObjectFromCookie you first have to decode the content, and then parse the JSON to an object.
Notes:
If you're getting the data asynchronously, be careful to send the cookie before rendering. Otherwise, you will get an error because the res.render() ends the response. If the data fetching takes too long, you may use another solution that doesn't hold the rendering that long. An alternative could be to open a socket from the client and send the information that you were holding in the server. See here for that approach.
Probably data is not the best name for a cookie, as you could overwrite something. Use something more meaningful to your purpose.
I didn't find this solution anywhere else. I don't know if using cookies is not recommended for some reason I'm not aware of. I just thought it could work and checked it did, but I haven't used this in production.
Use res.send instead of res.render. It accepts raw data in any form: a string, an array, a plain old object, etc. If it's an object or array of objects, it will serialize it to JSON for you.
var visitCard = {
name: 'John Smit',
phone: '+78503569987'
};
exports.index = function(req, res, next){
res.send(visitCard};
};
Check out Steamer, a tiny module made for this this exact purpose.
https://github.com/rotundasoftware/steamer
Most elegant and simple way of doing this is by using rendering engine (at least for that page of concern). For example use ejs engine
node install ejs -s
On server.js:
let ejs = require('ejs');
app.set('view engine', 'ejs');
then rename desired index.html page into index.ejs and move it to the /views directory. After that you may make API endpoit for that page (by using mysql module):
app.get('/index/:id', function(req, res) {
db.query("SELECT * FROM products WHERE id = ?", [req.params.id], (error, results) => {
if (error) throw error;
res.render('index', { title: results[0] });
});
});
On the front-end you will need to make a GET request, for example with Axios or directly by clicking a link in template index.ejs page that is sending request:
<a v-bind:href="'/index/' + co.id">Click</a>
where co.id is Vue data parameter value 'co' that you want to send along with request

Categories

Resources