My collection contains escape HTML entities such as <, > etc. when i retrieve and display it, it escapes and prints the respective characters but does not interpret the HTML.
Eg:
Template.Help.helpers({
faq_content: function () {
var x = Help.findOne({});
return x.faq;
}
})
assuming x takes the following value:
x =
{
faq: <div class=\"help_articles\">\\n
....some_content....\\n
</div>
tips: <div class=\"help_articles\">\\n
....some_content....\\n
</div>
articles: <div class=\"help_articles\">\\n
....some_content....\\n
</div>
}
Even when i place this helper using three curly braces i.e,
{{{faq_content}}} ,
The content that gets displayed on the Browser is
<div class="help_articles">....some_content....</div>.
This gets displayed as text but i want it to be interpreted into HTML.
Please suggest some solution
Looks like you need to _.unescape() the string once before presenting inside the {{{}}} braces:
Template.Help.helpers({
faq_content: function () {
var x = Help.findOne({});
return _.unescape(x.faq);
}
})
Be careful that the FAQ content is fully controlled by you and not submitted by your users without cleansing the content.
Related
I'm trying to return HTML from a helper function after certain logical conditions are met. However, using the Spacebars.SafeString() function doesn't seem to be working and I gather that using this method to return HTML is unsafe and is prone to code injection from external sources.
For example, returning this HTML:
<object data="/rating/what.svg" width="20" height="20" type="image/svg+xml"></object>
Set up like this:
Spacebars.SafeString("<object data=" + "/rating/what.svg" + "width='20' height='20' type=" + "image/svg+xml" + "></object>");
Can anyone guide me to the best way to return HTML from a helper and how to do such a task? Couldn't find a definitive answer anywhere else.
First of all, if your requirements allow it, don't return HTML at all, just use a template and populate it with a data context, for example:
in your template:
<template name="someHtml">
<object data="/rating/{{dynamicName}}.svg" width="20" height="20" type="image/svg+xml"></object>
</template>
in the corresponding helper:
Template.someHtml.helpers({
dynamicName: function() {
return 'what'; // obviously, you generate this with some code
}
})
But, if you truly must use html content to be printed, you can use one of the two most popular sanitization packages, either djedi:sanitize-html-client or vazco:universe-html-purifier
With the first:
cleanHtml = sanitizeHtml(dirtyHtml, {
allowedTags: [ 'b', 'i', 'em', 'strong', 'a' ],
allowedAttributes: {
'a': [ 'href' ]
}
});
and with the latter:
cleanHtml = UniHTML.purify(dirtyHtml, {
withoutTags: ['b', 'img'],
noFormatting: true
});
And then of course you include the return value of these in your template with either triple braces so that the HTML is not escaped.
You are correct in that Spacebars.SafeString() can return unsafe code. Your best bet is to strip out bad tags or injections that you do not want and either use Spacebars.SafeString() or triple brace syntax like below:
<div>
{{{htmlHelper}}}
</div>
htmlHelper: function() {
return "<img src=..."
}
I'm aware that you can turn off reactivity with reactive: false while fetching collections. How to accomplish the same with collection fields within, say, a contenteditable area? Example:
Template.documentPage.events({
'input .document-input': (function(e) {
e.preventDefault();
content = $(e.target).html();
Documents.update(this._id, {
$set: {
content: content
}
});
}
});
<template name="documentPage">
<div class='document-input' contenteditable='true'>{{{content}}}</div>
</template>
to turn of reactivity of e.g. helpers or other reactive functions wrap your function with Tracker.nonreactive(fn). See: http://docs.meteor.com/#/full/tracker_nonreactive
you could set the reactive data to an attribute and move it to the html in onRendered
<template name="documentPage">
<div class='document-input' contenteditable='true' data-content='{{content}}'></div>
</template>
Template.documentPage.onRendered(function () {
var doc = this.find(".document-input");
doc.innerHTML = doc.dataset.content;
});
That way the data-content attribute will be updated by blaze on input not the text of the element.
SECURITY NOTE: I changed the template to use {{content}} instead of {{{content}}} so it can be escaped by meteor but used doc.innerHTML so the content will be "unescaped" in the element. Any time you are rendering html from user input you should make sure you are sanitizing it so you don't open an xss vulnerability in your site.
The alternative would be to use a library like https://github.com/UziTech/jquery.toTextarea.js to change the editable content of a div to text and use doc.textContent in place of doc.innerHTML to remove any chance of xss
Some properties of my model should be loaded asynchronously (implemented by promises). I don't want to wait it - I want to render it right now and partially update it when promises will be resolved. Does handlebars.js support it?
Handlebars does not support async. This makes it impossible to use Promises for expressions and helpers. You could use await but this suspends execution, so no rendering takes place until the Promise is settled. Other templating frameworks like dust.js offer async functionality.
I think i can propose a workaround to get the async (or promises) to (seem to) work. Below is the example. Essentially this is what i am doing
First return a div with an unique Id (i am using namespace + auto increment here)
Do your processing (ajax or what ever slowly). Once done replace the innerHTML of the div in step1 with new data by accessing it via the unique id
code below. Observe the tripple bracket ({{{ }}}). This is to make sure generated HTML is not escaped
<script id="handlebars-demo" type="text/x-handlebars-template">
<div>
-->> {{{functionName "functionParam" }}} <<--
</div>
</script>
<div id="output" />
<script>
window.id=0;
Handlebars.registerHelper('functionName', function(funcParam ){
let tempId = "my_namespace_" + window.id++;
$.post("https://something.com/abc").done(function(data){
$("#"+tempId ).html(data.something);
}
)
return '<div id='+tempId+'>loading</div>'
});
var template = document.getElementById("handlebars-demo").innerHTML;
var templateScript = Handlebars.compile(template);
var context = { };
var html = templateScript(context);
document.getElementById("output").innerHTML= html;
</script>
Below I have a basic template that has a numerical input form. When you type a number in the form and click Add a list of Divs get created. The Divs are created with a class of "synth" and an id of "synth" + a number. The numbers go in succession based on a counter.
I want to not only store this information in the database but do so in a manner that (eventually) when a user logs in they will have access to their list of Divs as a "saved state" from their previous log in.
I am not even sure if I am going about this in an appropriate manner. I am simply sticking the createSynth() function in the Collection insert for lists. I have a feeling to do this "correctly" I should have two events that work in parallel - one sending to the lists Collection and the other to the dom/Template. These two blocks would then exchange data (some how) which in conjunction create the illusion of a "saved state".
Below is the code I have thus far.
HTML
<head>
<title></title>
</head>
<body>
{{> start}}
</body>
<template name="start">
<input id ="amount" type ="number" />
<input id ="submit" type="button" value="Add" />
<div id="applicationArea"></div>
</template>
Javascript
var lists = new Meteor.Collection("Lists");
var counter = 0;
counterSynth = 0;
if (Meteor.isClient) {
'use strict';
Template.start.events({
'mousedown #submit' : function () {
var amount = document.getElementById("amount").value;
for(i=0;i<amount;i++) {
lists.insert({SoundCircle:createSynth()}); // I am inserting the entire function call, is this the right path?
}
function createSynth() {
var synth = document.createElement("div");
synth.className = "synth";
synth.id = "synth" + (counterSynth++);
applicationArea.appendChild(synth);
};
},
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
You have to use a slightly different approach to this, basically just insert your stuff into the collection, and use handlebars to get it out. I'm not entirely sure what you were doing but you should get a good idea with the below
Server js
synths = new Meteor.Collection('synths'); //This will store our synths
Client js:
synths = new Meteor.Collection('synths'); //This will store our synths
Template.start.events({
'mousedown #submit' : function () {
var amount = document.getElementById("amount").value;
for(i=0;i<amount;i++) {
lists.insert({class:"synth", id:counterSynth});
}
},
});
Template.start.synth = function() {
return synths.find(); //This gives data to the html below
}
HTML:
{{#each synth}}
<div class="{{class}}" id="synth{{id}}">
Synth stuff here
</div>
{{/each}
It's probably best to dynamically recreate the DIVs every time you need them on the client, so the DIV is not stored on the server. If you really want to hard code/store the DIV on the server you would need to simply save the HTML as a string, to a Meteor collection.
So I'm trying to display via a template some data from a JSON request. The data has some nested objects (of varying amounts) similar to this:
data: "Some data",
nested: [
{
nested_data: "foo",
bar: "foobar"
}, ...
],
...
I've managed to parse the JSON fine and store it in a WinJS.Binding.List object, and bound the result to a template. The problem I've got is actually displaying the nested JSON data in my template. The template looks something like this:
<div class="appTemplate">
<div class="innerTemplate">
<h1 data-win-bind="innerText: data">
<h2 data-win-bind="innerText: nested">
</div>
</div>
When I run the program those, the 'nested' part of the template is just a bunch of [object Object] rather than the data I want to display.
Is there some way I can create the template such that it can handle the nested data? Would it be easier to define a template function? Not sure what to do here...
There is no built in way to do this -- the only built in control for iterating over data is the List View. However, you cannot nest List Views.
There are two ways to solve this problem, depending on your desired outcome:
1) Stringifying the nested data: You can do this by writing a WinJS.Binding.converter that will convert your array of data into a single string value. Example
Code:
WinJS.Namespace.define("Examples.Converters", {
nestedDataConverter: WinJS.Binding.converter(function(input) {
// Assume input is array
var result = "";
input.forEach(function(data) {
result += data.nested_data + ", " + bar + ";";
});
result result;
}),
});
Template:
My recommended solution would to build your own control that will take your array (or WinJS.Binding.List) and create the elements / layouts needed. I have done this in a project I work on, and it's super simple.
Example Template:
<div class="appTemplate" data-win-control="WinJS.Binding.Template">
<div class="innerTemplate">
<h1 data-win-bind="innerText: data">
<h2 data-win-bind="innerText: nested Examples.Converters.nestedDataConverter">
</div>
</div>
Now, the h2 will have the single-string version of that data.
2) Create a control to display the data richly: To do this you need to create a new WinJS control and use it in your template.
Control example:
WinJS.Namespace.define("Examples.Controls", {
Stamper: WinJS.Class.define(function(element, options) {
WinJS.UI.setOptions(this, options);
this.domElement = element;
}, {
domElement: nullm
_data: null,
data: {
set: function(val) {
this._data = val;
updateLayout();
}
},
updateLayout: function() {
this.domElement.innerHTML = "";
if(!this._data) {
return;
}
this._data.forEach(function(item) {
var el = document.createElement("div");
el.textContent = "Data: " + item.nested_data + ", " + item.bar;
this.domElement.appendChild(el);
}.bind(this));
}
}),
});
Template:
<div class="appTemplate" data-win-control="WinJS.Binding.Template">
<div class="innerTemplate">
<h1 data-win-bind="innerText: data"></h1>
<div data-win-control="Examples.Controls.Stamper"
data-win-bind="winControl.data: nested"></div>
</div>
</div>
This control can be extended to render nested templates, and other items. It's all a question of how complex you want to get.
Try data-win-bind="innerText: nested.nested_data"
This will be much easier with a template function.
The built in bindable templates are great for templates that are logicless, but fall short when you need the template to make decisions based on data values, and in your case, deeper properties.