I've just started using handlebars.js in attempt to move away from rendering dynamic data the ugly way using string concat and injection. I am trying to separate the template script from the main HTML file and render the template file via a function call. Here is what I've got:
script.js
----------
$(function() {
var myData = { requests: [
{id: "1", firstname: "Roger", lastname: "Jones", age: 24},
{id: "2", firstname: "Phillip", lastname: "Green", age: 44}
]};
$.ajax({
url: 'templates/requests.html',
dataType: 'html',
cache: false,
success: function(data, status, response) {
var template = Handlebars.compile(response.responseText);
var context = myData;
$('#InjectTemplate_Requests').html(template(context));
}
});
});
index.html
-------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Handlebars</title>
</head>
<body>
<div id="InjectTemplate_Requests">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0-alpha.4/handlebars.min.js"></script>
<script src="script.js"></script>
</body>
</html>
requests.html (template file inside the 'templates' directory)
--------------
<table>
<thead>
<th>Name</th>
<th>Status</th>
<th>Type</th>
</thead>
<tbody>
{{#requests}}
<tr>
<td>{{firstname}} {{lastname}}</td>
<td>{{age}}</td>
</tr>
{{/requests}}
</tbody>
</table>
File Structure
--------------
index.html
|
script.js
|
|
|---templates
|
|
|---requests.html
I seem to be getting this error on the console: Failed to load resource: The requested URL was not found on this server. However, when I add the template to Index.html (and remove ajax call from the script file), the template renders just fine.
Can anybody shed some light as to why this is happening and how I can fix this?
Thanks in advance.
I managed to fix the issue by changing this:
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0-alpha.4/handlebars.min.js"></script>
to this:
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.js"></script>
Thanks for all the suggestions though.
This isn't an issue with Handlebars; it's an issue with your URL (as the error notes); I noticed you're using a relative path
templates/requests.html
in the AJAX request. If you use an absolute URL instead (/templates/requests.html), does that fix the problem?
Likewise, is whatever backend server you're using configured to serve that directory?
Related
I'm creating an application in node.js. I need to create a report in PDF to show the data of my collection in the database. The data is fetched from mongodb.
How can I do to show the data from my collection in the pdf?
The simplest way to generate PDFs using NodeJS is to use the pdf-master package.
You can generate static and dynamic PDFs using HTML with one function call.
Just provide data fetched from MongoDB to generatePdf function and it to HTML template.
Installation
npm install pdf-master
Example
Step 1 - Add required packages and generate a PDF
const express = require("express");
const pdfMaster = require("pdf-master");
const app = express();
app.get("", async (req, res) => {
var PDF = await pdfMaster.generatePdf("template.hbs");
res.contentType("application/pdf");
res.status(200).send(PDF);
});
generatePdf() syntax and parameters
generatePdf(
templatePath, //<string>
data, //<object> Pass data to template(optional)
options //<object> PDF format options(optional)
);
Step 2 - Create your HTML Template (save the template with .hbs extension instead of .html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Render dynamic data in template and PDF format options
const express = require("express");
const pdfMaster = require("pdf-master");
const app = express();
app.get("", async (req, res) => {
var students = {
{
id: 1,
name: "Sam",
age: 21
},
{
id: 2,
name: "Jhon",
age: 20
},
{
id: 3,
name: "Jim",
age: 24
}
}
let options = {
displayHeaderFooter: true,
format: "A4",
headerTemplate: `<h3> Header </h3>`,
footerTemplate: `<h3> Copyright 2023 </h3>`,
margin: { top: "80px", bottom: "100px" },
};
let PDF = await pdfMaster.generatePdf("template.hbs", students, options);
res.contentType("application/pdf");
res.status(200).send(PDF);
});
template for the above example (save the template with .hbs extension)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<h1>Student List</h1>
<ul>
{{#each students}}
<li>Name: {{this.name}}</li>
<li>Age: {{this.age}}</li>
<br />
{{/each}}
</ul>
</body>
</html>
To learn more about pdf-master visit
Last time I created a PDF with nodejs is quite some time ago but I used a npm package called PDFkit.
https://www.npmjs.com/package/pdfkit
PDFkit is quite easy to learn and if you would like an example of how to make a datatable it is in the following stackoverflow question:
HTML table in pdfkit (Expressjs-Nodejs)
and in the following stackoverflow question is an example on how to put json in your pdf:
generate-pdf-from-json-array-objects-with-proper-tabular-format
I hope this helps you with generating a pdf of your data.
I am trying to access a simple javascript variable / object I defined in an external file. My question is how to load it by dojo. Here is my plunker code that didn't work:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tutorial: Hello Dojo!</title>
</head>
<body>
<!-- load Dojo -->
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js"
data-dojo-config="async: true"></script>
<script type="text/javascript">
require({
packages: [
{
name: 'myApp',
location: window.location.href.replace(/\/$/, "")
}]},
["dojo/dom", "myApp/config", "dojo/domReady!"], function(dom, config) {
console.log(config.keys);
});
</script>
</body>
</html>
config.js
var keys = {
"key_1": {
"your_name": "jimmy",
"your_msg": "hello world"
},
"key_2": {
"your_name": "billy",
"your_msg": "foo equals bar"
}
};
As you can see I tried to load the config.js file as config, and tried to access it as config in my code. I am getting undefined in the console.
I did some research and what I need to use looks like the dojo's define function so that config.js looks like this:
define({
keys : {
"key_1": {
"your_name": "jimmy",
"your_msg": "hello world"
},
"key_2": {
"your_name": "billy",
"your_msg": "foo equals bar"
}
}
})
You should define a module for your configuration file.
This can be achieved using the define passing as argument your object.
Basic example:
define({ yourProperty: yourValue});
More information can be found here: https://dojotoolkit.org/documentation/tutorials/1.10/modules/
Having the following code:
var Tasks = Backbone.Collection.extend({
url: 'http://localhost:5000/tasks'
});
var TaskView = Backbone.View.extend({
el: '.page',
render: function() {
var that = this;
var tasks = new Tasks();
tasks.fetch( {
success: function(tasks) {
var template = _.template($('#task-list-template').html(), {tasks: tasks.models});
that.$el.html(template);
}
})
}
});
var Router = Backbone.Router.extend({
routes: {
'' : 'home' // intentionally blank for the home page
}
});
// Display logic
var taskListView = new TaskView({ });
var router = new Router();
router.on('route:home', function() {
taskListView.render();
});
Backbone.history.start();
The following HTML:
<body>
<div class="container">
<h1>TODO app</h1>
<hr />
<div class="page"></div>
</div>
<script type="text/template" id="task-list-template">
<table class="table striped">
<thead>
<tr>
<th>Task</th>
<th></th>
</tr>
</thead>
<tbody>
<% _.each(tasks.tasks, function(task) { %>
<tr>
<td><%=task.get('task') %></td>
<td></td>
</tr>
<% }); %>
</tbody>
</table>
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
<script type="text/javascript" src="todoapp.js"></script>
</body>
and the following JSON return from an AJAX request:
{
"tasks": [
{
"id": 6314025183,
"task": "1"
}
]
}
I was wondering how to fill the Collection with the JSON data. I'm unable to fill my HTML table. I suspect my collection to not being filled properly.
How could I validate the content of the collection?
I'm I filling the collection the right way?
This code is based on this video from Thomas Davis available on youtube.
https://www.youtube.com/watch?v=FZSjvWtUxYk
You have two problems. One is code related and one is unfortunately API related.
The API problem can be solved in two ways, but I'll just lay it out first.
When a Collection requests data (from the url property) it expects an array of data. Unfortunately your API is returning an object:
{
"tasks": [
{
"id": 6314025183,
"task": "1"
}
]
}
This is pretty common in a lot of API design, and really speaks to a general misunderstanding of what makes APIs useful.
You'll notice the data you actually want is here in the tasks key of the object:
[
{
"id": 6314025183,
"task": "1"
}
]
It's an array of task objects, each with an id and - what I assume is - a task id.
Great, so you have two options here: you can fix the API so that a request to a collection route like /tasks returns the collection:
[
{
"id": 6314025183,
"task": "1"
}
]
Or, you can use Backbone's parse method to hack around the junk data.
From the documentation for Collection.parse:
Override this if you need to work with a preexisting API, or better namespace your responses.
Here's a quick example:
var Tasks = Backbone.Collection.extend({
'url': 'http://localhost:5000/tasks',
'parse': function( apiResponse ){
return apiResponse.tasks;
}
});
Note the information contained in that parse method that does not have a home. How do I know that the key of the response is tasks?
If I'm a new developer coming into this code, the fact is that I don't. It's tribal knowledge or knowledge I have to go searching for in the API raw response body. The better solution is to namespace the API response to return the collection as requested.
Your second problem is related to your code. In your code you have a Collection and a View and a template, but in your template, you're treating your tasks like a plain ol' javascript object, using underscore to loop over a key.
Instead, tell your collection how to represent it's data.
A collection is a set of related Models.
var Task = Backbone.Model.extend({});
var Tasks = Backbone.Collection.extend({
'url': 'http://localhost:5000/tasks',
'model': Task,
'parse': function( apiResponse ){
return apiResponse.tasks;
}
});
Now, when you hydrate your collection it will automatically create a model representing each set of discrete data.
You can change your view to look like this:
var TaskView = Backbone.View.extend({
'el': '.page',
'template': _.template($('#task-list-template').html()),
'render': function() {
var that = this;
var tasks = new Tasks();
tasks.fetch( {
success: function() {
that.$el.html( that.template( { 'tasks': tasks } ) );
}
})
}
});
Since all Backbone objects extend underscore in some way or another (see the docs for the details), you don't need to manually wrap the passed in collection in underscore. In fact, doing so will almost always create errors. Your template can look like this:
<html>
<body>
<div class="container">
<h1>TODO app</h1>
<hr />
<div class="page"></div>
</div>
<script type="text/template" id="task-list-template">
<table class="table striped">
<thead>
<tr>
<th>Task</th>
<th></th>
</tr>
</thead>
<tbody>
<% tasks.each( function( task ){ %>
<tr>
<td><%= task.get( 'task' ) %></td>
<td></td>
</tr>
<% }); %>
</tbody>
</table>
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
<script type="text/javascript" src="todoapp.js"></script>
</body>
</html>
The solution posted here is untested, but should allow you to make major debugging leaps even if it does not completely solve the problems
I am currently trying to set up a Jstree, loading the data asynchronously from a webserver with the data provided in the json format. Before, I had the data stored in the html page itself, eg. by creating the <ul> </ul> structure. This worked well.
Then, following
http://agiliq.com/blog/2011/10/how-use-jstree/
I saved the data in the page as a Json string, which also worked fine. After that, I tried the asynchronous loading, where I encounter two problems:
When using Jstree jsTree 1.0-rc3 (as found in https://github.com/akshar-raaj/Using-jsTree), the tree structure is being built up correctly, but two expansion symbols appear left of each folder, which looks rather strange.
Then I tried to use the current version of Jstree, but this produces no tree at all and neither gives an error message.
Here is my html page:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<title>Menu test</title>
<link rel="stylesheet" href="/static/jsTree/themes/default/style.min.css" />
<script src="/static/jquery-1.11.1.js"></script>
<!--<script src="/static/jsTree/jstree.js"></script>-->
<script src="/static/jsTree/jstree_v3.js"></script> <!-- Current Version -->
<script>
$(function() {
$.ajax({
async : true,
type : "GET",
url : "../test2.json",
dataType : "json",
success : function(json) {
createJSTrees(json);
},
error : function(xhr, ajaxOptions, thrownError) {
alert(thrownError);
}
});
});
function createJSTrees(jsonData) {
$("#menuTree").jstree({
"json_data" : {
"data" : jsonData
},
"plugins" : [ "themes", "json_data", "ui" ]
});
}
</script>
</head>
<body>
Tree:
<div id="menuTree" >
</div>
</body>
</html>
And this is what test2.json gives out:
[ { "data" : "a", "children" :[ {"data":"b", "metadata":{"href":"b"}}, {"data":"c", "metadata":{"href":"c"}} ] } ]
Could you help me?
Thank you very much in advance!
I'm working through the example at http://dojotoolkit.org/documentation/tutorials/1.7/hello_dojo/demo/module.html and I've created a directory structure as follows
w:/djt2/index.html
w:/djt2/js/mymodule.js (exact copy of http://dojotoolkit.org/documentation/tutorials/1.7/hello_dojo/demo/myModule.js)
I then set the Tinyweb web-server to serve localhost from w:/djt2
I've changed index.html a tiny bit, to adjust for putting mymodule.js inside the js subdirectory (I've removed comments too below):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tutorial: Hello Dojo!</title>
<script>
var dojoConfig = {
async: true,
packages: [{
name: "djt2",
location: '/js' /* Location #1 */
}]
};
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js"></script>
</head>
<body>
<h1 id="greeting">Hello</h1>
<script>
require(["djt2/mymodule"], function(myModule){ /* Location #2 */
myModule.setText("greeting", "Hello Dojo!");
setTimeout(function(){
myModule.restoreText("greeting");
}, 3000);
});
</script>
</body>
</html>
This works, but I'm not entirely sure I understand why... In particular, at Location #1, the original had location.pathname.replace(/\/[^/]+$/, '') which evaluates to the empty string and makes the loader look for mymodule.js at the CDN location (I also tried to set location to "/", but that made it search at http://mymodule.js/).
At Location #2 it is a little mystical that the prefix should be djt2/...
If someone could explain it, or direct me to the documentation (I'm a bit overwhelmed with learning a new framework :-).
Add an extra djt2 directory in your structure
w:/djt2/js/djt2/mymodule.js
Set the baseUrl in the configuration.
var dojoConfig = {
async: true,
baseUrl: '/js'
packages: [{
name: "djt2",
location: 'djt2'
}]
};
Setting the baseUrl will tell dojo where to look for custom modules. The location that is part of the package is relative to the baseUrl.
I always think of modules/packages as namesapces. To extend your example with an additional package, it would look like the following:
w:/djt2/js/another/anotherModule.js
var dojoConfig = {
async: true,
baseUrl: '/js'
packages: [{
name: "djt2",
location: 'djt2'
}, {
name: "another",
location: 'another'
}]
};