How do I call function from jade client side java scripts - node.js

I have two drop down lists in my form. Once user selected value from first list I want to filter second list as per selection and display. For that I am using onchange="showSubCat(); of first select option to get selected value. showSubCat() function is defined in client side js file. How do I get value returned by showSubCat() function in jade template so that I can filter array which is populating second list.
var subcode = showSubCat();
gives error.
Any suggestions?
Thank you.

New to Jade myself but as far as I can tell you have two options:
1) create the function in jade itself:
-function sayHi(name){
- return "hello "+name
-}
p= sayHi('bill')
Which I think muddies up your code a bit.
2) A better option would be to pass in the function from the model
app.get('/', function(req, res){
res.render('home', {
title: 'Home'
, fs: { sayHi:function(name){
return "hello "+name
}}
});
});
Then in your jade file you just:
p= fs.sayHi('bill')

Related

Persist data sent to a Pug template via render

I'm trying to find out how I can persist the data I pass to my Pug template from the Express render method.
I pass in some JSON data to the res.render() method in Express that renders my view with Pug. On the Pug template, I use that data immediately to populate one of my select elements with drop down values from the JSON data.
What I want to then do is store this data that was passed so I can use it in an event handler function I create for another field.
Basically, I'm passing a table name and the field names for the table, but for each table I have in the JSON data.
So the shape is like [{ tableName: "table name here", fieldNames: ['field1', 'field2', ...] }, ... ]
I have a select field for "choose a table name" and when the user picks a table name, I then want to get the fieldNames for a second select field that allows them to choose a field name to use. So I have an event handler setup on the "choose a table name" field that runs a little event handler I have setup in the pug template. Only problem is the event handler does not have access to the data that was passed to the Pug template originally.
I'm trying to google this but having no luck finding anything, so does anyone know how I can persist data sent via the res.render() method in a pug template for using after the page has been rendered inside an event handler or other functions?
Thank you!
Always be clear what is done at the server (pug) and what is done in client Javascript (browser).
While data passed to pug scripts are meant to be consumed at the server, it is possible to inject, for want of a better word, server data into client side Javascript variables.
The following creates two dropdown lists on the same page using the exact same data passed by Express. One is generated at the server, while the second is created entirely by Javascript running in the browser.
Express code:
app.get("/testdata", (req, res) => {
res.render("testdata", { data: [ 1, 2, 3, 4, 5]});
});
testdata.pug:
html
head
body
p Dropdown list generated at the server:
p
select
each n in data
option(value=n)=n
br
p Dropdown list generated in browser Javascript:
p
select#dropdown
script.
document.body.onload = () => {
const dropdown = document.getElementById("dropdown");
let data = JSON.parse(`!{JSON.stringify(data)}`); // line 18
data.forEach(d => {
const item = document.createElement("option");
item.innerText = d;
dropdown.appendChild(item);
})
}
I have used the same variable name in totally different contexts pointing to different entities. Be careful not to trip. For example, look at line 18:
let data = JSON.parse(`!{JSON.stringify(data)}`); // line 18
The first instance of data is a Javascript variable in the browser.
The second instance of data is a server object passed to the pug script in the render method. Basically any !{expression} instances found in a pug file are evaluated¹ when the view is rendered.
¹ I think the expression is evaluated and its toString method called. If I know it is an array, I could have used:
let data = [!{data}]; // line 18

how can i pass a <br/> inside a string from expressjs to pug template

I have this code in express and i what to break a line in middle of text or put an image etc..
app.get('/', (req, res) => {
obj = {
text: `some text <br/> more text`
};
res.render('index', obj);
});
Imagine I fetch the info from a database and inside the information i have some html tags
what is the best approach in this case to perform the desire result (break a line).
On the .pug side i have this code:
p
| #{obj.text}
The output is 'br' printed like text and not perform a break.
Thanks for your time :)
To output unescaped variable value use !{varname} instead of #{varname}. In your case it should be:
p
| !{obj.text}
In this case, if the obj.text is the only value for the p
p!= obj.text
Unescaped buffered code can be dangerous. You must be sure to sanitize any user inputs to avoid cross-site scripting (XSS).
https://pugjs.org/language/code.html

Jade Cannot read property 'length' of undefined in each loop

I have this code in my controller retrieving an object from mongo and sending it to the client:
index: function(req, res){
List.find({user:req.session.user.id}).exec(function foundLists(error, foundLists) {
if(error) {
return res.json({error:error});
} else {
return res.view({ title:'Lists',lists:foundLists });
}
});
}
In my view I do the following:
extends ../layout
block content
.container
p #{lists}
Which renders:[object Object],[object Object]
If I do p= JSON.stringify(lists)
It renders:
[{"user":"546109c0d640523d1b838a32","name":"third","createdAt":"2014-11-11T19:39:36.966Z","updatedAt":"2014-11-11T19:39:36.966Z","id":"546265f83e856b642e3b3fed"},{"user":"546109c0d640523d1b838a32","name":"forth","createdAt":"2014-11-11T19:42:09.268Z","updatedAt":"2014-11-11T19:42:09.268Z","id":"546266913e856b642e3b3fef"}]
I'm trying to achieve:
#lists
each list in lists
p #{list}
But I get this error:
Cannot read property 'length' of undefined
I'm using Sails and Jade 1.7.0
You have an array of objects, so if you do each list in lists then list is an object. I assume Jade wants a string. If you put p #{list.name} or something similar that should work.
If you want to show everything you can try nesting your loops like
each list in lists
each item in list
p #{item}
This error can occurred when at least one of the items in the lists does not have a list property. You can protect this using an if statement:
each val in lists
if val.list
each item in val
p #{item}

jade (for nodejs) substrings in templates

Would anyone please advise how in jade for nodejs I can truncate a string to a number of characters/words, ideally conscious about the HTML markup within the string?
This should be similar to Django's truncatechars/truncatewords and truncatechars_html/truncatewords_html filters.
If this doesn't exist in jade, which way is right to go? I'm starting my first nodejs+express+CouchDB app, and could do it within nodejs code but it seems that filters are much more appropriate.
I would also consider writing a filter like this (and others) if I knew how :))
Just a quick illustration:
// in nodejs:
// body variable comes from CouchDB
res.render('home.jade', { title : "test", featuredNews : eval(body)});
// in home.jade template:
ul.thumbnails
each article in featuredNews.rows
a(href="#"+article.slug)
li.span4
div.value.thumbnail
img(align='left',src='http://example.com/image.png')
p!= article.value.description:truncatewords_html(30)
So I've made up the truncatewords_html(30) thing to illustrate what I think it should be similar to.
Will appreciate any ideas!
Thanks,
Igor
Here is a little "truncate_words" function:
function truncate( value, arg ) {
var value_arr = value.split( ' ' );
if( arg < value_arr.length ) {
value = value_arr.slice( 0, arg ).join( ' ' );
}
return value;
}
You can use it before sending the string to the template, or in the template using a helper method.
cheerio is a nice little library that does a subset of jquery and jsdom. Then it's easy:
app.helpers({
truncateWords_html : function(html, words){
return cheerio(html).text().split(/\s/).slice(0, words).join(" ")
}
})
Then, in a jade template use:
#{truncateWords_html(article.value.description, 30)}
This looks like a generic way to add any filters, hurray! :))

Appending Text to the body field in Drupal

I am trying to append a string to the body field of a CCK node after it has been submitted or edited. However, I'm having trouble working with the body field in the form alter. My initial attempt was to modify the body field in the submit handler by using the .operator to append a string to the body field.
//Calling this submit function to add string to body.
function appendToBody_submit_function($form, &$form_state) {
$form_state['values']['body'] = array('0' => array('value' => $form['#body'])) . $stringToAppend;
}
However, I can't get this to work, and I'm not sure it's the right way. I am new to Drupal, Can someone point me in the right direction? Should I be using node_api for this?
I assume that you add your custom submit callback to the forms #submit array via hook_form_alter().
If you add it before any other entry in that array (as opposed to just appending it), your callback should be called before the standard submit function. That way, all you need to do is adjust the $form_state['values']['body'] content 'in place', and it will be picked up (and subsequently saved) on further processing by the standard submit callback implicitly:
/**
* Implementation of hook_form_alter()
*/
function yourModule_form_alter(&$form, $form_state, $form_id) {
// Is this a node edit form?
if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
// Yes, add custom submit handler *before* already existing ones
array_unshift($form['#submit'], 'appendToBody_submit_function');
}
}
// Custom submit function to add string to body.
function appendToBody_submit_function($form, &$form_state) {
$form_state['values']['body'] = $form_state['values']['body'] . $stringToAppend;
}
I recommend installing the Devel module so you can easily print out the contents of $form_state by placing dpm($form_state); in your method body. I usually start with that to make sure the values are where/what I expect.
// Replace "hook" in the function name with the name of your module.
function hook_submit($form, &$form_state) {
// dpm($form_state); // Debug code to view the contents of $form_state.
$body = $form_state['values']['body'] . ' new string to append';
// Place code to save this data to your database here.
}

Resources