I am learning about Node.js. While learning I usually
create a few demo’s. Another thing I am attending a training for is
learning how to give good presentations. I had to prepare a 10 minute
presentation. Would be nice if I could combine the learning about
Node.js with the 10 minute presentation. I decided to create a Node.js
application to give the presentation. So no Keynote for me this time.
In this blog post I present you an additional sample,
focussed on creating presentations as a programmer. I’ll use a few
node.js modules: Jade, Express.
On my employers blog I wrote an extensive article about Node.js, you can have a look at the article as well.
http://blog.jteam.nl/2011/04/18/learning-node-js/
Requirements
I want to have a tool that supports multiple presentations that can
all be shown in a browser. I do not consider a lot of browsers. I like
the css support and the full screen view of chrome on the Mac. Therefore
I test my work with Chrome only. I guess most of the features work on
other browsers as well, some small issues might arise. A big advantage
of using the browser is that you have lots of libraries available to
create nice looking slides.
The tool needs to be developer friendly, therefore slides are created
with code. No hot deploys, no tools to just enter slides. The slides
are within the code. This is not an end user tool, this is a developer
tool.
Each presentation consists of a title, should be able to work with a chosen template and contains slides of course.
Giving or presenting the presentation should be easy as well.
Therefore I want to be able to choose a slide, go to the next slide, the
previous slide and the index of all presentations.
Going to a different presentation means using another url.
For each presentation we provide a path and the slide index. A url for a
presentation skills presentation than becomes:
http://dev.gridshore.nl:8018/presentation/create-presentation-nodejs
The first slide in that presentation than becomes:
http://dev.gridshore.nl:8018/presentation/create-presentation-nodejs/slide/1
The final feature that would be nice is generating a handout. For now
this will be a print view in the browser, maybe a pdf. An overview with
all the slides on one page will be here:
http://dev.gridshore.nl:8018/presentation/create-presentation-nodejs/all
Chosen technologies
A slide consists of content, layout and styling. I want to
limit the amount of styling information in the content. Still I want to
make it simple. Therefore I have chosen to use a template engine for the
front-end. I have some experience with Jade, which seems to do the job
fine. The styling is done using css and where necessary, with additional
components. An example is presenting source code on a slide.
When working with a web application and a browser it is wise
to have a way to handle requests. Express.js is a nice node.js
framework to use for url handling. I’ll get back with some sample code
later on.
Since we are creating a web application, we need style sheets and
images. I also use jQuery and a few plugins to position elements, to
handle keyboard commands and to show source code. Please refer to the
references section if you need more information about one of the
technologies.
The solution
Project layout
An express.js application consists of an app.js, a public folder and a
views folder. The app.js is the start of the application that you call
with the node executable. In the public folder we have the static web
resources. It contains the stylesheets, client-side javascripts and
other static resources. The views folder contains the layout.jade file
and all the jade views. Within the images and the views folder we have a
subfolder for each presentation. I prefer to use the same path as the
path specified for the url for the presentation. More on this later.
The project now has two dependencies: expressjs and jade. I like to manage my dependencies with npm.
Creating the presentation
The presentation is created as a separate component, have a look at
presentation.js.
It contains the title of the presentation, the urlIdentifier, a
description and the slides. The urlIdentifier is used in the url when
requesting the presentation in a browser. The slides are also
components. You can find the slide component in
slide.js.
The slide contains an identifier and a title. The identifier is
important, this is used to find the right jade view file. The following
code block shows the creation of the sample presentation.
var createPresentationNodejs = [];
createPresentationNodejs.push(new Slide("intro","Creating a presentation with Node.js"));
createPresentationNodejs.push(new Slide("goal","Goal"));
createPresentationNodejs.push(new Slide("requirements","Requirements"));
createPresentationNodejs.push(new Slide("technology","technology"));
createPresentationNodejs.push(new Slide("urlhandling","Configure url handling"));
createPresentationNodejs.push(new Slide("handlerequests","Handle requests"));
createPresentationNodejs.push(new Slide("viewcontent","View content"));
createPresentationNodejs.push(new Slide("thefuture","The Future"));
createPresentationNodejs.push(new Slide("questions","Questions"));
presentationController.addPresentation(new Presentation("Creating a presentation with Node.js","create-presentation-nodejs",
createPresentationNodejs,
"For my work I had to prepare a short presentation of around 10 minutes. I decided to create a tool to create presentation " +
"with and present about it. This is the result, a short presentation showing some of the ideas."));
In the first lines you can see how we create the array of slides. The
last few lines show how we add a new presentation to the
presentationController. We get back to this presentationController later
on. First we have to look at express and url handling.
Express and url handling
Express is a web framework for node.js. You can do lots of things, in
this blog post I do not want to give to much basics. Check the blog
post I wrote on my employers blog:
http://blog.jteam.nl/2011/04/18/learning-node-js.
THe thing I do want to mention is the url mapping for GET and POST
requests. These are important for the flow of the application. You can
also see the way that the PresentationController is used. Have a look at
the following code block
var presentationController = new PresentationController();
app.get('/', presentationController.allPresentations);
app.get('/presentation/:urlIdentifier', presentationController.index);
app.get('/presentation/:urlIdentifier/slide/:id', presentationController.slide);
app.post('/presentation/:urlIdentifier/slide/:id', presentationController.command);
app.get('/presentation/:urlIdentifier/all', presentationController.allSlides);
Going to the root of the application gives a list over presentations.
Asking the presentation with the urlIdentifier of the presentation
gives an overview of the presentation. You can also ask the /all, this
will give all the slides. Check the section about a slide handout for
more information on this.
The handling of the POST request is for navigation. Within the slide
you can enter n for next, p for previous, a number for a specific slide
and enter for the next slide. This will result in a command on the
server using the post request. Creating this request can be found in the
layout.jade. It is a normal form for the action to the url of the
current slide number. Using the command and the current slide number we
can determine the slide to select. The next block shows the client code,
handling the post request is discussed in the next section.
form(action="/presentation/"+urlIdentifier+"/slide/"+numSlide,method="POST")
input(type="text", size="100", name="command", id="command")
Now we have seen the url handling, let us move on to the logic in the PresentationController
The presentation controller
The presentation controller contains the logic to handle a request
and send the response back to the requestor. The following code block
shows the the handling for the request to show all available
presentations.
PresentationController.prototype.allPresentations = function(req, res) {
res.render('index', {locals: {presentations:presentations}});
};
The presentations is an internal array for the presentation
controller. They are passed to the ‘index’ view. More on the view with
jade in the next section. The following code block is slightly more
advanced. It contains the logic to show one slide.
PresentationController.prototype.slide = function(req, res) {
var id = req.params.id;
var presentation = obtainPresentation(req.params.urlIdentifier);
var slide = presentation.slides[id - 1];
res.render(presentation.urlIdentifier + '/slide/' + slide.identifier,
{locals: {
numSlide:req.params.id,
totalSlide:presentation.slides.length,
titleSlide:slide.title,
urlIdentifier:presentation.urlIdentifier
}});
};
function obtainPresentation(urlIdentifier) {
var len = presentations.length;
for (var i = 0; i < len; i++) {
var presentation = presentations[i];
if (urlIdentifier == presentation.urlIdentifier) {
return presentation;
}
}
throw "Could not find the url: " + urlIdentifier;
}
The code contains a helper function to determine the presentation to
show a slide for. The presentation is obtained by the url identifier.
The page is rendered by using the specific jade template that is stored
using the urlIdentifier in the slides folder. The name of the jade
template is the same as the identifier as stored in the slide object.
The last bit I want to discuss is the command handling. We have a
small form at the bottom of the page. This form accepts commands. We
support ‘n’ or ‘next’, ‘enter’, ‘p’ or ‘previous’ and the number of a
slide. These all navigate between the slides. We also have the ‘home’
command. This redirects to the root of the application. Handling the
different commands is done with a very easy if-the-else structure. The
following code block shows the idea.
PresentationController.prototype.command = function(req, res) {
var id = req.params.id;
var urlIdentifier = req.params.urlIdentifier;
var command = req.body.command;
var presentation = obtainPresentation(urlIdentifier);
var numSlides = presentation.slides.length;
if (command == 'next' || command == 'n' || command == '') {
if (id < numSlides) id++;
} else if (command == 'previous' || command == 'p' || command == 'prev') {
if (id > 1) id--;
} else if (command == 'home') {
res.redirect('/');
return;
} else if (!isNaN(command)) {
var commandNum = parseInt(command);
if (commandNum > 0 && commandNum <= numSlides) id = commandNum;
}
res.redirect('/presentation/' + urlIdentifier + '/slide/' + id);
};
Enough about logic, let us move on to the view side and discuss templating using Jade.
Little bit of jade
Again I do not want to go into all the details of jade. Jade is a
very condensed format for creating html pages. It integrates nicely with
express. Data is passed from express to jade. Jade uses a layout file,
you can also use partial rendering without a layout file. More on this
in a next section. In this case we include the header and the footer in
the layout file. Each slide is presented in its own jade template file.
The following code block shows the jade template for the intro slide in
the sample.
img(src='/images/' + urlIdentifier + '/logo.jpg')
div.heading author
div.heading Jettro Coenradie
img(src='/images/' + urlIdentifier + '/nature.jpg', class="shadow")
Another nice thing about using the web browser as a tool is that you
can use nice plugins available for websites to create your slides. A
good example is showing source code. This is what the next section is
about.
Showing code
pre(class="brush: js")
| app.get('/', function(req, res) {
| res.render('index',
| {locals: {slides:slide.allSlides()}});
| });
| app.get('/slide/:id', slide.index);
| app.post('/slide/:id', slide.command);
script(type="text/JavaScript")
| SyntaxHighlighter.all()
Creating a slide handout
One of the requirements of a presentation is often to have the slides
available as a pdf document or other format that is able to share the
slides. The path I am taking is using one page for a presentation that
contains the complete presentation. I would love to have a pdf generated
from the webpage. But for now this is one step to far. I did have a
look at using node.js child processes to run a command line tool called
wkhtmltopdf. I want to do some more experimentation here. Will get back
when I have a good solution. Beneath are some references if you want to
try it out yourself.
To be able to create one page with all the slides I made use
of the partial rendering of jade and express. The idea is to pass a the
presentation to a view. The view steps over all the slides in the
presentation and renders the views on one page. The following code block
shows the view (all.jade).
div#All slides
h2 #{presentation.title}
ul
- each slide in presentation.slides
- var slideTemplate = presentation.urlIdentifier + '/slide/' + slide.identifier
.slideAll
- if(typeof(slide.title) != "undefined")
div.title #{slide.title}
!=partial(slideTemplate, {'urlIdentifier':presentation.urlIdentifier})
The “presentation” object is provided by the express
component. From this object we obtain the path in the views folder. The
presentation object also contains the slides. Each slide has its own
identifier. Using the slide identifier and the urlIdentifier we can find
the jade template to render. The trick is in the last line !=partial(…). This tells the engine to include the specific slide templates in he current view.
The demo
There is an online demo available at:
http://dev.gridshore.nl:8018/.
It contains only one presentation at the moment. But that is enough to
get an idea. This blogpost also contains two screen dumps.
The future
I want to integrate the pdf generation within the
application. I have some hints, but not the complete solution. Maybe
extend the tool in a way that you can create new presentations on the
fly, but I do not want to make an alternative to presentation tools that
are already there. I also want to add more controls, would be nice to
be able to use the arrow keys and maybe support remote controls that
way.
References
Comments
Post a Comment