Client side + Server side templating, feels wrong to me, how to optimize? - node.js

In the web app I am making, I use the classical Express+Jade to render client pages and also expose some REST API (let's say : "user list API").
These client pages use provided API to retrieve "user list" and display it. To display it, I use the handlebars template library once the data is retrieved.
It seems a bit dirty to me, using two template engines, parsing the code twice, how to make it better ?
Note : I already optimized the thing by sending the initial data within the client page by inserting it a script variable. This variable is then displayed the same way data received by API would be. The API is only used in case of data refresh.
UPDATE : using jade both server and client side is a good idea but how to seperate / specify ? Wich part of the rendered template should be done by when serving the page and what part will be used by the client ?

That's very easy to use Client side + Server side templating.When we are building some web apps,we should use ajax to get some data and use the callback function to deal with it.So we should render these data on the client side.
The question is how to render them on client side?
Now We just need a client side jade.js.
Follow this document : https://github.com/visionmedia/jade#readme
First
git clone https://github.com/visionmedia/jade.git
Second
$ make jade.js ( in fact the project has already compile the file for us )
so we just need to copy this file to the path that we use.
Third
read my demo below :
<script type='text/javascript' language='javascript' src="lib/jquery-1.8.2.min.js"></script>
<script type='text/javascript' language='javascript' src="lib/jade/jade.js"></script>
<script type='template' id='test'>
ul
li hello world
li #{item}
li #{item}
li #{item}
</script>
<script>
var compileText = $("#test").text();
console.log( typeof( compileText ) );
var fn = jade.compile( compileText , { layout : false } );
var out = fn( {item : "this is item " } );
console.log( out );
$("body").append( out );
</script>
Now you can see the output result in the body
hello world
this is item
this is item
this is item
After reading this demo I think that you would know how to seperate jade server side and client side.If you can understand which one compile the jade template,then all the questions are easy.
Maybe you would have another question now.How to write some jade template codes in *.jade?The document also provide us a way to do it.This Tutorial may help you.
index.jade
!!!5
html
head
title hello world
body
ul#list
script#list-template(type='template')
|- for( var i in data )
| li(class='list') \#{ data[i].name }
|- }
index.js
/* you javascript code */
var compileText = $('#list-template').text();
var compile = jade.compile( compileText , { layout : false } );
var data = [{ "name" : "Ben" } , {"name" : "Jack" } , {"name" : "Rose" }];
var outputText = compile( data );
$("#list").append( outputText );

Use http://github.com/flatiron/plates template engine which will work both on the client side and server side.

A few weeks ago I wrote an npm package for Handlebars templates to share them between client and server. It's pretty basic, but it's been working really well for me so far:
https://github.com/jwietelmann/node-handlebars-precompiler
Edit: I'm separately using "hbs" as the package for server-side rendering. The precompiler just delivers precompiled templates to my public javascripts directory whenever I update my hbs views.

Related

Writing nodejs scripts inside nextjs HTML

I am programing my new website in a structure with nodejs, nextjs and expressjs. The problem is tho that I want to write a if statement inside of the HTML part which in that case it will write the if statement as normal html on the website:
const Admin = () => (
<AdminLayout>
<style global jsx>
{
`body {
background: #eff0f3;
}`
}
</style>
<div className="jumbotron" style={jumbotron}>
if (1=1) {
<h3>Please select a tool first.</h3>
} else {
<h3>Something is wrong :/ .</h3>
}
</div>
</AdminLayout>
)
export default Admin
And the out put is just:
if (1=1)
Please select a tool first.
else
Something is wrong :/ .
as HTML on the website. How would I do this but having the script actually being a script?
Next.js uses React that allows you to write something called jsx, which is what you're calling the "HTML part". You can write JavaScript expressions in jsx, but they need to be wrapper in {} brackets. Otherwise any JS code will actually just be interpreted as a text string in the rendered HTML. Check out the React Docs for Embedding Expressions in JSX.
There's lots of ways to do conditional rendering in React.
Here's one way to rewrite your code using {} to embed an expression and the ternary operator:
const Admin = () => (
<AdminLayout>
<style global jsx>{`
body {
background: #eff0f3;
}
`}</style>
<div className="jumbotron" style={jumbotron}>
{1 == 1 ? (
<h3>Please select a tool first.</h3>
) : (
<h3>Something is wrong :/ .</h3>
)}
</div>
</AdminLayout>
)
export default Admin

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!!!

is this node.js mustache partials functionality ideal?

I'm new to node and have been trying to find a way to integrate mustache in a way thats flexible for how I build templates. After trying hogan, mustache-express and the other permutations of mustache, I found the following to make sense to me.
Im not sure if there is a more efficient way of handling this. Basically I like to have control of partial templates so I can load them in module positions on a master template. So this is the basic setup
I have 3 partial templates and one wrapper/master template, the following values are just for example.
./views/wrapper.tpl
<html>
<title>{{ title }}</title>
<body>
<h1>{{ heading }}</h1>
<div>{{> position_1 }}</div>
<div>{{> position_2 }}</div>
<div>{{> position_3 }}</div>
</body>
</html>
./views/module_1.tpl
<h2>{{module_1.title}}</h2>
<p>{{module_1.body}}</p>
./views/module_2.tpl
<h2>{{module_2.title}}</h2>
<p>{{module_2.body}}</p>
./views/module_3.tpl
<h2>{{module_3.title}}</h2>
<p>{{module_3.body}}</p>
Im using Express but removed the default jade render engine. I only included the code that needs to be added to a default express install.
mustache and fs are needed
.
.
.
var mustache = require('mustache');
var fs = require('fs');
.
.
.
app.get('/', function(req, res){
// grab master template
var wrapper = fs.readFileSync('./views/wrapper.tpl', "utf8");
// load data that will be used in template & partials
var data = {
'title': 'dashboard',
'heading': 'welcome to your dashboard',
'module_1':{
'title': 'module 1 title',
'body': 'module 1 body'},
'module_2':{
'title': 'module 2 title',
'body': 'module 2 body'},
'module_3':{
'title': 'module 3 title',
'body': 'module 3 body'}};
// load partial templates
var partials = {'position_1': fs.readFileSync('./views/module_1.tpl', "utf8"),
'position_2': fs.readFileSync('./views/module_2.tpl', "utf8"),
'position_3': fs.readFileSync('./views/module_3.tpl', "utf8")}
// include partials and then replace variables with given data
var html = mustache.to_html(wrapper, data, partials);
// send output to browser
res.send(html);
});
.
.
.
.
This to me makes alot more sense then other examples I have seen, and it works well with mustache. I am able to give the user custom control as to where they want the modules to be positioned. So my questions are
Is there a more efficient way of doing this?
Is there anything wrong with the way Im going about this?
By bypassing Express rendering capabilities what am I missing out?
Why should I use libraries like consolidate and add another layer to express?
Thanks!
If you don't want express rendering, thats fine in my book, I even removed the whole module in some of my lab projects just to get a better understanding of routing in node.
However I'd really advice against using readFileSync since It's a blocking method and will make concurency a problem, always use readFile so you get that async callback.

Node.js, Express, Jade - Separate layout files

I'm working on some project with Node.js, Express and Jade, where I'd like to seperate layout files. Inside the main file is already separated header, but I don't know how to do this for sublayout where I need to pass data. In this case I need to pass data to widgets for every view on page, but in the route would be too many things to load data into widgets instead of some easy solution which I'm looking for.
I could do this thing on the way which I described above - to load data in view with every request, but this is somehow time & cpu consuming.
Another way I'm thinking of is to create some sublayout for widgets in which I'd load data once and then would be available all the time without calling data from DB in all requests. What's the best way to do that?
I work with mustache but I think you can use a similar strategy that I do.In most of the mustache templates that I use there is a common header and footer section.Along with the scripts and css files.I have created a separate partials file that exports these partials
.For instance my partial file looks like this.
exports.partials = function (isAuthenticated)
{
var menu;
isAuthenticated ?
menu = {
header: '',
footer: ' '
} :
menu = {
header: '',
footer: ''
}
return menu;
};
exports.staticResources = {
bootstrap :'//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css',
fonts : '//netdna.bootstrapcdn.com/font-awesome/3.0/css/font-awesome.css',
jquery : '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js'
};
I have another method called generatePartials which as the name suggest generate the partials for my templates
exports.generatePartials = function(isAuthenticated){
var menu = resources.partials(isAuthenticated);
var partials = {
header : menu.header,
footer : menu.footer,
bootstrap : resources.staticResources.bootstrap,
fonts :resources.staticResources.fonts,
jquery :resources.staticResources.jquery,
};
return partials;
};
Now while rendering the template all I have to do is this
app.get('/routeName',function (req, res){
var partials = require('../helpers').generatePartials(req.isAuthenticated());
return res.render('viewName.html', partials);
};
And that's it.

How do you post data to CouchDB both with and without using JavaScript

I have a show which displays a form with fields populated from a document. I'd like to change the values in the field and then save the updated document.
I'm having trouble finding a clear, concise example of how to do this.
Seriously, just finishing this example would work wonders for so many people (I'm going to leave a lot of stuff out to make this concise).
Install Couchapp
This is outside the scope of my question, but here are the instructions for completeness.
Create a couchapp
Again, this is kind outside the scope of my question. Here is a perfectly concise tutorial on how to create a couchapp.
Create a template
Create a folder in the root of your couchapp called templates. Within the templates folder create an HTML page called myname.html. Put the following in it.
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<form method='post' action='#'>
<fieldset>
Hello <input type='text' name='name' value='{{ name }}'>
<input type='submit' name='submit' value='submit'>
</form>
</body>
</html>
Create a show
See the tutorial above for hwo to do this.
Add this code to a show called myname.
function(doc, req) {
if (doc) {
var ddoc = this
var Mustache = require("vendor/couchapp/lib/mustache");
var data = {
title: "The Name",
name: "Bobbert"
}
return Mustache.to_html(ddoc.templates.myname, data)
} else {
return ('nothing here baby')
}
}
Update the document with a new name by ...
So who can complete this step via both the client side and the server side?
Please don't point me to the guide, I need to read it in your words.
Thanks.
Edit:
Although the return value isn't pretty, just posting a form to the update handler will update the document.
You will probably want to look into update handler functions.
An update handler handles granular document transformations. So you can take 1 form, that has one distinct purpose, and only update the relevant fields in your document via the update handler.
Your update handler will need to take a PUT request from your form. A browser can't do this directly, so you'll need some javascript to handle this for you. If you're using jQuery, this plugin can take your form and submit it seamlessly via AJAX using PUT for you.
Inside the function, you can take the fields you are accepting, in this case name and apply that directly to the document. (input validation can be handled via the validate_doc_update function)
Update Handler (in your Design Document)
{
"updates": {
"name": function (doc, req) {
doc.name = req.form.name;
return [doc, "Name has been updated"];
}
}
}
HTML
<form id="myForm" action="/db/_design/ddoc/_update/name/doc_id">...</form>
JavaScript
$(document).ready(function() {
$('#myForm').ajaxForm({
type: "PUT",
success: function () {
alert("Thank you");
}
});
});
Once you've gotten this basic example up and running, it's not much more difficult to add some more advanced features to your update handlers. :)

Resources