I wish to have a template render some data which needs to be passed back to the caller of render. For example, I am using a template to generate emails, for which I need a subject as well as the body. I would like to do something like this:
app.render( 'email', function(err,html) {
subject = ?get from template somehow?
postEmail( subject, html, user_addr );
});
That is, I wish for the template to decide what should appear in the subject (preferably without creating another template just for the subject line).
Not sure if you figured this out yet, but you can send back information from Jade by altering the value of the arguments.
email.jade:
- subject.text = "Hi " + user + ", welcome to the site.";
| Subject: #{subject.text}
app.js:
args = { user: 'Test User', subject: { text: '' } };
app.render( 'email', args, function(err,html) {
subject = args.subject.text;
postEmail( subject, html, user_addr );
});
It has to be a nested object (i.e. subject.text instead of simply subject), otherwise you won't get the modified data. Although, if you call templates created with jade.compile() directly, then the nesting appears to be unnecessary. I think express must make a shallow copy of the arguments before sending it to the view engine.
Related
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
Hi and I I am not a pain, I got help on sending a statement and that worked perfectly, but I need to send invoices with it but I can't find a method for that like I did for the statement. I find references to using a template file, then you have to store the file in the cabinet. I have to attach there to emails, what would you think the best way to do this?
Here is the other link, he gave a very helpful complete answer about sending statements. I should have asked him about invoices at the same time but I didn't think of it, apologies.
SuiteScript 2 can send pdf statements
added additional info
Ok I tried this but I am getting an error that doesn't make sense as I am using their templates.
"error.SuiteScriptError","name":"USER_ERROR","message":"Error Parsing XML: The reference to entity \"c\" must end with the ';' delimiter.",
require(['N/render', 'N/file', 'N/record'],
function(render, file, record) {
function renderRecordToPdfWithTemplate() {
var renderer = render.create();
renderer.setTemplateByScriptId("STDTMPLCUSTINVC");
var xml = renderer.renderAsString();
renderer.addRecord(record.Type.INVOICE, record.create({
type: record.Type.INVOICE,
id:415619
}));
var invoicePdf = renderer.renderAsPdf();
var foo = this;
}
renderRecordToPdfWithTemplate();
});
thanks for any help with this
Ok I found it thanks if anyone was looking for this for me. It is pretty straightforward, but like NetSuite always is, you have to find it. :)
the entityid is the invoice internalid the rest is easy, then just pass the file object to the email.
require( [ 'N/render', 'N/file', 'N/record' ],
function( render, file, record ) {
function renderRecordToPdfWithTemplate() {
var transactionFile = render.transaction({
entityId: 415619,
printMode: render.PrintMode.PDF
});
var foo = this;
}
renderRecordToPdfWithTemplate();
} );
thanks again
My Kentico server is unable to send e-mails, so I have to transform my e-mail using MacroResolver, but send it using some other way.
var clients = new List<Client>();
var macroResolver = MacroResolver.GetInstance();
macroResolver.AddDynamicParameter("clients", clients);
var emailMessage = new EmailMessage {
From = "someone#somewhere.com",
Recipients = "otherone#somewhere.com",
Subject = "Whatever"
};
var template = EmailTemplateProvider.GetEmailTemplate(templateName, siteName);
EmailSender.SendEmailWithTemplateText(siteName, emailMessage, template, macroResolver, true);
In other words, I would like to use Kentico just as a Template Engine. Is there anyway to achieve this?
What SendEmailWithTemplateText method basically does is it fills empty fields of message by its equivalent from a template and resolve macro values in it. If you are only after message body, then you can create the email message by:
emailMessage.Body = macroResolver.ResolveMacros(emailMessage.Body);
emailMessage.PlainTextBody = macroResolver.ResolveMacros(emailMessage.PlainTextBody);
For most scenarios it's also better to tell the resolver to encode resolved values. You can do it by: resolver.EncodeResolvedValues = true;
Also you are passing whole 'clients' collection to the resolver. You'll probably need to take it one by one and generate emails in a loop.
I'm trying to send an email from a FirefoxOS App to share content generated by it.
Currently I'm using:
var createEmail = new MozActivity({
name: "new",
data: {
type : "mail",
}
});
But I haven't been able to find any way of appending or attaching content to this email
Thanks to #sebasmagri answer I learnt that the "mailto" URI accepts many more fields than I knew about. Specially interesting is the body and subject:
mailto:someone#example.com?
cc=someone_else#example.com
&subject=This%20is%20the%20subject
&body=This%20is%20the%20body
This allows me to set the different parts of the email as I wanted to.
The final code looks like:
var body = encodeURIComponent(JSON.stringify(event.target.result));
var createEmail = new MozActivity({
name: "new",
data: {
type : "mail",
url: "mailto:?subject=FiREST%20Request&body=" + body,
}
});
It looks like you can set attachments through data.blobs and data.filenames, and misc content (to, subject, content) through data.URI.
Detauls about the mailto: syntax can be found in the MDN entry on Email links.
Regards,
Edit May 2014
As the mail app was refactored, I've dropped the old broken code link in favour of MDN docs.
I'm using mongodb to store application error logs as json documents. I want to be able to format the error logs as HTML rather than returning the plain json to the browser. The logs are properly schemaless - they could change at any time, so it's no use trying to do this (in Jade):
- var items = jsonResults
- each item in items
h3 Server alias: #{item.ServerAlias}
p UUID: #{item.UUID}
p Stack trace: #{item.StackTrace}
h3 Session: #{item.Session}
p URL token: #{item.Session.UrlToken}
p Session messages: #{item.Session.SessionMessages}
as I don't know what's actually going to be in the JSON structure ahead of time. What I want is surely possible, though? Everything I'm reading says that the schema isn't enforced by the database but that your view code will outline your schema anyway - but we've got hundreds of possible fields that could be removed or added at any time so managing the views in this way is fairly unmanageable.
What am I missing? Am I making the wrong assumptions about the technology? Going at this the wrong way?
Edited with extra info following comments:
The json docs look something like this
{
"ServerAlias":"GBIZ-WEB",
"Session":{
"urltoken":"CFID=10989&CFTOKEN=f07fe950-53926E3B-F33A-093D-3FCEFB&jsessionid=84303d29a229d1",
"captcha":{
},
"sessionmessages":{
},
"sessionid":"84197a667053f63433672873j377e7d379101"
},
"UUID":"53934LBB-DB8F-79T6-C03937JD84HB864A338",
"Template":"\/home\/vagrant\/dev\/websites\/g-bis\/code\/webroot\/page\/home\/home.cfm, line 3",
"Error":{
"GeneratedContent":"",
"Mailto":"",
"RootCause":{
"Message":"Unknown tag: cfincflude.",
"tagName":"cfincflude",
"TagContext":[
{
"RAW_TRACE":"\tat cfhome2ecfm1296628853.runPage(\/home\/vagrant\/dev\/websites\/nig-bis\/code\/webroot\/page\/home\/home.cfm:3)",
"ID":"CFINCLUDE",
"TEMPLATE":"\/home\/vagrant\/dev\/websites\/nig-bis\/code\/webroot\/page\/home\/home.cfm",
"LINE":3,
"TYPE":"CFML",
"COLUMN":0
},
{
"RAW_TRACE":"\tat cfdisplay2ecfm1093821753.runPage(\/home\/vagrant\/dev\/websites\/nig-bis\/code\/webroot\/page\/display.cfm:6)",
"ID":"CFINCLUDE",
"TEMPLATE":"\/home\/vagrant\/dev\/websites\/nig-bis\/code\/webroot\/page\/display.cfm",
"LINE":6,
"TYPE":"CFML",
"COLUMN":0
}
]
}
}
... etc, but is likely to change depending on what the individual project that generates the log is configured to trigger.
What I want to end up with is a formatted HTML page with headers for each parent and the children listed below, iterating right through the data structure. The Jade sample above is effectively what we need to output, but without hard-coding that in the view.
Mike's analysis in the comments of the problem being that of creating a table-like structure from a bunch of collections that haven't really got a lot in common is bang-on. The data is relational, but only within individual documents - so hard-coding the schema into anything is virtually impossible as it requires you to know what the data structure looks like first.
The basic idea is what #Gates VP described. I use underscore.js to iterate through the arrays/objects.
function formatLog(obj){
var log = "";
_.each(obj, function(val, key){
if(typeof(val) === "object" || typeof(val) === "array"){
// if we have a new list
log += "<ul>";
log += formatLog(val);
log += "</ul>";
}
else{
// if we are at an endpoint
log += "<li>";
log += (key + ": " + val);
log += "</li>";
}
});
return log;
}
If you call formatLog()on the example data you gave it returns
ServerAlias: GBIZ-WEBurltoken: CFID=10989&CFTOKEN=f07fe950-53926E3B-F33A-093D-3FCEFB&jsessionid=84303d29a229d1sessionid: 84197a667053f63433672873j377e7d379101UUID: 53934LBB-DB8F-79T6-C03937JD84HB864A338Template: /home/vagrant/dev/websites/g-bis/code/webroot/page/home/home.cfm, line 3GeneratedContent: Mailto: Message: Unknown tag: cfincflude.tagName: cfincfludeRAW_TRACE: at cfhome2ecfm1296628853.runPage(/home/vagrant/dev/websites/nig-bis/code/webroot/page/home/home.cfm:3)ID: CFINCLUDETEMPLATE: /home/vagrant/dev/websites/nig-bis/code/webroot/page/home/home.cfmLINE: 3TYPE: CFMLCOLUMN: 0RAW_TRACE: at cfdisplay2ecfm1093821753.runPage(/home/vagrant/dev/websites/nig-bis/code/webroot/page/display.cfm:6)ID: CFINCLUDETEMPLATE: /home/vagrant/dev/websites/nig-bis/code/webroot/page/display.cfmLINE: 6TYPE: CFMLCOLUMN: 0
How to format it then is up to you.
This is basically a recursive for loop.
To do this with Jade you will need to use mixins so that you can print nested objects by calling the mixin with a deeper level of indentation.
Note that this whole thing is a little ugly as you won't get guaranteed ordering of fields and you may have to implement some logic to differentiate looping on arrays vs. looping on JSON objects.
You can try util.inspect. In your template:
pre
= util.inspect(jsonResults)