template not rendering correctly - node.js

Can someone please point me in the right direction. I tried google and didn't find much regarding my issue. While the following code works perfectly fine when running the .html file directly, it doesn't while serving the file in a node express app. The problem is that with node I don't see any data. page loads fine but no data.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="jquery.js"></script>
<script src="handlebars.js"></script>
<script src="underscore.js"></script>
<script src="backbone.js"></script>
<script src="moment.js"></script>
<!-- Setup our templates -->
<script id="PersonTemplate" type="text/x-handlebars-template">
<div>
Person:<br>
{{name}}
{{age}}
</div>
</script>
<!--
Note the [] this is important
because handlebars and backbone collections
dont play well with each other in regards
to naming JSON groups
-->
<script id="PeopleTemplate" type="text/x-handlebars-template">
<div>
People:<br>
{{#each []}}
{{this.name}}
{{this.age}}
<br/>
{{/each}}
</div>
</script>
<!-- End templates setup -->
<script>
// Stub out the person model
var Person = Backbone.Model.extend({
});
// Create a collection of persons
var People = Backbone.Collection.extend({
model: Person
});
// Define the view for a single person
var PersonView = Backbone.View.extend({
render: function() {
// This is method that can be called
// once an object is init. You could
// also do this in the initialize event
var source = $('#PersonTemplate').html();
var template = Handlebars.compile(source);
var html = template(this.model.toJSON());
$(this.el).html(html);
}
});
// Define the view for People
var PeopleView = Backbone.View.extend({
render: function() {
// This is method that can be called
// once an object is init. You could
// also do this in the initialize event
var source = $('#PeopleTemplate').html();
var template = Handlebars.compile(source);
var html = template(this.collection.toJSON());
$(this.el).html(html);
},
initialize: function(){
this.collection.on('add', this.render, this)
}
});
// Create instance of People Collection
var people = new People();
// Create new instances of the person models
var person = new Person({name: "Tim", age: 5});
var person2 = new Person({name: "Jill", age: 15});
// Create instances of the views
var personView = new PersonView({
model: person
});
var peopleView = new PeopleView({
collection: people
});
$(document).ready(function(){
// We have to do this stuff in the dom ready
// so that the container objects get built out
// Set el of the views.
personView.el = $('#personContainer');
peopleView.el = $('#peopleContainer');
// Add them to a new instance of the people collection
people.add(person);
people.add(person2);
// Render the views. If you are using the initialize
// method then you do not have to do this step.
personView.render();
//peopleView.render();
// Try on console!
// people.add(new Person({name: 'Rames', age:'23'}));
});
</script>
</head>
<body>
<div id='personContainer' ></div>
<hr>
<div id='peopleContainer' ></div>
</body>
</html>
Thanks in advance for your help.

Related

Client side and Server side rendering of ejs template

I always wanted to learn NodeJS to be able to run the same code on server and client side.
I am using NodeJS with Express and EJS.
So. I have a .ejs page with lot's of HTML, JS, CSS and a small bit with template. For the sake of justice let it be like this:
the_list-->some.ejs
<ul>
<% for(i=0;i>the_list.length;i++) { %>
<li>the_list[i]</li>
<% } %>
</ul>
After some rendering on the server we have a perfect list.
So.
Now I want to rerender it on the client. I made some ajax request and now I have new items in the_list. What is the right way?
As per ejs templates documentation
var template = new EJS({
text: `
<ul>
<% for(i = 0; i < the_list.length; i++) { %>
<li>the_list[i]</li>
<% } %>
</ul>
`
});
var html = template.render({ the_list: data });
document.getElementById('list-wrapper').innerHTML = html;
<div id="output"></div>
<script src="/assets/js/ejs.js"></script>
<script>
let blogPosts = [
{
title: 'Perk is for real!',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-19'),
createdAt: new Date('2016-03-19')
},
{
title: 'Development continues...',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-18'),
createdAt: new Date('2016-03-18')
},
{
title: 'Welcome to Perk!',
body: '...',
author: 'Aaron Larner',
publishedAt: new Date('2016-03-17'),
createdAt: new Date('2016-03-17')
}
];
var html = ejs.render(`<% for(let i = 0; i < posts.length; i++) { %>
<article>
<h2><%= posts[i].title %></h1>
<p><%= posts[i].body %></p>
</article>
<% } %>`, {posts: blogPosts});
// Vanilla JS:
document.getElementById('output').innerHTML = html;
</script>
download ejs.js or ejs.min.js from latest version
Sure, EJS works on the client. You can trivially keep the template in a string variable or apply EJS to user-provided input, but more likely, you'll want to store a template in a script (which can be in an external file) or use fetch to grab your template from another file on demand.
Using a template in a <script> is straightforward:
const people = ["geddy", "neil", "alex"];
const template = document
.querySelector("#template")
.innerText;
document.querySelector("#output")
.innerHTML = ejs.render(template, {people});
<!-- could be an external file -->
<script id="template" type="text/template">
<%= people.join(", "); %>
</script>
<div id="output"></div>
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
For fetch, I'll mock the response so it'll be runnable in a snippet:
// mock fetch for illustrative purposes;
// its response content would be another file
fetch = async url => ({text: async () => '<%= people.join(", "); %>'});
fetch("/your-template")
.then(res => res.text())
.then(template => {
const people = ["geddy", "neil", "alex"];
document.querySelector("#output").innerHTML =
ejs.render(template, {people});
});
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
<div id="output"></div>
If this seems like too much heavy lifting, you can bury the fetch in a helper function, or go a step further and pick an attribute for each URL, then plug everything in with a call to a library function you can abstract away from the main code. A simple example:
// mock fetch for illustrative purposes;
// its response content would be in other files
const responses = {
"/template.ejs": "<%= 42 %>",
"/other-template.ejs": "<%= 43 %>",
};
fetch = async url => ({text: async () => responses[url]});
[...document.querySelectorAll("[data-template]")]
.forEach(e => {
fetch(e.getAttribute("data-template"))
.then(res => res.text())
.then(template => {
e.innerHTML = ejs.render(template);
});
});
<script src="https://unpkg.com/ejs#3.1.8/ejs.min.js"></script>
<div data-template="/template.ejs"></div>
<div data-template="/other-template.ejs"></div>
Either way, keep in mind that JS will run after the static HTML is parsed and the DOM loads. This means the data won't appear all in one fully-formed piece as when using EJS on the server. Network errors are possible.
See also using ejs partials from express client side. If you want to mock the include function, the problem is that the fetch call is asynchronous but the include function isn't. EJS offers an include callback that seems like it offers an opportunity to pull in an external file, but it's purely synchronous and won't await any promises you return. How to work around this best depends on your use case.
This should work, looks like your problem was the relational operator '>' because it will never output something.
<ul>
<% for(var i=0; i<the_list.length; i++) { %>
<li>
<a>
<%= the_list[i]%>
</a>
</li>
<% } %>
</ul>

Destroy Darkroom / Fabric JS Canvas

$(function() {
$('button').on('click', function() {
new Darkroom('#edit', {
plugins: {
save: {
callback: function() {
console.log('saving');
var darkroom = this.darkroom;
darkroom.canvas.clear().renderAll();
darkroom.selfDestroy();
return true;
}
}
}
});
});
});
<html>
<head>
<link href="https://rawgithub.com/MattKetmo/darkroomjs/master/build/darkroom.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.2/fabric.min.js"></script>
<script src="https://rawgit.com/MattKetmo/darkroomjs/master/build/darkroom.js"></script>
</head>
<body>
<div style="margin-top:50px">
<img id="edit" class="data-uri-example" src="data:image/gif;base64,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">
</div>
<button type="button">Edit</button>
</body </html>
Using darkroom js and fabric js. After I save the canvas data I want to remove the img/canvas from the page. The original img tag used in initialization '#edit' is no longer an element on the page. Seems it's replaces by a new img.
I've tried the .clear() also .clear().renderAll(). I get no error but the image still shows on the page.
Remove div element for control Darkroom and in code create again
$(function () {
$('button').on('click', function () {
$('#main-el').html('');
$('#main-el').append('<img id="edit" class="data-uri-example" src="...">');
new Darkroom('#edit');
});
});

Change polymer attributes externally

I see a lot of questions regarding on how to listen for changes in attributes. But none on how to actually change them.
Even in debug, I can't find the attributes in the object tree. How do I achieve this? is there a more polymeric way of doing the following ?
<polymer-element name="my-element" attributes="owner">
<template>
<p id="el">{{owner}}</p>
</template>
<script>
Polymer({
owner: "Miguel",
});
</script>
</polymer-element>
<my-element id="el1" owner="blabla"></my-element>
<script>
$(document).ready(function(){
$("el1").owner = "Mary"
})
</script>
It prints blablab, but doesn't change it to Mary
In 0.5, you need to wait for polymer-ready. See docs here.
<head>
<link rel="import" href="path/to/x-foo.html">
</head>
<body>
<x-foo></x-foo>
<script>
window.addEventListener('polymer-ready', function(e) {
var xFoo = document.querySelector('x-foo');
xFoo.barProperty = 'baz';
});
</script>
</body>
Ideally, your app would be one element like <my-app></my-app> so that you'd have a binding for the owner attribute like so:
<my-element owner="{{owner}}"></my-element>
And my-element would reside in another Polymer element in which you can set the owner attribute like so:
// Parent Polymer element
Polymer({
_someFunctionYouCall: function() {
this.owner = 'Mary';
}
});

How to set a variable for the main handlebars layout without passing it to every route?

I'm using handlebars with nodejs and express. This is my main.handlebars file:
<!doctype html>
<html>
<head>
...
</head>
<body>
<div class ="container">
...
<footer>
© {{copyrightYear}} Meadowlark Travel
</footer>
</div>
</body>
</html>
So far I'm passing the copyright year to every route:
var date = new Date();
var copyrightYear = date.getFullYear();
app.get(
'/',
function( req, res) {
res.render(
'home',
{
copyrightYear: copyrightYear
}
);
}
);
Is it possible to set the copyrightYear variable globally, so I don't have to pass it on to every route/view?
ExpressJS provides some kind of "global variables". They are mentioned in the docs: app.locals. To include it in every response you could do something like this:
app.locals.copyright = '2014';
For this case, you can alternatively create a Handlebars helper. Like this:
var Handlebars = require('handlebars');
Handlebars.registerHelper('copyrightYear', function() {
var year = new Date().getFullYear();
return new Handlebars.SafeString(year);
});
In the templates, just use it as before:
© {{copyrightYear}} Meadowlark Travel
Using express-handlebars is just a little bit different:
var handlebars = require('express-handlebars').create({
defaultLayout:'main',
helpers: {
copyrightYear: function() {
return new Date().getFullYear();
},
}
});

RealTime Collaborative Text-Editor in Nodejs & Socket.io

I am developing a real-time text editor with paragraph locking property similar to https://quip.com/. in socket.io and nodejs.
It means when you write onto a given paragraph, other collaborators cant edit it.
Moment you hit enter or move cursor to a new line that paragraph becomes Editable for other Collaborators.
I am quite stuck after this. I am thinking a nice approach to move further. Suggestions please.
Below is my code which works perfectly. Till now i can get list of all collaborators and broadcast the content of editor to other collaborators.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Connected Clients</title>
<!--<meta charset="UTF-8"> -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!--<script type="text/javascript" src="jquery.js"></script> -->
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<textarea id="editor" style="height:200px;width:300px">
Thinknest Pragraph locking Test sample !
</textarea>
<script>
function msgReceived(msg){
//clientCounter.html(msg.clients);
document.getElementById('people').innerHTML=msg.uid;
//console.log(msg.id);
}
var clientCounter;
$(document).ready(function () {
clientCounter = $("#client_count");
var socket = io.connect(
'http://localhost:5000',
{'sync disconnect on unload':true}
);
var uId=prompt("enter your userId",'');
socket.emit('collabrator',uId);
socket.on('message', function(msg){
msgReceived(msg);
});
socket.on('online_collabs',function(data){
$('#online_ppl').html(data);
clientCounter.html(data.length);
});
socket.on('remained_collabs',function(data){
$('#online_ppl').html(data);
clientCounter.html(data.length);
});
socket.on('note_collabs',function(data){
$('#note_colabs').html(data);
});
socket.on('updated_para',function(data){
//$('#editor').append(data);
document.getElementById('editor').innerHTML=data;
});
$('#editor').on('keydown',function(){
//var para=$('#editor').value;
var para= $('#editor').val();
//var para=document.querySelector('[contenteditable]');
// var text=para.textContent;
socket.emit('para',{paragraph:para});
});
});
</script>
<p><span id="client_count">0</span> connected clients</p><br/>
<ul id="people"></ul>
<h3>Online Collaborators</h3>
<span id="online_ppl"></span> <br>
<h3>Note Collaborators</h3>
<span id="note_colabs"></span>
</body>
</html>
server.js
var app = require('express')()
, server = require('http').createServer(app)
, io = require('socket.io').listen(server);
server.listen(5000);
app.get('/',function(req,res){
res.sendfile("./index.html");
});
var activeClients = 0;
var Collaborators=['Colab1','Colab2','Colab3'];
var people=[];
io.sockets.on('connection', function(socket){
clientConnect(socket);
socket.on('disconnect', function(){
clientDisconnect(socket);
});
socket.on('para',function(data){
//io.sockets.emit('updated_para',data.paragraph);
socket.broadcast.emit('updated_para',data.paragraph);
});
});
function clientConnect(socket){
//activeClients +=1;
var userSocketId=socket.id;
check_Collaborator(socket);
io.sockets.emit('message', {uid:userSocketId});
}
var online_collabs=[];
function check_Collaborator(socket){
socket.on('collabrator',function(data){
if(Collaborators.indexOf(data)!=-1){
socket.data=data;
if(online_collabs.indexOf(data)==-1) {
online_collabs.push(data);
}
io.sockets.emit('online_collabs',online_collabs);
io.sockets.emit('note_collabs',Collaborators);
} else {
console.log("collabrator not found");
}
});
}
function clientDisconnect(socket){
var index=online_collabs.indexOf(socket.data)
if(index>-1)
online_collabs.splice(index,1);
//activeClients -=1;
//io.sockets.emit('message', {clients:activeClients});
io.sockets.emit('remained_collabs',online_collabs);
}
I saw this yesterday already. What exactly is your question? Do you want to know how to 'lock' a text area with javascript? I am confused as to why you put such a strong emphasis on node/socket.io in your question.
Also, next time please format your code. You want help, I get it, but then make it easier for others to help you.
What you have to do in order to make a paragraph not editable by others, I don't know. But let me suggest what I'ld do in socket.io:
Store each paragraph separately and remember who has a lock on it. For locking, I would use the sessionID in case users don't have to register. This would look something like this:
var paragraphs = {
data : [
{
text: "this is an unlocked paragraph",
lock: ""
},
{
text: "this is a locked paragraph",
lock: "oWEALlLx5E-VejicAAAC"
}
]
}
Now, users will likely be allowed to add a paragraph before an existing one. Therefore you should keep an additional index like:
var paragraphs = {
index : [
1,
0
],
data : [
{
text: "this the second paragraph",
lock: "oWEALlLx5E-VejicAAAC"
},
{
text: "this is the first paragraph",
lock: ""
}
]
}
The amount of data being sent over the sockets should now be very small - altough with additional client/server-side logic.
Paragraph lock can be easily achieved by adding a class to the currently editing paragraph. Transfer this paragraph with the class to the other user. so that if the user tries to write over that prevent him by validate with the class.
Generate a class name look like - className_userid (className_1).

Resources