Node.js Workshop Tom Hughes-Croucher Chief Evangelist / Node Tech - - PowerPoint PPT Presentation

node js workshop
SMART_READER_LITE
LIVE PREVIEW

Node.js Workshop Tom Hughes-Croucher Chief Evangelist / Node Tech - - PowerPoint PPT Presentation

Node.js Workshop Tom Hughes-Croucher Chief Evangelist / Node Tech Lead @sh1mmer tom@joyent.com Overview Introduction Why Server-Side JavaScript? What is Node? Using Node Understanding Node Node Ecosystem Programming


slide-1
SLIDE 1 Chief Evangelist / Node Tech Lead tom@joyent.com @sh1mmer Tom Hughes-Croucher

Node.js Workshop

slide-2
SLIDE 2

Overview

  • Introduction
  • Why Server-Side JavaScript?
  • What is Node?
  • Using Node
  • Understanding Node
  • Node Ecosystem
  • Programming Style
  • More Complex applications
  • Deploying Node apps to the cloud

Running order

slide-3
SLIDE 3

Introduction

  • Tom Hughes-Croucher
  • Chief Evangelist at Joyent
  • Node.js core contributor
  • Author of "Up and Running with Node.js"
slide-4
SLIDE 4 Scalable Server-Side Code with JavaScript

Tom Hughes-Croucher

Node

Up and Running

slide-5
SLIDE 5

Major update this week

slide-6
SLIDE 6

Why Server-Side JavaScript?

slide-7
SLIDE 7

JavaScript programmers

3 > 2 > 1

slide-8
SLIDE 8

Massive Code base of jQuery and other JS libraries

slide-9
SLIDE 9

I could have said effjciency, but I think we all secretly long to sit around in our underwear.

Laziness or “I’m sick

  • f writing stufg twice”
slide-10
SLIDE 10

Remember WWCD (What Would Crockford Do)

*close enough

Progressive Enhancement is free*

slide-11
SLIDE 11

Like a Unicorn riding a Narwhal

TL;DR: SSJS is Awesome

slide-12
SLIDE 12
slide-13
SLIDE 13

If SSJS is so awesome why is it "new"?

slide-14
SLIDE 14
  • 1. Professionalism
slide-15
SLIDE 15
slide-16
SLIDE 16

“Yahoo!'s corporate motto is: Don't be eval().“

slide-17
SLIDE 17
  • 2. JavaScript

Runtimes

slide-18
SLIDE 18

Runtimes

  • V8 (Google), C++
  • Spider Monkey (Mozilla), C++
  • Rhino (Mozilla), Java
slide-19
SLIDE 19

JavaScript Performance

V8 Spider Monkey

slide-20
SLIDE 20

8x

Sep 08 Mar 11

slide-21
SLIDE 21

Anatomy of SSJS

slide-22
SLIDE 22

{

Node

slide-23
SLIDE 23

Runtime != Browser

slide-24
SLIDE 24

No DOM

(By default, anyway)

slide-25
SLIDE 25

Summary

  • Benefits of SSJS
  • Lots of JavaScript expertise
  • Lots of web code in JS libraries
  • Write once, run anywhere
  • Progressive Enhancement
  • Why SSJS happened now
  • Professionalism in JavaScript
  • New generation of JavaScript runtimes
slide-26
SLIDE 26

What is Node?

slide-27
SLIDE 27

Node

  • JavaScript programming environment
  • Uses

V8 runtime

  • Event Driven
  • Non-blocking libraries
  • Supports CommonJS module format
  • Supports C/C++ based add-ons

Service behind traffjc server

slide-28
SLIDE 28

Woah! Overload.

slide-29
SLIDE 29
  • 1. It's JavaScript
slide-30
SLIDE 30

See Above.

slide-31
SLIDE 31
  • 2. It's Fast
slide-32
SLIDE 32

concurrency=300, Smaller is Better

response size (bytes) response time (ms)

100 200 300 400 24 26 28 210 212 214 216 218 server nginx thin tornado node_buffer
slide-33
SLIDE 33
slide-34
SLIDE 34
  • 3. It's easy to extend
slide-35
SLIDE 35

'Modules' in JS 'Add-ons' in C

slide-36
SLIDE 36
  • 4. Node is _not_ Rails/

Django/etc

slide-37
SLIDE 37

Node is bare-bone to the metal

slide-38
SLIDE 38

However, the Node community are making Rails/Django/etc

slide-39
SLIDE 39
  • 5. Node is young
slide-40
SLIDE 40
slide-41
SLIDE 41

Stable is "stable" Unstable moves fast

slide-42
SLIDE 42

Using Node

slide-43
SLIDE 43

Using Node

  • Part 1. Installation
  • Part 2. Basics
  • Part 3. Getting stuck in
slide-44
SLIDE 44

Part 1. Installation

slide-45
SLIDE 45

Nave

a.k.a the easy way

slide-46
SLIDE 46

Enki:~ $ wget -q http://github.com/isaacs/nave/raw/ master/nave.sh Enki:~ $ chmod 755 nave.sh Enki:~ $ ./nave.sh install latest

slide-47
SLIDE 47
  • Installs and versions Node
  • Allows Node shells with specific versions
  • Allows you to get 'latest' <-- Stable
  • May add 'unstable' option in future

Nave

slide-48
SLIDE 48

Manual Installation

slide-49
SLIDE 49

Go to http://nodejs.org/#download and get the URL of the current stable release

slide-50
SLIDE 50

Enki:~ $ wget -q http://nodejs.org/dist/node- v0.4.10.tar.gz Enki:~ $ tar xzf node-v0.4.10.tar.gz Enki:~ $ cd node-v0.4.10 Enki:~/node-v0.4.10 $

slide-51
SLIDE 51

Local or system?

slide-52
SLIDE 52

Local

slide-53
SLIDE 53

Enki:~/node-v0.4.10 $ mkdir ~/local Enki:~/node-v0.4.10 $ ./configure --prefix=~/local Checking for program g++ or c++ : /usr/bin/g++ Checking for program cpp : /usr/bin/cpp ... Checking for fdatasync(2) with c++ : no 'configure' finished successfully (3.466s)

slide-54
SLIDE 54

Enki:~/node-v0.4.10 $ make Waf: Entering directory `/Users/sh1mmer/node-v0.4.10/build' DEST_OS: darwin DEST_CPU: x86 Parallel Jobs: 1 [ 1/69] cc: deps/libeio/eio.c -> build/default/deps/libeio/eio_1.o /usr/bin/gcc -rdynamic -D_GNU_SOURCE -DHAVE_CONFIG_H=1

  • DEV_MULTIPLICITY=0 -pthread -g -O3 -DHAVE_OPENSSL=1 -

DX_STACKSIZE=65536 -D_LARGEFILE_SOURCE - D_FILE_OFFSET_BITS=64 -DHAVE_FDATASYNC=0 - DPLATFORM="darwin" -DNDEBUG -Idefault/deps/libeio -I../deps/ libeio -Idefault -I.. ../deps/libeio/eio.c -c -o default/deps/libeio/ eio_1.o ...

slide-55
SLIDE 55

Enki:~/node-v0.4.10 $ make install Waf: Entering directory `/Users/sh1mmer/node-v0.4.10/build' DEST_OS: darwin DEST_CPU: x86 Parallel Jobs: 1 * installing build/default/config.h as /Users/sh1mmer/local/ include/node/config.h * installing build/default/node as /Users/sh1mmer/local/bin/ node * installing build/default/src/node_config.h as /Users/sh1mmer/ local/include/node/node_config.h Waf: Leaving directory `/Users/sh1mmer/node-v0.4.10/build' 'install' finished successfully (0.373s) Enki:~/node-v0.4.7 $

slide-56
SLIDE 56

Enki:~/node-v0.4.10 $ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/ local/bin:/usr/X11/bin:/usr/local/git/bin:/Users/ croucher/Code/narwhal/bin:/opt/local/bin:/usr/local/ git/bin:/Users/sh1mmer/bin Enki:~/node-v0.4.10 $ node -v

  • bash: node: command not found

Enki:~/node-v0.4.10 $ echo PATH=~/local/bin:\$PATH >> ~/.profile Enki:~/node-v0.4.10 $ node -v v0.4.10

slide-57
SLIDE 57

System

slide-58
SLIDE 58

Enki:~/node-v0.4.10 $ ./configure ... Enki:~/node-v0.4.10 $ make ... Enki:~/node-v0.4.10 $ sudo make install ... Enki:~/node-v0.4.10 $ node -v v0.4.7 Enki:~/node-v0.4.10 $

slide-59
SLIDE 59
  • Get Node head from Github using

Git

  • Install to ~/node

Exercise

slide-60
SLIDE 60

Part 2. Basics

slide-61
SLIDE 61

Interactive JavaScript terminal

node-repl

slide-62
SLIDE 62

$Enki:~ $ node

slide-63
SLIDE 63

$Enki:~ $ node > 3 > 2 > 1 false > true == 1 true > true === 1 false

slide-64
SLIDE 64

> console.log('Hello World'); Hello World > .help .clear Break, and also clear the local context. .exit Exit the prompt .help Show repl options > .clear Clearing context... > .exit Enki:~ $

slide-65
SLIDE 65

Enki:~ $ node > var foo = "bar"; > foo; 'bar' > .clear Clearing context... > foo ReferenceError: foo is not defined at [object Context]:1:1 at Interface.<anonymous> (repl:98:19) at Interface.emit (events:27:15) at Interface._ttyWrite (readline:295:12) at Interface.write (readline:132:30) at Stream.<anonymous> (repl:79:9) at Stream.emit (events:27:15) at IOWatcher.callback (net:489:16)

slide-66
SLIDE 66

var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8124, "127.0.0.1"); console.log('Server running at http://127.0.0.1:8124/');

slide-67
SLIDE 67

var http = require('http'); //include the http library

slide-68
SLIDE 68

http.createServer(function (req, res) { }).listen(8124, "127.0.0.1"); //create an http server //when ‘stufg’ happens call this anonymous function //listen on port 8124 of the IP 127.0.0.1

slide-69
SLIDE 69

http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }) //when ‘stufg’ happens my function fires //I get a request object and a response object //I write to the response object header //HTTP status 200 and content-type ‘text/plain’ //close the response with the body: //Hello World

slide-70
SLIDE 70

console.log('Server running at http://127.0.0.1:8124/'); //write Server is running at http://127.0.0.1:8124/ //to the console

slide-71
SLIDE 71

Interactive Debugging

slide-72
SLIDE 72

Enki:~/Code/node-examples $ node --debug helloworld.js debugger listening on port 5858 Server running at http://127.0.0.1:8124/

slide-73
SLIDE 73
slide-74
SLIDE 74
slide-75
SLIDE 75
slide-76
SLIDE 76
slide-77
SLIDE 77
slide-78
SLIDE 78
slide-79
SLIDE 79

Enki:~ $ npm install node-inspector node-inspector@0.1.8 ./node_modules/node-inspector ├── websocket-server@1.4.04 └── paperboy@0.0.2 Enki:~ $ node-inspector visit http://0.0.0.0:8080/debug?port=5858 to start debugging

slide-80
SLIDE 80
slide-81
SLIDE 81

Exercises

  • Modify the HTTP server to return the text

"I'm learning Node"

  • Change the HTTP response to HTML and

return your text in an HTML page

  • Return the User-Agent string from the

browser as part of your HTML page

  • Return different textual responses to 2 (or

more) browsers

slide-82
SLIDE 82

Part 3. Getting Stuck In

slide-83
SLIDE 83

HTTP Client

slide-84
SLIDE 84

var http = require('http'); var request = http.request({'host': 'www.google.com', 'port': 80, 'path': '/', 'method':'GET'}); request.on('response', function (response) { console.log('STATUS: ' + response.statusCode); console.log('HEADERS: ' + JSON.stringify (response.headers)); response.setEncoding('utf8'); response.on('data', function (chunk) { console.log('BODY: ' + chunk); });

slide-85
SLIDE 85

Streaming API

slide-86
SLIDE 86

write(data) write(data) end() response(headers) Response Destination (google.com) Request data(chunk) data(chunk) data(chunk) end()

slide-87
SLIDE 87

Exercise

  • Fetch the NYTimes.com and output

the contents to the console

  • Create a web server
  • Create an HTTP client
  • POST data to your web server
  • Output the POST data to console
slide-88
SLIDE 88

Events

slide-89
SLIDE 89
  • bject.on('event', function() {

//stufg } );

slide-90
SLIDE 90

EventEmitter

slide-91
SLIDE 91

EventEmitter

  • manage "event handlers"
  • list of functions to be called per

event

  • provide mechanism to trigger

events

slide-92
SLIDE 92

var util = require('util'), EE = require('events').EventEmitter; util.inherits(MyClass, EE); var myObj = new MyClass(); //nb using first class functions myObj.on('something', function);

slide-93
SLIDE 93

exports.inherits = function (ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false } }); };

slide-94
SLIDE 94

More than just core

slide-95
SLIDE 95

CommonJS Modules

slide-96
SLIDE 96

Library format for SSJS

slide-97
SLIDE 97

var inc = require('increment').increment; var a = 1; inc(a); // 2 exports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum; }; var add = require('math').add; exports.increment = function(val) { return add(val, 1); };

math.js increment.js

slide-98
SLIDE 98

Protip

exports.awesome = function() { //yay my code is awesomesauce }; var exports.fail = function() { //exports is a global //by redeclaring it as //a local variable //your code _will_ fail };

slide-99
SLIDE 99

Exercise

  • Create a CommonJS module called "fish"
  • Provide functions to:
  • swim
  • mouth breath
  • flop around
  • Import your module into a node project
  • Call the various functions
slide-100
SLIDE 100

Node Package Manager (NPM)

slide-101
SLIDE 101

NPM is written in JavaScript for Node

slide-102
SLIDE 102

Enki:~ $ cat `which npm` #!/usr/bin/env node ;(function () { // wrapper in case we're in module_context mode var log = require("../lib/utils/log") log.waitForConfig() log.info("ok", "it worked if it ends with") var fs = require("../lib/utils/graceful-fs") , path = require("path") , sys = require("../lib/utils/sys") , npm = require("../npm") , ini = require("../lib/utils/ini") , rm = require("../lib/utils/rm-rf")

slide-103
SLIDE 103

Enki:~/Code/node(master) $ npm install express express@2.3.11 ../../node_modules/express ├── mime@1.2.2 ├── connect@1.4.2 └── qs@0.1.0 Enki:~/Code/node(master) $

slide-104
SLIDE 104
  • Yay. Easy.
slide-105
SLIDE 105

Install instructions

https://github.com/isaacs/npm

slide-106
SLIDE 106

Express.js

slide-107
SLIDE 107

Sinatra Style MVC framework

slide-108
SLIDE 108

var app = require('express').createServer(); app.get('/', function(req, res){ res.send('hello world'); }); app.listen(3000);

slide-109
SLIDE 109

HTTP Verb Oriented

slide-110
SLIDE 110

Middleware

slide-111
SLIDE 111

app.use(express.bodyParser()); app.use(express.cookieParser()); app.post('/', function(req, res){ // Perhaps we posted several items with a form // (use the bodyParser() middleware for this) var items = req.body.items; console.log(items); res.send('logging'); });

slide-112
SLIDE 112

Templating

slide-113
SLIDE 113

var express = require("express"); app.configure(function () { var public = __dirname + "/../public/"; public = require("path").normalize(public); app.set("views", __dirname + "/views"); app.set("view engine", "jade"); }); app.get("/", function (req, res) { res.render("index", { locals : { h1: 'Router Stats', scripts : [ "/public/smoothie.js", "/public/raphael.js", "/public/base.js", "/public/gfx.js", "/public/explore.js", "/public/app.js" ] } } });

slide-114
SLIDE 114
  • Create an Express server
  • Serve two difgerent pages based on

value of the HTTP Get param "page"

  • Create a redirect from /old to /new
  • Set a cookie on the client

Exercise

slide-115
SLIDE 115

Express in depth

slide-116
SLIDE 116

Routes

  • Routes are based on verbs
  • GET
  • POST
  • PUT
  • DELETE
  • ALL (not a real verb, but obvious)
slide-117
SLIDE 117

Simple routes

slide-118
SLIDE 118

app.get(‘/’, function(req,res) { res.send(‘hello root’); });

slide-119
SLIDE 119

Routes with variables

slide-120
SLIDE 120

app.get(‘/user/:id’, function(req,res) { res.send(‘hello ‘ + req.params.id); });

slide-121
SLIDE 121

Optional flags in routes

slide-122
SLIDE 122

app.get(‘/:filename?’, function(req,res) { if(req.params.filename) { res.send(req.params.filename); } else { res.send(‘root’); } });

slide-123
SLIDE 123

Regex as routes

slide-124
SLIDE 124

app.get(/\//, function(req, res) { //like using ‘/’ ? res.send(‘/’); });

slide-125
SLIDE 125

app.get(/^\/\d+\/?$/, function(req,res) { res.send(‘matches a number’); });

slide-126
SLIDE 126

app.get(/^\/(.+)\/?$/, function(req,res) { //note translation of %23, etc res.send(‘Got: ’ + req.params[0]); //also captures become an array });

slide-127
SLIDE 127

Using regex to define parameters

slide-128
SLIDE 128

app.get(‘/index.:format((html|json))’, function(req,res) { res.send('Got: ' + req.params.format); });

slide-129
SLIDE 129

app.get('/:id(\d+)', function(req,res) { //only digits, right? res.send(req.params.id); });

slide-130
SLIDE 130

app.get('/:id(\\d+)', function(req,res) { //escape your \ in strings res.send(req.params.id); });

slide-131
SLIDE 131

function normalizePath(path, keys) { path = path .concat('/?') .replace(/\/\(/g, '(?:/') .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){ keys.push(key); slash = slash || ''; return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || '([^/]+?)') + ')' + (optional || ''); }) .replace(/([\/.])/g, '\\$1') .replace(/\*/g, '(.+)'); return new RegExp('^' + path + '$', 'i'); }

Routing magic: Router.js

slide-132
SLIDE 132

Routing magic

  • If . before :variable? then . is also optional
  • If ? is not after a variable then only the previous character is

affected

  • / at the end of URLs automatically optional
  • * Can be used as a wildcard in routes
  • Includes when end of URL is optional

e.g. '/app/e?'

  • Regex can be used any place in a route string

e.g. '/app/(\\d)r?'

slide-133
SLIDE 133

Exercises

  • Create an express app with routes that capture '/'

'/products' and '/services'

  • Create a route that captures the product ID after

'/product/' e.g. '/product/abc12' and returns it in the response

  • Use a regular expression to restrict the ID

parameter to 3 letter followed by 3-5 numbers

  • Create a route using a regex that matches the

entire route

slide-134
SLIDE 134

Passing Control

  • Routes are actually 'stacked middleware'
  • You can pass control between routes
  • The next() function calls the next matching

route

slide-135
SLIDE 135

app.get('/users/:id', function(req, res, next){ var id = req.params.id; if (checkPermission(id)) { // show private page } else { next(); } }); app.get('/users/:id', function(req, res){ // show public user page });

slide-136
SLIDE 136

Passing Control

  • next() is a function of router (and defined in the

closure containing the route)

  • router will grab routes in the order they were

declared

  • e.g. since'/*' will match everything so it should be

the last route!

  • router doesn't care about verbs so you can use all

() to operate on all verbs/routes and then use next () to pass to get(), put(), etc

slide-137
SLIDE 137

Exercises

  • Create a simple check for correct product

IDs if not pass control to a route showing a custom error page

  • Use app.all() to check user permission

before showing (mock-up) edit controls on a web site

slide-138
SLIDE 138

Middleware

slide-139
SLIDE 139

It's a pattern

slide-140
SLIDE 140 next() next() next() req, res
slide-141
SLIDE 141 next() next() next() Dispatcher req res req, res
slide-142
SLIDE 142

req, res, next

slide-143
SLIDE 143

var express = require('express'), app = express.createServer(); var middleware = function (req, res, next) { req.foo = 'bar'; next(); }; app.use(middleware); app.get('/', function(req, res) { res.send(req.foo);

slide-144
SLIDE 144

var express = require('express'), app = express.createServer(); var middleware = function (req, res, next) { var send = res.send; res.send = function(d) { res.send = send; res.send('hijacked! ' + d); } next(); }; app.use(middleware); app.get('/', function(req, res) { res.send('hi'); });

slide-145
SLIDE 145
  • logger
  • bodyParser
  • cookieParser
  • session
  • static
  • errorHandler
  • profiler
  • responseTime
  • basicAuth
  • favicon
  • vhost

Connect middleware

(Renamed express.* for convenience)

slide-146
SLIDE 146

var express = require('express'), app = express.createServer(); app.use(express.logger()); app.use(express.bodyParser()); app.use(express.cookieParser()); app.use(app.router); app.use(express.static(__dirname + '/images')); app.use(express.errorHandler()); app.get('/', function(req, res) { res.send('<html><img src="/image.png"></html'); }); app.listen(9003);

slide-147
SLIDE 147

Ordering matters

slide-148
SLIDE 148

Router uses "internal middleware"

slide-149
SLIDE 149

var express = require('express'), app = express.createServer(); var middleware = function (req, res, next) { req.foo = 'bar'; next(); }; app.get('/', middleware, function(req, res) { res.send(req.foo); });

slide-150
SLIDE 150

var a, b, c, d; a = b = c = d = function(req,res,next) { next(); } var set1 = [a,b]; var set2 = [c,d]; var all = [set1, set2]; app.get('/set1', set1, function(req,res) { res.send('output'); }); app.get('/set2', [c,d], function(req,res) { res.send('output'); }); app.get('/all', all, function(req,res) { res.send('output'); });

slide-151
SLIDE 151

Middleware factories

slide-152
SLIDE 152

Middleware are just functions

slide-153
SLIDE 153

var a, b, c, d; a = b = c = d = function(req,res,next) { next(); } var set1 = [a,b]; var set2 = [c,d]; var all = [set1, set2]; app.get('/set1', set1, function(req,res) { res.send('output'); }); app.get('/set2', [c,d], function(req,res) { res.send('output'); }); app.get('/all', all, function(req,res) { res.send('output'); });

slide-154
SLIDE 154

var mFactory = function(letter) { return function(req,res,next) { var send = res.send; res.send = function(d) { res.send = send; res.send(letter + ' ' + d); } next(); } }; var set1 = [mFactory('a'),mFactory('b')]; var set2 = [mFactory('c'),mFactory('d')]; var all = [set1, set2]; app.get('/set1', set1, function(req,res) { res.send('output'); }); app.get('/set2', set2, function(req,res) { res.send('output'); }); app.get('/all', all, function(req,res) { res.send('output'); });

slide-155
SLIDE 155
  • Create a middleware to detect mobile

phone browsers and attach a boolean to req

  • Create an express app that serves up links

to an image using staticProvider

  • Modify Profiler to profile your app and

write each profile to a log file

  • Create a middleware factory that sets the

HTTP Expires header based on roles

Exercise

slide-156
SLIDE 156

Error handling

slide-157
SLIDE 157

function NotFound(msg){ this.name = 'NotFound'; Error.call(this, msg); Error.captureStackTrace(this, arguments.callee); } NotFound.prototype.__proto__ = Error.prototype; app.get('/404', function(req, res){ throw new NotFound; }); app.get('/500', function(req, res){ throw new Error('keyboard cat!'); });

slide-158
SLIDE 158

app.error(function(err, req, res, next){ if (err instanceof NotFound) { res.render('404.jade'); } else { next(err); } });

slide-159
SLIDE 159

View Rendering

slide-160
SLIDE 160

app.get('/', function(req, res){ res.render('index.ejs', { title: 'Falsy Demo' }); });

slide-161
SLIDE 161

Enki:~/Code/express-demo $ tree . ├── app.js ├── lib ├── public └── views ├── index.ejs ├── layout.ejs ├── layout1.ejs └── partials └── stylesheet.ejs 4 directories, 5 files Enki:~/Code/express-demo $

slide-162
SLIDE 162

npm install ejs npm install jade

Don't forget to install

slide-163
SLIDE 163

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title><%= title %></title> </head> <body> <%- body %> </body> </html>

layout.ejs

slide-164
SLIDE 164
  • layout is a framework unless you

turn it ofg

  • body is a special variable for layout

referring to the file specified

slide-165
SLIDE 165

app.set('view engine', 'ejs'); app.get('/', function(req,res) { res.render('index', { title:'Falsy Demo'}); });

slide-166
SLIDE 166

//global app.set('view options'), { layout: false; }); //or per route res.render(index, {layout: false});

slide-167
SLIDE 167

Partial views

slide-168
SLIDE 168

View Partials

  • Repeating elements
  • Take a collection
  • Iterate over the collection
  • "Built in" variables for managing

collections

slide-169
SLIDE 169

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <%- partial('stylesheet', stylesheets) %> <title><%= title %></title> </head> <body> <h1><%= header %></h1> <%- body %> </body> </html>

layout.ejs

slide-170
SLIDE 170

partials/stylesheet.ejs

<link rel="stylesheet" type="text/css" href="<%- stylesheet %>">

slide-171
SLIDE 171

res.render('index', { locals: {'title': title, 'header': header, 'content': content, stylesheets: ['/public/style.css'] }, } );

slide-172
SLIDE 172
  • Create an express server that use jade, haml, ejs to

render an index page

  • Create a public folder and include file from it (CSS,

images, etc) in your layout

  • Create a route for '/blog/id' to accept only digits
  • Make a 'fake database' (array) of blog posts and use a

middleware to validate each id is valid

  • Create a view for the '/blog/id' show the correct post
  • Use a view partial to a preview of each blog post on

the index page

Exercises

slide-173
SLIDE 173

Questions