Getting access to data in jade template (to make static html) - javascript

I am trying to pass some data to jade template to generate static content. I do not know much about node.js and express, I don't use them as backend. I use jade as a template engine and to generate static html.
There are lot of request in jade issue list about having multi line code, and the owner comments
I'd like to promote keeping too much js out of the templates, maps etc
can be exposed via locals
if there's a lot of logic or objects etc within a template you should
be using app.helpers() etc, they can still be view-only helpers but at
least it keeps the templates cleaner
I am not quite sure how to do this in a grunt based environment. Basically, if I can get access to javascript variables (which may be a text, js or json file) in my jade template, so I can use the data in my template and generate static html files. What is the best way to do this?
Edit
To clarify further, I may have data like (say in a json file)
user1 = {link: '', lbl: 'User', txt: '.... lot 0f text ....'}
user2 = {link: '', lbl: 'User', txt: '.... lot 0f text ....'}
in my mixin, somehow I need to access user1, user2 etc in my jade template
.content
+colum(user1 )
+colum(user2 )
mixin colum(d)
.span4
h4
| #{d.lbl}
p
| #{d.txt}
Thanks a ton.

If you want to do it with grunt-contrib-jade try the data option:
jade: {
compile: {
options: {
data: function(dest, src) {
// Return an object of data to pass to templates
return require('./userData.json');
},
},
files: {
"dest.html": ["templates/*.jade"]
},
},
}
Here are the docs on it: https://github.com/gruntjs/grunt-contrib-jade#data

You can render your data in jade with: #{your_variable}

Hope this helps: The jade public API https://github.com/visionmedia/jade#readme
Update: after play around for awhile, I got this:
var jade = require('jade');
// Compile a function
var fn = jade.compile('p= data');
console.log(fn({'data':'test'}));
When I ran this code, I got: <p>test</p>. So here is how jade can work:
jade.complie(jadeString) is a function to determine what string that jade have to complie - the jadeString parameter, you can use fs module to load the content of your jade template and place it here. fn(jsonData) is the function that actually complie the jade template to html, jsonData is the data you want to use in the template.

Related

How to call a function inside a Template literal in node js

I am using node js , express. I am trying to call a function in template literal.
I made a module and import in main file and then add the function to app.locals and accessing in my views that works fine. But when i try to use it in template literals and also in my public file it give me error "currencyToWords is not defined".
I think in public file it makes sense because i make public folder static.
but in my views it is weird because when i use it in html it works but when i use it in template literal it gives error.
I want to use the currencyToWords function in template literals to change the response.
I have one solution that i can make the file in public folder and add the function in script in that file and import in footer but i dont want to do that.
Can anybody help me in solving the issue.
My app stucture
module.exports = { currencyConverter }
In app.js
app.js is my main file where i create server
var { currencyToWords } = require('./config/functions');
app.locals.currencyToWords = currencyToWords;
by adding in locals i can access it in my application any where
This code is working:
<div class="fix pull_right">
<h3><%= currencyToWords(property.propertyDetails.price)%></h3>
</div>
This is not working
var a =`
<p>${currencyToWords(property.propertyDetails.price)}</p>
`;
the above code is same but only difference is first one is use in ejs tags and second in ejs and template literals.
Here is the documentation from express about app.locals
app.locals
The app.locals object has properties that are local variables within the application.
console.dir(app.locals.title)
// => 'My App'
console.dir(app.locals.email)
// => 'me#myapp.com'
Once set, the value of app.locals properties persist throughout the life of the application, in contrast with res.locals properties that are valid only for the lifetime of the request.
You can access local variables in templates rendered within the application. This is useful for providing helper functions to templates, as well as application-level data. Local variables are available in middleware via req.app.locals (see req.app)
app.locals.title = 'My App'
app.locals.strftime = require('strftime')
app.locals.email = 'me#myapp.com'

Using eval to dynamically render JSX served from backend

I'm working on a React frontend that gets data from a python JSON API. One section of my website has premium content and is reserved for paying users; currently I ensure that other users don't have access to it by fetching the content as a JSON object and converting it to JSX on the frontend according to a certain convention. For example:
{
{ 'type': 'paragraph', 'value': 'some text'},
{ 'type': 'anchor', 'href': 'some url', 'value': 'some description'}
}
would be rendered as :
<p>some text</p>
some description
Not surprisingly, things started to get pretty complicated as the content began to get more structured, simple things like making part of the text bold require a disproportional amount of effort.
As a potential solution, I had this idea: instead of sending the content as an object and parsing it, why not send a string of JSX and evaluate it on the frontend?
I started like this:
import * as babel from "#babel/standalone";
export function renderFromString(code) {
const transformed_code = babel.transform(code, {
plugins: ["transform-react-jsx"]
}).code;
return eval(transformed_code);
}
I imported this function in my premiumContent page and tried passing a complete component as a string (with import statements, etc) but got errors because the modules can't be found. I assumed this happens because the code is being interpreted by the browser so it doesn't have access to node_modules?
As a workaround, I tried passing only the tags to renderFromString and call it in the context of my component where all the modules are already imported :
import * as babel from "#babel/standalone";
export function renderFromString(code, context) {
const _es5_code = babel.transform(code, {
plugins: ["transform-react-jsx"]
}).code;
return function() {
return eval(_es5_code);
}.call(context);
}
This also failed, because it seems that eval will still run from the local context.
Finally, I tried doing the same as above but executing eval directly in my component, instead of from my function .This works as a long as I store "React" in a variable : import ReactModule from "react";const React = ReactModule, otherwise it can't be found.
My questions are:
Is there any way I can make my first two approaches work?
I know eval is considered harmful, but since the content is always completely static and comes from my own server, I don't see how this wouldn't be safe. Am I wrong?
Is there a better solution for my problem? That is, a way to safely deliver structured content to only some users without changing my single page app + JSON api setup?
The best solution for this is React server-side rendering.
Since you need markup that is client-side compatible but at the same time dynamically generated through React, you can offload the markup generation to the server. The server would then send the rendered HTML to the client for immediate display.
Here's a good article about React SSR and how it can benefit performance.

Pass an array as a parameter for view rendering in twig

I'm pretty new to Twig.js, and notice that it lacks some documentation. In fact, I could only find extremely basic usage information on their GitHub wiki.
My views rely on a bunch of variables. All views extend from layout.twig which includes navbar.twig. The last one takes a lot of parameters, like color, names and others I haven't implemented yet.
The problem with this is that I would need to pass lots of variables every time a view gets rendered. So I thought a solution would be to pass an array each time, instead of multiple fieds. My question comes there, as in how I'd interact with this array
My current and inefficient solution can be represented when displaying an error:
res.render('error', {title: appConfig.name, color: appConfig.color (...)});
It would be better if I could pass an array and then interact with it inside of my twig view.
I'm open to other solutions. Being able to access appConfig from inside the view would be one of them, although I don't think that is possible. If it this though, please tell me how! I'll thank you forever.
Thanks in advance.
Pass appConfig:
var appConfig = { name: 'Jeremy', color: 'green' };
res.render('home', appConfig );
Render it:
<div class="{{ color }}">Hello {{ name }}</div>
Twig can work just fine with nested objects too:
var navConfig = { color: 'salmon' }
res.render('home', { nav: navConfig, name: 'Arnold' })
<nav class="{{ nav.color }}">{{ name }}</nav>
You can simply parse appConfig to JSON Object using JSON.parse.
Note - JSON.parse can tie up the current thread because it is a synchronous method. So if you are planning to parse big JSON objects use a streaming json parser.

How do I deep compile nested handlebars content?

A project I'm working on uses Handlebars.js template system. It reads in content and when compiling the template injects the content where appropriate:
<div class="content">
<p>lorem ipsum</p>
{{{ content }}}
</div>
In this case the handlebars is compiled with a JS object who has a content property that is a string of text or HTML (hence the triple brackets).
However it is entirely possible that the content value (being text or HTML) could also include handlebars interpolation code:
var contentPassedToHandlebars = {
content: '<p>{{ foobar }}</p>',
foobar: 'foo'
};
Which currently outputs <p>{{ foobar }}</p> but what I would intend to get is <p>foo</p>.
Does handlebars have a facility for this nested content or is a custom helper required? ({{{custom_parse content}}})?
For context to this question
The situation derived from a build system (metalsmith) that reads in files as markdown, converted them to HTML, attach result to the content property of the file object, then parse a handlebars template which injects the file.content into it output. All this and I was hoping there was a solution to place handlebars or string interpolation into the markdown so the markdown files could have access to the same variables the templates have access to (obviously more global values in a config.json not the values associated with the file object being constructed).
There is no built in way to do this. You would have to manage you own pre-rendering process or internal helper.
For the solution I ended up running a pre-render before the official render. Although the code is not handlebars but instead part of the metalsmith-templates plugin, here is the solution I used.
This roughly translated to:
var contentPassedToHandlebars = {
content: '<p>{{ foobar }}</p>',
foobar: 'foo'
};
var x = Handlebars.compile(contentPassedToHandlebars.content)(contentPassedToHandlebars);
contentPassedToHandlebars.content = x;
Handlebars.compile(realTemplateSource)(contentPassedToHandlebars);
OR use this:
metalsmith-in-place
```
index.js
var inPlace = require('metalsmith-in-place')
....
.use(inPlace(true))
.....
Now, if you
write {{> footer }}
it will do the work.

Django - Append a reusable template from javascript

I'm currently reusing a template to populate a list using the code below:
<li>
{% include "ding/search_result.html" with title=field.title url=field.url description=field.description %}
</li>
When new data comes in, I want to be able to add new elements to my list dynamically from javascript. I can use the code below to add new list items:
$("ul.search-results-list").append('<li>New List Item</li>')
Is there a way for me to append the reusable template code I'm using, and pass in the appropriate parameters?
Yes there is! AND you get to refactor your APIs as well.
Imagine that you have an API that returns this json blob for every new list item.
{
name: "limelights"
}
And let's just use jQuery for this (even though I kinda don't want to.)
So you query the API like this
$.getJSON('api/v1/getName.json', {'someParameter', 123}, function() {
//this is where you would do your rendering then.
});
So how to do the rendering? Well, I'm a huge fan of underscore.js and it's rendering of templates, so I'm gonna go ahead and use that (but you can exchange it for Mustasche, Handlebars, Hogan or whatever you fancy).
First, we'll define a script template like this: (We're using underscore.js own ERB style template language).
<script type="text/template" id="my_fancy_list">
<li><%= name %></li>
</script>
Now this can live pretty much anywhere, either in your template or you can require it in if you're using Require.JS but we're gonna assume that it lives in your template (Hint: it could be rendered by some Django template tag)
Anyhoo,
Fill this template with data now:
var template = _.template($('script#my_fancy_list').html());
//this will give you the template as a javascript function.
//Query the API
$.getJSON('api/v1/getName.json', {'startsWith': 'l'}, function(data) {
$('ul.search-results-list').append(template(data));
});
And presto, you now have an JavaScript driven application that renders new data that comes from the server without fetching the entire template and render it all again.

Categories

Resources