Pass a variable when using pug extends - node.js

I have a settings page called posting.pug and it extends a pug settingsLayout template.
Where are several settings pages in my app. And I want to pass a variable posting=1 to the settingsLayout page to show a user that the posting page is the current.
How should I pass a variable to the template I extend?
I tried to set a variable before extends. That's not allowed.
I tried passing it as an attribute -- doesn't work.

You can try like this
settingsLayout.pug
block variables
doctype html
html
head
body
// Try to call variable here
h1 posting
posting.pug
extends settingsLayout
block variables
- var posting = 1
I hope this work!

The only thing which does not work is overriding pug variables in included files.
It works well with
php compiler pug-php/pug#^3.4
but does not work with
webpack pug-loader#^2.4.0 pug#^2.0.4 webpack#^4.43.0 webpack-dev-server#^3.11.0.
I have a more complicated version.
I would like to override a lot of variables, for instance I have pageTitle, siteTitle, siteDivider variables.
I have a homepage which is views/index.pug.
I have a layout which is views/layouts/default.pug.
I have default variables stored in a few files: views/vars/page.pug, views/vars/site.pug.
Let's start with variables:
File views/vars/page.pug
- var pageTitle = "##Default##"
File views/vars/site.pug
- var siteTitle = "Yaro"
- var siteDivider = "|"
File views/index.pug
extend layouts/default
block vars
include vars/site
include vars/page
- var pageTitle = "Business Solutions & IT development"
File views/layouts/default.pug
block vars
include ../vars/site
include ../vars/page
doctype html
html
head
meta(charset="utf-8")
meta(name="viewport", content="width=device-width, initial-scale=1, shrink-to-fit=no")
title #{pageTitle} #{siteDivider} #{siteTitle}
body
block body
So I wait for the result in the title: Business Solutions & IT development | Yaro
But I receive only: Business Solutions & IT development
without siteDivider and siteTitle :(
But if I have a file like this views/index.pug
extend layouts/default
block vars
include vars/site
include vars/page
- var pageTitle = "Business Solutions & IT development"
- var siteDivider = "|"
- var siteTitle = "Yaro"
It works just fine.
My point is to use default variables to override only needed but not all.

Related

handlebars - add content to head of view from partial

I am using express-handlebars in my project and have the following problem:
Question
I want to be able to add <script> oder such tags to my overall views head from a partial that is called inside the view.
Example:
The view
{{#layout/master}}
{{#*inline "head-block"}}
<script src="some/source/of/script">
{{/inline}}
...
{{>myPartial}}
{{/layout/master}}
The view is extending another partial (layouts/master) that I use as a layout. It adds its content to that ones head block through the inline partial notation, which works fine
the Partial "myPartial
<script src="another/script/src/bla"></script>
<h1> HELLO </h1>
Now I would like that particular script tag in there to be added to my views head-block. I tried going via #root notation but can only reference context there. Not change anything.
I know I could use jquery or similar to just add the content by referencing the documents head and such. But I wanted to know if this is possible at all via Handlebars.
I do doubt it is in any way. But if you have any ideas or suggestions, please do send them my way! Many thanks!!!
UPDATE
This wont work if you have more than one thing injected into your layout / view. Since this happens when the browser loads the page, it creates some kind of raceconditions where the helpers has to collect the things that have to be injected into the parent file. If its not quick enough, the DOMTree will be built before the helper resolves. So all in all, this solution is NOT what I hoped for. I will research more and try to find a better one...
Here is how I did it. Thanks to Marcel Wasilewski who commented on the post and pointed me to the right thing!
I used the handlebars-extend-block helper. I did not install the package, as it is not compatible with express-handlebars directly (Disclaimer: There is one package that says it is, but it only threw errors for me)
So I just used his helpers that he defines, copied them from the github (I am of course linking to his repo and crediting him!) like so:
var helpers = function() {
// ALL CREDIT FOR THIS CODE GOES TO:
// https://www.npmjs.com/package/handlebars-extend-block
// https://github.com/defunctzombie/handlebars-extend-block
var blocks = Object.create(null);
return {
extend: function (name,context) {
var block = blocks[name];
if (!block) {
block = blocks[name] = [];
}
block.push(context.fn(this));
},
block: function (name) {
var val = (blocks[name] || []).join('\n');
// clear the block
blocks[name] = [];
return val;
}
}
};
module.exports.helpers = helpers;
I then required them into my express handlebars instance like so:
let hbsInstance = exphbs.create({
extname: 'hbs',
helpers: require('../folder/toHelpers/helpersFile').helpers() ,
partialsDir: partialDirs
});
Went into my central layout/master file that`is extended by my view Partial and added this to its <head> section
{{{block 'layout-partial-hook'}}}
(The triple braces are required because the content is HTML. Else handlebars wont recognize that)
Then in the partial itself I added things like so:
{{#extend "layout-partial-hook"}}
<link rel="stylesheet" href="/css/index.css"/>
{{/extend}}
And that did the trick! Thanks!!!

Gathering document fragments at rendring time using `pug`

I use pug to generate HTML email messages from a template:
doctype html
html
head
title Hello #{name}
body
...
The title is the subject of the email.
Currently, I extract the title text content by parsing the HTML document rendered by pug. But it doesn't seem to be a very efficient way of doing.
Is there some feature or hook available in pug to collect part of the document while rendering it? I considered pug filters, but as far as I understand, those are not suitable since they are triggered at compile time. Not while rendering the document.
I came to a solution using a mixin:
mixin collect(name)
-
// This is just an ugly hack to
// capture the inner block rendered
// text
const savedHtml = pug_html;
pug_html = "";
if (block) block();
const innerHtml = pug_html;
self[name]=innerHtml;
pug_html = savedHtml+innerHtml;
html
head
title
+collect('title')
| Hello #{self.name}
var pug = require("pug");
const compiledFunction = pug.compileFile('template.pug', {debug:true,self:true});
console.log(compiledFunction(out={
name: 'Timothy',
}));
console.log(JSON.stringify(out));
Displaying:
<html><head><title>Hello Timothy</title></head></html>
{"name":"Timothy","title":"Hello Timothy"}
The code of the collect() mixin is not particularly pretty because as far as I know it there is no elegant way to capture the block() output. So I had to tackle into the internal undocumented pug_html variable.
Or is there a cleaner way to achieve that?

MEAN js dynamic title

how to add dynamic title for each pages of the MEAN js application. in the layout.server.js has defined the title as follows.
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{title}}</title>
so how can we make dynamic title?
Some people might be mislead thinking that besides it is already dynamic, it can be changed and it is controlled by angular out of the box because of having an expression with {{ }} but that's not quite true.
In fact {{title}} could mean an expression that should be evaluated against scope.title, however if you take a deeper look at MEAN.js you will see that it is using the swig template engine which also uses {{ }} to define variables. In this case, {{title}} is NOT an angular expression, it is a swig variable which was passed via express/swig and it can be changed in the config/env/default.js (in MEAN.js 0.4.0).
If you want the title to be changed in the frontend (i.e. possible to change it within angular logic) you have to assign a scope variable to the title element or use a custom directive. Even if, at first, the title value is the one defined using express/swig, angular can take control afterwards and change it accordingly to your needs.
One solution could be to define the title in your angular states like this:
.state('some-state', {
url: '/someurl',
templateUrl: 'some-path-to-view',
data: {
title: 'My new title',
}
})
And then listen for the $stateChangeSuccess event to set the title:
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
console.log(toState.data.title); // Prints the new title to the console
// Set the title
});
EDIT: First paragraph rewritten for more coherence.
To add to the accepted question, in the MeanJS stack you can do the following:
Create a new view in the modules/core/client/views ex. title.client.view.html.
In the file title.client.view.html you can get the title by doing:
<div ng-controller="HeaderController">
<span>{{$state.current.data.pageTitle}}</span>
</div>
The HeaderController has a $state variable that contains the current state title specified in:
.state('some-state', {
url: '/someurl',
templateUrl: 'some-path-to-view',
data: {
title: 'My new title',
}})
Then to get the title in the modules/core/server/views/layout.server.view.html file you include title.client.view.html like this:
<div ng-include="'/modules/core/client/views/title.client.view.html'"></div>
This will render the title of a state dynamically as you navigate.
The MEAN.JS page title is already dynamic and can be found at modules/core/client/directives/page-title.client.directive.js:
function listener(event, toState) {
var applicationCoreTitle = 'MEAN.js',
separator = ' - ',
stateTitle = applicationCoreTitle + separator;
Whereas 'MEAN.js' is the default page title and can be changed accordingly.

Where is title or app name for sails.js (using node.js and express.js)?

I'm trying to change my websites name.
Can't find where I can set title or app name.
You can create any file in config/ directory, for example config/app.js which contains comething like this:
module.exports = {
appName : 'My App'
};
Any property of exported object become an option of Sails` config.
appName property is used as application title. For example, in default layout:
<title><%- title %></title>
where title === sails.config.appName === 'My App'
By default appName config variable is set to 'Sails'.
I've got a good answer at the sails github issues#768.
Adding app name at the app.js and bootstrap.js file works fine for me.
To customise your app's name simply add the following to the module.exports = { … } block in the file config/local.js
appName : 'My Brilliant App',
You can also override the page's title within your controller by passing in your own title value to the view renderer.
res.view({title: 'My brilliant title'});
and, to be SEO friendly, in your jade template specify
head
title= sails.config.appName + " | " + title
As of Sails 1.2.2, you may change the app name in \views\layouts\layout.ejs on line 4: <title>Your App Name</title>.

Use a variable in a Jade include

I'm working with Jade and Express and I would like to use a variable in my include statement. For example:
app.js
app.get('/admin', function (req, res) {
var Admin = require('./routes/admin/app').Admin;
res.render(Admin.view, {
title: 'Admin',
page: 'admin'
});
});
layout.jade
- var templates = page + '/templates/'
include templates
When I do this I get the error EBADF, Bad file descriptor 'templates.jade'
I even tried
include #{templates}
to no avail.
AFAIK JADE does not support dynamic including. What I suggest is to "include" outside the template, i.e.
app.js
app.get('/admin', function (req, res) {
var Admin = require('./routes/admin/app').Admin;
var page = 'admin';
var templates = page + '/templates/';
// render template and store the result in html variable
res.render(templates, function(err, html) {
res.render(Admin.view, {
title: 'Admin',
page: page,
html: html
});
});
});
layout.jade
|!{ html }
this also works:
//controller
var jade = require('jade');
res.render('show', {templateRender: jade.renderFile});
//template
!= templateRender('my/path/'+dynamic+'.jade', options)
This probably will not increase the performance that you would expect from using the 'view cache' setting (it's on by default in NODE_ENV === 'production'). Or even break the app (e.g. if files are not available on the hard drive while deploying new code). Also trying to use this trick in a client-side or isomorphic app will not work because the template can not be compiled.
Found this page googling for the same question, but in a different context, so thought I'd put my solution (read: workaround) here for posterity:
I wanted to surround my include with more context pulled from the variable, e.g. (simplified):
- var templates = page + '/templates/'
- var headid = page + 'head'
- var imgsrc = '/images/' + page
div(id=headid)
h1 #{page}
img(src=imgsrc)
div(id=page)
include templates
Since that doesn't work (Jade doesn't support dynamic includes, as noted by freakish), I hybridized with a mixin:
(Edit– a little more elegant than my previous workaround:)
mixin page1
include page1/templates
mixin page2
include page2/templates
...
- for (var i = 0; i < 3; i++)
- var page = 'page' + i
- var headid = page + 'head'
- var imgsrc = '/images/' + page
div(id=headid)
h1 #{page}
img(src=imgsrc)
div(id=page)
+page
My previous answer:
mixin templates(page)
- var headid = page + 'head'
- var imgsrc = '/images/' + page
div(id=headid)
h1 #{page}
img(src=imgsrc)
+templates('page1')
#page1
include page1/templates/
+templates('page2')
#page2
include page2/templates/
...
It's not elegant, and it won't work if you need to include more than a few things this way, but at least part of the Jade is dynamic.
Why do not use jade inheritance?
Render what you want at middleware level:
res.render('templates/' + template_name + '.jade')
Write common common.jade:
h1 This is a page
.container
block sublevel
h2 Default content
Then write file that extends common.jade:
extends common.jade
block sublevel
h2 Some things are here
It's 2019 and using variables in Pug (previously Jade) mixins has become simple.
When creating your mixin, you can give it parameters as per value(s) you're expecting to pass to the mixin. You can access any nested values using dot notation.
mixinFile.pug:
mixin myMixin(parameter1, parameter2, parameter3)
h2.MyHeading #{parameter1}
p.MyParagraph #{parameter2.myVariable}
.MyBox(id= parameter3.id)
index.pug:
include mixinFile
block content
+MyMixin(variable1, variable2, variable3)
You can read more in the official Pug documentation on Mixins.

Resources