How to know if block content is empty in Jade - node.js

How can we check if block is exist in Jade, I'm trying to make a template in Jade where in i have following block in layout.jade and in login.jade i have extended layout.jade. Now when in account.jade i will have both left and main content blocks
extend layout.jade
div(class='col-md-4')
block leftcontainer
div(class='col-md-8')
block content
Now in login.jade i want to hide
div(class='col-md-4')
block leftcontainer
and make the main content full page, if leftcontainer is not there. It should look like this
div(class='col-md-12')
block content
if user is on some page i want to hide left column block. Say if user is in login page then

If I got it right, you wan't to hide a block if a user (e.g. a user who is logged in to your app) is at a certain page. If this is right do something like this:
Example with Express & Passport:
// server
app.get('/login', function(req, res){
if(req.isAuthenticated()) {
res.render('login', { 'user' : req.user } );
} else {
res.render('login', { 'user' : null } );
}
});
// layout
extend layout.jade
if user !== null
div(class='col-md-12')
block content
else
div(class='col-md-4')
block leftcontainer
div(class='col-md-8')
block content
In general you can work with other conditional statements in Jade, without Passport, this was just an example. You may want to take a look at the reference, especially under conditional or case.

Related

Button in Node.js project works properly after two clicks not one

I'm making a simple project with node.js, express, mongodb, express session etc. It is a travel type site and a user in the database has a username, password and a list of destinations he wants to go to. Each destination page has a button to add that destination to the list and there is also a page which shows the current user's list. The proper functionality is that on the first button press the destination should be added to the users list in the database and the ejs view for the list page should be updated and on the second button press an alert should come up saying that the current destination is already on the user's list. However, seemingly randomly the button sometimes only works properly after two button presses and the alert and ejs view update of the list page occur on the third button press. Here are some code snippets:
This is my get request for the list page where I pass the current session user's want to go list as a parameter:
function isAuthenticated (req, res, next) {
if (req.session.user) next()
else res.redirect('/');
}
app.get('/wanttogo',isAuthenticated,function(req,res){
res.render('wanttogo',{dests:req.session.user.wantgo})
});
I update this list both in the session and in my database in each page's post request. This is an example for one of the pages:
app.post('/inca',function(req,res){
if((req.session.user.wantgo.length === 0) || !(req.session.user.wantgo.includes("Inca Trail to Machu Picchu"))){
req.session.user.wantgo.push("Inca Trail to Machu Picchu");
req.session.save();
db.collection("myCollection").updateOne({username:req.session.user.username},{$set:{wantgo:req.session.user.wantgo}});
db.collection("myCollection").findOne({username: req.session.user.username},(err,result)=>{
req.session.user.wantgo = result.wantgo;
req.session.user=result;
req.session.save();
});
}
else{
alert('This destination is already on your Want-To-Go List');
}
res.redirect('/inca');
and this is the part of the ejs view of the list page where I loop over the user's list and print it using the parameter I passed earlier:
<div>
<% for(var i=0; i < dests.length; i++) { %>
<h1><%= dests[i] %></h1>
<% } %>
</div>
Any help is appreciated!
This could be due to the fact "updateOne" and "findOne" are async functions and thus the database is not updated before you do the second button press.
Since this depends on time when the async operation is completed it sometimes works and sometimes doesn't.
Try using async await like this
app.post('/inca',async function(req,res){
if((req.session.user.wantgo.length === 0) || !(req.session.user.wantgo.includes("Inca Trail to Machu Picchu"))){
req.session.user.wantgo.push("Inca Trail to Machu Picchu");
req.session.save();
await db.collection("myCollection").updateOne({username:req.session.user.username},{$set:{wantgo:req.session.user.wantgo}});
await db.collection("myCollection").findOne({username: req.session.user.username},(err,result)=>{
req.session.user.wantgo = result.wantgo;
req.session.user=result;
req.session.save();
});
}
else{
alert('This destination is already on your Want-To-Go List');
}
res.redirect('/inca');

Changing script files used on the front end of a node backed website

I'm making a solitaire game using NodeJs and Express backend and phaser.io frontend. It will offer different layouts of cards to play.
I have the game working "just fine" alpha state, but with a single layout of cards setup in HTML like:
<script src="js/gameboards/data.js"></script>
<script src="js/tableau.js"></script>
where data.js is the file that describes the card layout, and tableau.js is the game logic. I have several different files in /gameboards and for the testing/building, I just change the filename when I want to change the layout.
data.js is not a JSON file, it's a JS object
let gameboard = {
info: {
title: "Standard",
description: "6 columns of 5 cards"
},
[...]
deal: function() {
for (let i = 0; i < this.vars.allstacks; i++) {
etc...
That contains simple object data as well as object methods that define patterns and repetition (like multiple stacks, pyramids, etc) so it can't really be made into a JSON or other straight data file.
What I want to do is present a list of anchors/links to the player of the layout options. They'll click the link to get sent into /game with the layout they chose.
The method I thought of was to have /index send POST-data containing the layoutname. Then, in /game
router.post('/', function(req, res, next) { ... })
with the HTML template and res.render containing
<script src="js/gameboards/<layoutname>.js"></script>
to call the right file.
Is there a better way of doing this than what I listed above? It seems kludgey to me as if there would be a more 'nodey' and elegant way to do it.
To do it in the way you describe you need to enable a templating engine like handlebars or Pug. For pug I do something like this:
In server:
const cdnAction = process.env.S3_CDN_ACTIVE;
app.route('*')
.get( (req, res) => {
const cdnCSSPath = `https://s3.amazonaws.com/${process.env.S3_BUCKET_CDN}/audience.css.gz`;
res.render('home', { cdnActive, cdnPath, cdnCSSPath });
});
In pug file 'home.pug' rendered above:
head
if cdnActive === 'true'
link(href=cdnCSSPath rel='stylesheet' type='text/css')
else
link(href='/style/embed.css' rel='stylesheet' type='text/css')

New to Sails.js how can I route to a view without including the layout.ejs?

New to Sails.js I've read over the docs but I don't see a way to NOT include the layout.ejs located in /views/.
I'm trying to implement Angular into my app and when I go
$routeProvider.when('/',{
controller: 'HomeController',
templateUrl: '/home'
})
So then in my routes I expect to be able to do:
'GET /home': { view: 'angular/home.ejs' }
But what happens is it loads home.ejs but also injects layout.ejs so it becomes an endless loop of injecting layout.ejs loading angular scripts from layout, then trying to load home.ejs which loads layout.ejs so on so forth
So how can I do this?
I know I can put views in /asset/angular/home.html but I would like to have .ejs so that I can render a different view (e.g. user is not logged in) or something.
Any information on how I can render views without injecting layout.ejs would be great, thanks!
Update:
So by doing this:
'GET /home': {
view: 'home',
locals: {
layout: false
}
}
it makes it work, I don't know if there is a global way so I don't have to flag layout: false for each one
To disable layouts globally, you can put layout : false under /config/view.js.
To disable layout individually, leave /config/view.js alone and do something like this in your routes:
'GET /home': {
view: 'home',
locals: {
layout: false
}
}

How to conditionally show different UI in Jade?

I have the following template:
extends layout
block content
#wrapper
#logo
a(href='/')
p #{title}
#msg
| hi #{user}
#display
#register
----A registration form here----
#login
----A login form here-----
include footer
If the user opens this page via POST and I use a token to decide if he is logged in like so:
exports.home = function(req, res) {
// if user is not logged in, show both login and register forms
if (typeof req.session.username == 'undefined') {
res.render('home', { title: 'Online Coding Judge'});
}
// if user is logged in already, just say hi to him
else {
// what here???
}
};
How do I show different things on the page depending on whether he logged in or not?
EDIT: A more realistic example would be to show an error message if there was a failed login in the same UI I would need to conditionally show an error message.
Something like this should work:
EDIT: revised:
block content
#wrapper
#logo
a(href='/')
p #{title}
if {user}
#msg
| hi #{user}
#display
#register
----A registration form here----
#login
----A login form here-----
else
--- some other logic ---
include footer

How to use multiple blocks from different files?

I have a layout.jade that looks like this:
html
body
block content
block footer
My content.jade looks like that:
extends layout
block content
#Content Welcome
My footer.jade looks like that:
extends layout
block footer
#Footer Impressum
Now, when I run my app like that:
app.get('/', function(req, res) {
res.render('layout');
});
I do not see neither the content nor the footer.
When I run:
app.get('/', function(req, res) {
res.render('content');
});
Then I see the content.
When I run:
app.get('/', function(req, res) {
res.render('footer');
});
Then I see the footer.
How can I see both, content and footer?
You probably want something like this:
layout.jade
html
body
block content
include footer
pagename.jade
extends layout
block content
h1 My Content
footer.jade
p.footer Here is my footer
Then run res.render('pagename');.
Unless you want to have specific stuff in your footer per page, there's no point in making it a block.

Resources