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
To show source code I use a syntax highlighter from alex gorbatchev. The client side library files are included in the public folder. Showing code in a jade template looks like this.
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.
- http://blog.darkhax.com/2010/11/29/2-node-js-apps-that-showed-me-the-light
- http://code.google.com/p/wkhtmltopdf/
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
- http://nodejs.org/ – Node.js home page
- http://expressjs.com/– Express.js
- http://jade-lang.com/– Jade
- http://alexgorbatchev.com/SyntaxHighlighter– Code beautifier
- https://github.com/jettro/nodejs-presenter – the code for the sample.
- http://blog.darkhax.com/2010/11/29/2-node-js-apps-that-showed-me-the-light – inspiration for pdf export.
- http://code.google.com/p/wkhtmltopdf/ – want to use for pdf export.