Scope of exported variable in express and jade - node.js

I'm having a few issues with Jade and scope of variables exported from the route. This might be an obvious answer, but my googling abilities have failed me.
In my route, I have this code :
res.render('index', {title: "App",
csvData: json // This is a json object
};
In my view, I want to display the length of the json object on the click of a button. My jade looks like this :
extends layout
block content
script
-var test123 = csvData;
-console.log(test123.length);
div
button.btnCSV(onclick='console.log(test123)') Save As CSV
The first console.log prints the correct length, but when I do press the button, it tells me that test123 is undefined. I think this has something to do with the difference between client side/server side variables. If that is the case, is there anyway to make a server side variable accessible to a client side scope?

I'm not sure how your example would work with the script content prefixed with a -, this indicates unbuffered code. JavaScript that runs server side and that produces no direct output, so your in-line script is most likely empty.
Similarly your onclick handler is just compiling a string on the server, which is the main problem you appear to report.
In order to achieve what you trying to do, you should define a function in the script block which can be called from your buttons onclick handler. Take care to ensure that your script keyword ends with a . so that the following lines are treated as block content of the script.
Here's what your template should look like.
extends layout
block content
script.
var test123 = !{JSON.stringify(csvData)};
function printLength() {
console.log(test123.length);
}
div
button.btnCSV(onclick='printLength()') Save As CSV
Then on the server side make sure you're returning an actual JavaScript object, or an Array, and not a string representation... it should look like this
res.render('index', {title: "App",
csvData: [{ val1: 'value1', val2: 'value2' }]
};
This allows variables to be used for server side scripting (if required) as well as client side.

Related

get values from client side to server side (node.js + express.js)

I'm still trying to understand the concepts of node.js so please don't blame me if this is a dumb question..
In node.js, is it possible to get a value from index.jade to index.js?
For example:
index.jade
a(href="/bla" name="someName") Blabla
index.js
router.get('/bla', function(req, res){
//get value of name ("someName") or string ("Blabla")
console.log(req.body.name) ??
});
If this is not possible, I would like to know why...
Thanks.
No, it's not possible, for the simple reason that the name attribute in your HTML doesn't get passed to the server (any server, not necessarily a Node-based server).
If you want to pass a value in a GET request, you generally pass it as part of the URL:
a(href="/bla?name=someName") Blabla
This will generate the following HTML:
Blabla
In your server code, you can access the value using req.query.name.
Taking this a step further: if you have a variable available to your template called "name", you can use something similar, but a bit more dynamic:
a(href="/bla?name=" + encodeURIComponent(name)) Blabla
encodeURIComponent makes sure that any "special" characters (that may have a special meaning in URL's) will be encoded properly.

node, express, jade: How to create select options from a hash

I've just started to play around with node, express and thus, jade. I try to do something like this in a jade template:
my-options = {"this": "something", "that": "something else", "those": "more stuff", "these": "also included"}
form
select(name="myselection")
each option, key in my-options
option(value="#{key}") option
However, all I get is an 500 server error which is not helpfull at all. What am I doing wrong here? Any help would be much appreciated.
Update
I've just figured out, that jade templates themselves cannot contain variable definitions. Thus, the my_options hash needs to be defined in the corresponding router.js file and passed as an parameter to the appropriate result.render() function.
Your first line must be executable JS code and 'my-options' is not a valid JS variable name.
Actually, you can define variables in Jade, you just have to start the line with a hyphen. A - at the start of a line tells Jade to execute the code that follows without outputting it. A = at the start of a line tells Jade to execute the code that follows and output it after it's done.
So for example if you do
- var foo = "bar"
= 2+2
p #{foo}
you will see this in your page source:
4
<p>bar</p>

node.js express.js Error handling in View

I am very new to node.js and express.js and in programming concepts. I already made a basic MVC modeled app on node and express.
My problem is how do you handle error, I got this following code:
exports.submitBloodRequest=function(kaiseki,resView,request){
var params = {
bloodCenterId:request.session.centerID,
bloodTypeId: request.body.bloodType,
requestQuantity:request.body.numberOfDonors
}
kaiseki.createObject('blood_center_request', params, function(err, res, body, success) {
if(success){
resView.redirect('/bloodRequest')
}else{
//WHAT TO DO HERE?
}
});
}
Kaiseki is just a middleware for parse.com, I don't know what to do if it got error. Usually I use ajaxForm.js to look for BadRequest then use javascript to display error message in my view.
I want my error to appear in the same page, where it is success, should I pass a json error to my view?
Or still use ajaxForm.js and instead of res.render or res.redirect I should use res.status(500)
Is there anyway to handle the error and showing it into the view. Without using any javascript to detect BadRequest?
And can a view have a optional variable? In my view If I didnt pass any value on it it gives me error like if i have #{variable} it asks for its value. Can it be made to be optional? Im using Jade Template
To respond to an XHR request with an error you can do something like return resView.status(500).send(err); which will send the err object back as JSON. If you want to render an HTML error page instead you can do return resView.status(500).locals({err: err}).render('/errorPage');
You didn't say which template engine you are using but most likely the #{} version will automatically escape HTML characters for you (turn < into <, etc) to avoid XSS attacks and rendering problems whereas !{} will render the contents of the variable directly without escaping, which is dangerous if the variable contains any user-generated content, but necessary if the variable has HTML you want rendered by the browser.

How to transfer data between the content scripts of two different tabs?

In my extension I need to transfer some data from one tab's content script to another tab's content script. How can I choose certain tab using chrome.tabs, if I know a part of that tab object's name or url in it? How can two tabs' scripts communicate?
UPDATE:
Apparently I don't have method sendMessage in chrome.extension. When I run the following from content script:
chrome.extension.sendMessage("message");
I get in console:
Uncaught TypeError: Object # has no method 'sendMessage'
First, note that messages passed within an extension are JSON-serialized. Non-serializable types, such as functions, are not included in the message.
Within a content script, you have to pass the message to the background page, because there is no method to directly access other tabs.
// Example: Send a string. Often, you send an object, which includes
// additional information, eg {method:'userdefined', data:'thevalue'}
chrome.extension.sendMessage(' ... message ... ');
In the background page, use the chrome.tabs.query method to get the ID of a tab. For the simplicity of the example, I've hardcoded the patterns and urls. It might be a good idea to include the query values from the previous message, in this way: {query:{...}, data:...}.
// background script:
chrome.extension.onMessage.addListener(function(details) {
chrome.tabs.query({
title: "title pattern",
url: "http://domain/*urlpattern*"
}, function(result) {
// result is an array of tab.Tabs
if (result.length === 1) { // There's exactely one tab matching the query.
var tab = result[0];
// details.message holds the original message
chrome.tabs.sendMessage(tab.id, details.message);
}
});
});
chrome.tabs.sendMessage was used to pass the original data to a different tab.
Remark: In the example, I only passed the message when the query resulted in one unique tab. When uniqueness is not a prerequisite, just loop through all resulting tabs, using result.forEach or:
for (var i=0, tab; i<result.length; i++) {
tab = results[i];
...
}
From a content script you only can communicate with the background process.
You may communicate between two content scripts in differents tabs using the backgound as intermediate.
Also the DOM provide other way to communicate between DOM windows, but with the same origin policy...
To get a tab's url, you may execute a content script on it. The content script can get the url using window.location.href

Passing raw Markdown text to Jade

I'm playing around with my first Node.js Express application, and as every programmer knows, the first thing you should build when testing out a new framework is a blog! Anyway, I'd like to write the articles in Markdown and then render it in the view. I saw that Jade allows for this to be done inside the view itself, using filters, but I can't get that working.
To simplify the situation, here's an example of what I'm talking about.
//app.js
res.render("article", {
md : "Hello World!\n\n*Woo*"
});
//article.jade
section
:markdown
#{md}
But, that outputs this: <section><h1>{md}</h1></section>... it isn't substituting in the variables I've passed to it.
Then I tried this:
//article.jade
section
:markdown
!{md}
And the output is this:
<section><p>Hello World!
*Woo*</p></section>
So, now it's not parsing the markdown!
I have been able to get this to work by parsing the markdown in the app.js file and then passing the HTML to the view to display, but I don't know, that seems a bit messier.
Is there a way to pass variables into Jade filters?
You can do this with a function passed in to jade from node:
var md = require("node-markdown").Markdown;
Then pass it into the view as a local:
res.render('view', { md:md, markdownContent:data });
Then render it in the jade view by calling the function:
!= md(markdownContent)
The node module node-markdown is deprecated. The marked is advanced new version. You can try like this
var md = require('marked');
Inside your router
res.render('template', { md: md });
Inside your jade template
div!= md(note.string)
I don't think jade can do this out of the box. One way to accomplish it that might feel slightly cleaner than pre-rendering the markdown is to create a helper function called markdown that takes a markdown string and returns HTML. Then you could do something like
section
!= markdown(md)
The markdown function should be included in the locals data when you render the jade template and can directly use a markdown library to convert the markdown syntax to HTML.
If you are using Scalate's Jade support you can enter:
section
:&markdown
#{md}
You can also import external files with:
section
:&markdown
#{include("MyFile.md")}

Resources