- about me - been building things for the web for 15 years - worked in PHP, Rails, .Net (a little) - now I'm excited about Node

Ember in the front, Node in the back

- what are we going to build - a simple clone of Sassmeister.com - ember in the front - node in the back
- the flow of our app: 1. A user enters Sass code into a text area 2. The client-side Ember app will post the input to server via AJAX 3. The server will process the input and respond with a JSON object containing the rendered CSS output 4. Ember will render the returned JSON in a template on our page 5. Profit

Flow of the app

  1. A user enters Sass code into a text area
  2. The client-side Ember app will post the input to the server via AJAX
  3. The server will process the input and respond with a JSON object containing the rendered CSS output
  4. Ember will render the returned JSON in a template on our page
  5. Profit
- file structure - keeping code organized makes life easy - easier to maintain a mental model - easier to debug - node doesn't have a canonical file structure - this is what I'm using currently

File Structure

app/
  controllers/
  index.js
  views/
config/
node_modules/
package.json
public/
  css/
  js/
test/
- `npm init` - it's a wizard

npm init

npm init

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (bad-grizzly)

npm init

name: (bad-grizzly)
version: (0.0.0)
description:
entry point: (index.js)
- entry point - file structure - app/index.js
entry point: (index.js) app/index.js
- For the rest, just keep hitting enter - defaults are fine

(Just keep hitting `Enter`. :-)

- npm init generates a package.json file that contains info on everything needed to build and run your app

package.json

$ cat package.json
{
  "name": "bad-grizzly",
  "version": "0.0.0",
  "description": "bad-grizzly ===========",
  "main": "app/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/jedfoster/bad-grizzly.git"
  },
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/jedfoster/bad-grizzly/issues"
  },
  "homepage": "https://github.com/jedfoster/bad-grizzly"
}

Stupid npm tricks

- npm tricks - scripts are additional npm commands - the key is a command, and the value is either a shell command or the path to a script. - There are a number of valid commands - we're interested in `npm start` - `node app/index.js` - doesn't run because `app/index.js` doesn't exist - we'll revisit scripts again when we write tests

Stupid npm tricks

"scripts": {
  "start": "node app/index.js",
  "test": "echo \"Error: no test specified\" && exit 1"
},

app/

- create /app - create directory and `index.js`

app/

$ mkdir app
$ touch app/index.js
- show node example - but there's an easier way

app/

Hello World example from nodejs.org:

var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');

console.log('Server running at http://127.0.0.1:1337/');
- install Express - `--save` option - updates package.json

Express

$ npm install --save express

Updates our package.json

$ cat package.json
{
  "name": "bad-grizzly",
  "version": "0.0.0",
  ...,
  "dependencies": {
    "express": "^4.8.7"
  }
}
- describe Express - “a minimal and flexible node.js web application framework [for building] web applications.” - handles low-level stuff like `createServer` boilerplate - run npm start - open http://localhost:3000

Express

A simple Express app

var express = require('express');
var app = express();

app.get('/', function(req, res) {
  res.send('Greetings, earthlings!');
});

app.listen('3000');
console.log("The server is now listening on port 3000");

$ npm start

PROFIT!

Stopping the server

<control>-C in your terminal.

- examine index.js - require express - initialize app with express() - route - listen on port

app/

var express = require('express');
var app = express();

app.get('/', function(req, res) {
  res.send('Greetings, earthlings!');
});

app.listen('3000');
console.log("The server is now listening on port 3000");
- HTTP verbs - Express routing methods mirror HTTP verbs

Verbs

Express provides routing methods that mirror HTTP verbs, here we used the GET verb.

app.get('/', function(req, res) {
  res.send('Greetings, earthlings!');
});
- arguments - path - callback - request object - contains query params - response object - methods for manipulating the response - status - send() to send a simple string

Other verbs, like POST, map to similar methods:

app.post('/form', function(req, res) { ... });
- port refactor - node global `process` variable

A minor refactor

We've hardcoded the port number in two places:

app.listen('3000');
console.log("The server is now listening on port 3000");

Use Node's global process object to access the environment

app.port = process.env.PORT || 3000;

app.listen(app.port);
console.log("The server is now listening on port %s", app.port);

The View

Views

This won't get us very far.

app.get('/', function(req, res) {
    res.send('Greetings, earthlings!');
});

Enter Jade

We want to write HTML, we just don't want to write HTML

$ npm install --save jade

Jade is a "terse and simple templating language" for Node.

- explain a little about Jade syntax - no closing tags - no angle brackets - `=` to output JavaScript variables

Jade

We can write this:

doctype html
html(lang="en")
  head
    title= pageTitle
  body
    h1 Jade template engine
    #container.col
      if youAreUsingJade
        p You are amazing
      else
        p Get on it!
      p.
        Jade is a terse and simple templating language...

And it will become:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Jade</title>
  </head>
  <body>
    <h1>Jade template engine</h1>
    <div id="container" class="col">
      <p>You are amazing</p>
      <p>
        Jade is a terse and simple templating language...
- set Jade as the `view engine` and set the `views` directory - explain set() and config options - `__dirname`

Using Jade

var express = require('express');
var app = express();

app.set('view engine', 'jade');
app.set('views', __dirname + '/views');

Our first view

$ mkdir app/views
$ touch app/views/index.jade

Open index.jade and add this code:

doctype html
html
  head
    meta(charset='utf-8')
    meta(http-equiv='X-UA-Compatible' content='IE=edge')
    title= pageTitle
    meta(name='viewport' content='width=device-width, initial-scale=1')
    link(rel='stylesheet' href='/css/app.css')
  body
    h1= 'Greetings, ' + user
- update app/index.js to render - explain render() - arguments - view - hash of local vars - looks in the configured `views` directory for file named `view` - local vars available in the template

Rendering Jade

app.get('/', function(req, res) {
  res.render('index', {
    pageTitle: 'Hello, ' + process.env.USER,
    user: process.env.USER
  });
});
- living in the future - 15 years ago, you'd have to walk uphill, both ways, to find code - modern package managers enable you to release a project in an afternoon that consist of 90% or more third-party code.

Bower

- npm for server-side - bower for client-side - install bower packages from just about anywhere - bower registry - GitHub - private git server - a URL - front-end assets - CSS - JS - images - Sass - if it goes on the front end, you can install it with bower

Intro to Bower

Install packages from:

Install CSS, JavaScript, Sass, images, you name it. If it goes on the front-end you can install it with Bower.

Install

- install with npm - `-g` flag tells bower to install globally - not installed in app's node_modules - available from anywhere

Install Bower:

$ npm install -g bower

Install a Bower package from the registry...

$ bower install <package>

...From a GitHub repo

$ bower install <github-user-name>/<repo-name>

...From a private git server

$ bower install https://git.drft.io/jedfoster/shoestring.git
- by default, bower installs packages to `bower_components` directory - I prefer /public/vendor - create .bowerrc - it's just JSON

Configuring Bower

Customize the install location with a .bowerrc file.

$ touch .bowerrc
- bower will create /public/vendor directory if it doesn't already exist
{
  "directory": "./public/vendor"
}
- just like package.json, we have a bower.json to track front-end dependencies

bower.json

$ touch bower.json
- bower will read packages from bower.json and install those - now have /public/vendor/shoestring-css - just some generic CSS I like to use on my projects
{
  "name": "app-name",
  "version": "1.0.0",
  "dependencies": {
    "shoestring-css": "latest"
  }
}
- note that you would use a bower.json file to _create_ a bower package for publication, same as we've just done. - You've created your first Bower package - won't appear in the registry, but technically it's a bower package that another project could consume
$ bower install
- sidebar on package managers and git - probably a good idea to add node_modules and bower_components to .gitignore - can bloat a git repo

Note on package managers and Git

It's a good idea to add node_modules and bower_components (or equivalent) to your .gitignore

# Dependency directory
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules

# Bower packages
public/vendor
- using vendor assets - update head of index.jade with reference to shoestring

Using Bower packages

head
  meta(charset='utf-8')
  meta(http-equiv='X-UA-Compatible' content='IE=edge')
  title= pageTitle
  meta(name='viewport' content='width=device-width, initial-scale=1')
  link(rel='stylesheet' href='/vendor/shoestring-css/shoestring.css')

Uh oh

- refresh localhost - shoestring.css comes back 404 - use express.static

Configuring Express to serve static files

app.set('view engine', 'jade');

app.set('views', __dirname + '/views');

app.use(express.static(__dirname + '/../public'));

Restart the app and refresh.

Glorious Helvetica Neue!

- review the flow of our app: - continue with server-side to check off #3

Flow of the app

  1. A user enters Sass code into a text area
  2. The client-side Ember app will post the input to server via AJAX
  3. The server will process the input and respond with a JSON object containing the rendered CSS output
  4. Ember will render the returned JSON in a template on our page
  5. Profit
- intro to TDD - write code that tests your code before you write code

Intro to TDD

- helps you catch broken code before it ships - frees you up to refactor application code without fear of breaking things - red, green, refactor - first you write a test that you know will fail, in this case because the application code you're testing doesn't exist - then write your application code so that the test passes - the refactor your code and clean up the campsite
- test tooling - install mocha and supertest - npm install --save-dev

Tools for TDD

$ npm install --save-dev mocha
$ npm install --save-dev supertest
- saves as dev dependencies in package.json - dev dependencies aren't loaded in production environment
"dependencies": {
    "express": "^4.8.7",
    "jade": "^1.6.0"
},
"devDependencies": {
    "mocha": "^1.21.4",
    "supertest": "^0.13.0"
}
- npm test - change test script in package.json to - ./node_modules/mocha/bin/mocha - now `npm test` will run the local mocha binary

npm test

Edit package.json

"test": "./node_modules/mocha/bin/mocha"
$ npm test

Mocha will execute all scripts in test/.

test/

$ mkdir test
$ touch test/test.js
- practice test - edit test/test.js
var assert = require("assert");

describe('When adding 1 and 1', function() {
  it('should return 2', function() {
    assert.equal((1 + 1), 2);
  });
});

Red, green...

$ npm test
$ npm test

> bad-grizzly@0.0.0 test /Code/bad-grizzly
> ./node_modules/mocha/bin/mocha


  When adding 1 and 1
    ✓ should return 2


  1 passing (5ms)
- change to `assert.equal((1 + 1), 3);` - run and watch it fail

Red, green...

Be suspicious if your test passes on the first try.

Change line 3:

assert.equal((1 + 1), 3);
$ npm test

Red, green...

  When adding 1 and 1
    1) should return 2


  0 passing (6ms)
  1 failing

  1) When adding 1 and 1 should return 2:
     AssertionError: 2 == 3
      at Context.<anonymous> (/Code/bad-grizzly/test/test.js:5:12)
      at callFn (/Code/bad-grizzly/node_modules/mocha/lib/runnable.js:249:21)
      at Test.Runnable.run (/Code/bad-grizzly/node_modules/mocha/lib/runnable.js:242:7)
      at Runner.runTest (/Code/bad-grizzly/node_modules/mocha/lib/runner.js:373:10)
      at /Code/bad-grizzly/node_modules/mocha/lib/runner.js:451:12
      at next (/Code/bad-grizzly/node_modules/mocha/lib/runner.js:298:14)
      at /Code/bad-grizzly/node_modules/mocha/lib/runner.js:308:7
      at next (/Code/bad-grizzly/node_modules/mocha/lib/runner.js:246:23)
      at Object._onImmediate (/Code/bad-grizzly/node_modules/mocha/lib/runner.js:275:5)
      at processImmediate [as _immediateCallback] (timers.js:336:15)


npm ERR! Test failed.  See above for more details.
npm ERR! not ok code 0
- real test - paste code - require supertest - require our app - supertest will actually run our and submit HTTP requests to just like if it were loaded in the browser - like Express, supertest gives us methods named after HTTP verbs - you call on a verb method with the desired route, and then state what we expect from a successful response - in this case we call post on /compile and expect a status of 200

Red, green...

A real test.

var request = require('supertest');
var app = require('../app/index.js');

describe("POST /compile", function() {
  it("responds successfully", function(done) {
    request(app)
      .post('/compile')
      .expect(200, done);
  });
});

SuperTest runs the app and simulates HTTP requests, just like we would make from the browser.

Red, green...

$ npm test
The server is now listening on port 3000

  POST /compile
    1) responds successfully

  0 passing (4ms)
  1 failing

  1) POST /compile responds successfully:
     TypeError: Object #<Object> has no method 'address'
      at Test.serverAddress (/Code/bad-grizzly/node_modules/supertest/lib/test.js:57:18)

      ...

npm ERR! Test failed.  See above for more details.
npm ERR! not ok code 0
- fails - because we have not exported our app - require('../app/index.js') isn't returning a usable object - I ran into this the first time I tried writing tests like this - add module.exports to app

Red, green...

We need to export our app in app/index.js.

Edit line 2:

var app = module.exports = express();

Now the app is a proper module and can be included into other scripts, like our test.

Red, green...

$ npm test
- now it fails because the server is returning 404 - to be expected, because we have written our /compile route yet - this is the "red" in "red, green, refactor"
POST /compile
  1) responds successfully


0 passing (18ms)
1 failing

1) POST /compile responds successfully:
  Error: expected 200 "OK", got 404 "Not Found"

Red, green..

When practicing TDD, your tests should fail the first time, so you know that you have actually fixed the feature. This is the "red" in "red, green, refactor".

The test fails because we don't have a /compile route. Add one:

app.post('/compile', function(req, res) {
  res.status(200).end();
});
$ npm test
- we have a passing test, but it's not really doing anything - need to test submission and processing of input - move quickly as this part is either familiar or not pertinent to this exercise - install body-parser and node-sass - npm install --save

...Refactor

On its own, Express can't parse the body of a request.

- need middleware - node modules that sit either between the request or the response and the app - body-parser does this for us
$ npm install --save body-parser
$ npm install --save node-sass

We'll also need node-sass.

- require node-sass and body-parser - explain app.use(bodyParser.json()); - tells express to use body-parser middleware and configures body-parser to expect JSON encoded request body

...Refactor

Add this, starting on line 3.

var nodeSass = require('node-sass');
var bodyParser = require('body-parser');

app.use(bodyParser.json());
- paste route code - process input and return compiled CSS - if the render succeeds we respond with a JSON object - if it fails, we respond with a status code of 500 and the error message - npm test - fails because we aren't submitting any data

A real route

Replace app.post('/compile') with this:

app.post('/compile', function(req, res) {
  var stats = {};

  var css = nodeSass.render({
    data: req.body.compiler.sass + ' ',
    outputStyle: req.body.compiler.outputStyle,
    stats: stats,

    success: function(css) {
      res.json({
        compiler: {
          css: css,
          stats: stats
        }
      });
    },

    error: function(error) {
      res.status(500).send(error);
    }
  });
});
- new test code - sending a JSON object, just as we would from the browser - also setting the content-type of the request. - we expect that the response will be JSON - we also expect that the body will contain a contain a key, `css`, with a certain value

A complete test

We need to submit some data with our test.

request(app)
  .post('/compile')
  .send({
    compiler: {
      sass: '$red: #f00;\n.test {\n  color: $red;\n}',
      outputStyle: 'compressed'
    }
  })
  .set('Content-Type', 'application/json')
  .expect(function(res) {
    if(res.body.compiler.css != '.test{color:#f00;}') throw new Error('expected ".test{color:#f00;}", got "' + res.body.compiler.css + '"');
  })
  .expect('Content-Type', /json/)
  .expect(200, done);

..Refactor

$ npm test
  POST /compile
    ✓ responds successfully (40ms)


  1 passing (43ms)

YAY!

We'll see this again soon.

Green

Here's a closer look at the JSON the server is returning:

{
  "compiler": {
    "css": ".test{color:#f00;}",
    "stats": {
      "entry": "data",
      "start": 1410121632247,
      "includedFiles": [],
      "end": 1410121632248,
      "duration": 1
    }
  }
}

One more refactor

Making Ember happy

When it creates a record, the Ember Data uses POST, but subsequent updates use PUT, which we have not accounted for.

We need to perform the same action with two different verbs.

- we aren't building a traditional REST app - there's no CRUD - if this were a blog app you might have `posts` that could be created, read, updated, and deleted - but our request and response objects don't really need that kind of modeling - using Ember though, and I wanted to build this the "Ember way" - so we will use the Ember Data library and its RESTAdapter - Ember data has opinions though. - even though we aren't modeling our data on the server-side, we'll need to model it on the client side - so ember data will treat our input like a model, complete with the CRUD methods - one of Ember Data's quirks is that when it creates a record (submits our input for the first time) it will POST to /compile - but on subsequent changes it's going to _update_ the "model" with a _PUT_ request to /compile - long story short, so, we need to refactor our /compile route so that it can respond to both POST and PUT - which is easy to do because all the interesting functionality happens in the callback to post() - show old code - paste new code - now our app will respond to both POST and PUT with the same function

Refactor the route

var compile = function(req, res) {
  var stats = {};

  var css = nodeSass.render({
    data: req.body.compiler.sass,
    outputStyle: req.body.compiler.outputStyle,
    stats: stats,

    success: function(css) {
      res.json({
        compiler: {
          css: css,
          stats: stats
        }
      });
    },

    error: function(error) {
      res.status(500).send(error);
    }
  });
};

app.post('/compile', compile);
app.put('/compile', compile);

With that, our server is complete. On to the client!

- you can start your node server in another terminal window and leave it running; everything from here on will be client-side

Ember

Installing Ember

Install Ember and Ember Data with Bower:

$ bower install ember#~1.7.0 --save
$ bower install ember-data#~1.0.0-beta.9 --save
- Like npm, bower offers a `--save` option that will update bower.json for you - NOTE: we're specifying versions of Ember and Ember data here - bower lets you specify with `$ bower install #` - we’ll accept any patch-level version that is at least 1.0.0-beta.9, but not greater than 1.0 - I spent a week troubleshooting issues with an outdated version of Ember Data I received when I ran just `bower install ember-data`

NOTE: The #~1.0.0-beta.9 is very important. Bower lets you specify a version for a package by adding a # followed by the desired version.

$ bower install <package>#<version>

I spent a week troubleshooting issues with an outdated version of Ember Data I received when I ran just bower install ember-data. I want to save you that grief.

- we need a file for our application-specifc JS - create public/js directory and js/app.js

app.js

Need application-specific JavaScript.

$ mkdir public/js
$ touch public/js/app.js
- include the JS in index.jade - not including ember data yet

index.jade

doctype html
html
  head
    meta(charset='utf-8')
    meta(http-equiv='X-UA-Compatible' content='IE=edge')
    title= pageTitle
    meta(name='viewport' content='width=device-width, initial-scale=1')
    link(rel='stylesheet' href='/vendor/shoestring-css/shoestring.css')

  body

    script(src='/vendor/jquery/dist/jquery.min.js')
    script(src='/vendor/handlebars/handlebars.min.js')
    script(src='/vendor/ember/ember.js')
    script(src='/js/app.js')
- create a new instance of Ember.Application and makes it available as App - similar to how we created our Node app with - var app = express();

Ember.Create()

Open js/app.js and add this:

var App = Ember.Application.create();
- html is nothing but script tags at this point

You now have an Ember application.

- paste this into index.jade and refresh

index.jade

Let's mock up our app.

.left
  h3 Input

  textarea(name='sass')

  select(name='outputStyle')
    option(value='nested') Nested
    option(value='compressed') Compressed

.right
  h3 Output

  pre
    code

  p

Put some Handlebars on it

- let's make this more dynamic - we wrap the content in a script tag, with a special type - replaced the textarea with some Handlebars markup - and we added some handlebars to our code tag

Handlebars

script(type='text/x-handlebars')
  .left
    h3 Input

    {{textarea value=sass}}
    select(name='outputStyle')
      option(value='nested') Nested
      option(value='compressed') Compressed

  .right
    h3 Output

    pre
      code
        {{sass}}
    p
- the `text/x-handlebars` type attribute on the script tag tells Ember that this should be parsed with handlebars - ember will render the contents of this tag when the page loads.

Ember will render the contents of this script tag to the page when it loads.

Data binding

Try typing something into our input field.

With that simple markup we bound our input to another element on the page.

Congratulations, you just wrote your first Ember template.

Ember 101

- let's pause and go over some core concepts of ember, then we'll get back to our app - Ember follows the Model-View-Controller pattern, similar to Rails - the concepts you find throughout most Ember app are:

Ember 101

Ember follows the Model-View-Controller pattern.

Some core concepts:

- Routes and router - routes are the urls for the various states of objects in your app - say a blog post's edit state versus its read state - those would be two different URLs - Ember places significant importance on URLs - a route links a model with its template - the router is a manifest of the routes in your app - technically a route is optional - we've already proven that with our simple example - with no configuration, given a URL /foo, Ember will render a template foo with an instance of model Foo - in practice you'll usually want to define your route explicitly

Routes and the Router

Routes are the URLs for the various states of the objects in your app.

Ember places significant importance on the URL.

A route links a model with its template.

The router is a manifest of the routes in your app. It also keeps the URL in the browser in sync with your app.

Technically, a route is optional, as we've already proven. However, in practice, you'll probably want to define a route explicitly.

- templates - templates are the V in Ember's MVC - it describes the UI for a model - it's written in handlebars - it updates itself as its model changes

Templates

Templates are the V in Ember's MVC. A template describes the UI for a model, and is written in Handlebars.

Templates update themselves as their model's data changes.

- models - models store the persistent state of objects in the app - commonly model data is retrieved from a database via a REST API

Models

Models store the persistent state of objects in our app. Commonly, a model's data is retrieved from a database through a REST API.

- controllers - a controller acts as an intermediary between a model and a template - it allows you to manipulate data either before it is displayed or before it is stored - it is optional - if you don't create one, Ember will make one for you in memory

Controllers

A controller acts an intermediary between the model and template.

Allows you to manipulate data either before it is displayed or before it is stored.

Controllers are optional; if you don't create one, Ember will auto-generate one for you in memory.

- views - despite the name, views are not the view in MVC - handlebars templates are powerful enough to describe most of your UI - you generally won't need these - views are primarily used for complex, custom event handling - can turn a click event into an application event - views are also used for creating reusable components - Ember includes a number of views for HTML elements such as form inputs and links

Views

Views are primarily used for complex, custom event handling and creating reusable components.

Ember includes a number of views for HTML elements such as form inputs and links.

- components - components are standalone views - they enable you to build a reusable element and can help simplify your templates - we won't be using these in our app

Components

A component is a standalone view. It enables you to build reusable elements and can help simplify your templates.

- helpers - Handlebars lets you "register" functions that modify data before display - helpful for formatting data

Helpers

Handlebars allows you to "register" helper functions that modify data before it's displayed. Useful for date formatting for example.

How the pieces fit together

- naming conventions - Ember favors convention over configuration - most obvious in naming things - these conventions make it very easy to wire your app together without unnecessary boilerplate - example is a URL - show users url objects - if you try to go against Embers conventions, you're going to have a bad time - just let it happen

Naming conventions

Ember favors convention over configuration. Ember's naming conventions glue your app together without unnecessary boilerplate.

For a URL /users, Ember will look for these objects:

If you go against Ember's naming conventions, you're going to have a bad time. Just let it happen.

- conventions make magic possible - we've seen some magic already

Magic

Conventions enable magic! We've already seen some of that magic happen with our simple template.

Ember gives you some things for free:

- our app is leveraging these already - show code - our template doesn't have a name so Ember assumes it is the application tempalte - Ember also gave us the ApplicationRoute and ApplicationController for free

Our app is leveraging these conventions:

script(type='text/x-handlebars')
  .left
    h3 Input

Our template doesn't have a name, so Ember uses it as the application template. Ember also created a route and controller for us.

- now that we understand some conventions lets get back to our app

Iterate

- we now know that we are using the application template by default - let's be more specific - paste code

Moving beyond defaults

Let's use the index template in index.jade:

script(type='text/x-handlebars' id='index')
  .left
- let's also add an App.IndexRoute - paste code - now we have a default value for your input

While we're at it, let's add an App.IndexRoute with a model to app.js:

var App = Ember.Application.create();

App.IndexRoute = Ember.Route.extend({
  model: function () {
    return { };
  }
});
- let's rewrite our select menu using handlebars - Ember.Select is a built-in view for rendering select elements - it take an array of values and a string for the currently selected item

Ember.Select

script(type='text/x-handlebars' id='index')
  .left
    h3 Input

    {{textarea value=sass}}

    {{view Ember.Select content=outputStyles value=selectedOutputStyle}}
- we'll need a controller to set up those variables - remember a controller is a bridge between the model and the view, and can be used to decorate the model with additional data.

We'll need to create a controller to set up the values.

App.IndexController = Ember.ObjectController.extend({
  outputStyles: ['nested', 'compressed'],

  selectedOutputStyle: 'nested'
});

Ember Data

Ember Data

Add it to index.jade.

script(src='/vendor/jquery/dist/jquery.min.js')
script(src='/vendor/handlebars/handlebars.min.js')
script(src='/vendor/ember/ember.js')
script(src='/vendor/ember-data/ember-data.js')
script(src='/js/app.js')
- we need a better model - for the most part ember is less opinionated about model names

A more complete model

var App = Ember.Application.create();

App.Compiler = DS.Model.extend({
  sass: DS.attr('string'),
  outputStyle: DS.attr('string'),
  css: DS.attr('string'),
  stats: DS.attr()
});
- notice how our model contains attributes for both the request and response?

Note, this contains attributes for both the request and response.

- ember data expects that the object it hands off to the data store and the object it receives will be of the same shape - but our /compiler route, isn't a standard data store - the object it returns is different than the one it received

API recap

The objects that our API is expecting:

// The request
{
  "compiler": {
    "sass": "$red: #f00;\n.test {\n  color: $red;\n}",
    "outputStyle": "compressed"
  }
}

// The response
{
  "compiler": {
    "css": ".test{color:#f00;}",
    "stats": {
      ...
    }
  }
}
- we can make ember happy by combining our request and response models - our server will just ignore the properties it doesn't need - likewise on the client-side, we'll just ignore the unnecessary properties in the response

The object Ember will use:

var App = Ember.Application.create();

App.Compiler = DS.Model.extend({
  sass: DS.attr('string'),
  outputStyle: DS.attr('string'),
  css: DS.attr('string'),
  stats: DS.attr()
});
- we need to link this model to our route - we have to do this manually because our model and route do not share a name - `createRecord` returns an empty instance of our model

Models in action

Add this model to the route:

App.IndexRoute = Ember.Route.extend({
  model: function () {
    return this.store.createRecord('compiler');
  }
});

This creates an empty instance of our model.

AJAX

by default, ember data will use its RESTAdapter but we need to make some minor modifications

RESTAdapter

Ember Data's RESTAdapter tries to infer URLs from model names.

The default path is the plural form of the model name.

Given a model, Post, RESTAdapter would make requests to /posts

- our route doesn't follow those conventions

Given our Compiler model, it would try to use /compilers. But that won't work...

Setting the API endpoint

Our model isn't conventional, so we need to set the URL explicitly.

- fortunately, the rest adapter is designed to be flexible - we define our own `buildURL` method and force it to return the URL we want
var App = Ember.Application.create();

App.ApplicationAdapter = DS.RESTAdapter.extend({
  buildURL: function(type, id, record) {
    return '/compile'
  }
});

By defining our own buildURL method we can force it to use the the path we've already built.

- need to do a little more plumbing before we can actually communicate with the server

Action!

Let's add a button to our form so that we can manually invoke the API call for debugging.

  {{view Ember.Select content=outputStyles value=selectedOutputStyle}}

  <button {{action 'compile'}}> Go</button>

.right

We use the built-in helper action which fires an event, compile, on click.

Action!

Listen for the compile event in our controller:

App.IndexController = Ember.ObjectController.extend({
  outputStyles: ['nested', 'compressed'],

  selectedOutputStyle: 'nested',

  actions: {
    compile: function() {
      alert("Button clicked");
    }
  }
});

Action!

Refresh, paste .test { color: red; } into the into the input, and click "Go". Yay!

Now, let's actually POST input to the server:

actions: {
  compile: function() {
    this.get('model').save()
      .then(function(response) {
        console.log('Success: ', response);
      });
  }
}

Action!

Refresh. Paste .test { color: red; }. Click.

Now in the console, we should see something like:

- now, let's insert the rendered CSS into our template

Action!

Let's show the CSS returned by the API:

pre
  code
    {{css}}
- notice, that's _all_ we have to do - css is a property of our model and calling save() automatically updated the values in the template

Our output is updated whenever we change the value of the input.

- the API doesn't return a value for sass, and as we now know, save() updates the model, which updates the template - when we click go, our input is cleared

Better UX

Because the API doesn't return a value for sass, the model updates itself with its blank default value.

When we click "Go", our input is cleared. We need our input to not be so tightly linked to the model.

- change textarea to {{textarea value=sassInput}}

Let's decouple our input template from the model.

{{textarea value=sassInput}}

Now, set the model attribute to the value in the template.

compile: function() {
  this.set('sass', this.get('sassInput'));

  this.get('model').save()
    .then(function(response) {
      console.log('Success: ', response);
  });
}

Now, as we click "Go" our input remains.

- accessing properties - notice in templates we access vars directly? {{sassInput}} - but in the controller, we use this.get('sassInput')

Getters and setters

In the template, we access our model's properties directly by name {{sassInput}}, {{sass}}.

But in our controller we access those properties with this.get('sassInput') or this.set('sass'). Why?

- data binding is one of Embers most powerful features - earlier, binding allowed us to mirror our textarea with about 3 lines of code - using get() and set() allows observing objects to be notified when a property changes and update itself appropriately - you _can_ access properties directly, but shouldn't do it - this leads us to...

Data binding, one of Ember's most powerful features.

Earlier, binding allowed us—with three lines of code!—to mirror our textarea.

get() and set() notifies observing objects when a property changes.

Which leads us to...

- clicking buttons to send input is sooo 2003! - we really want our input to be able to send itself whenever its value changes

Observing the form

Our input should send itself whenever it is changed.

- we really want our input to be able to send itself whenever its value changes

Wrap our compile action in an observer:

selectedOutputStyle: 'nested',

compileOnEntry: function() {
  this.send('compile');
}.observes('sassInput'),

Refresh. Paste $red: blue; .test { color: $red; }.

Automatic updating FTW!

A few minor tweaks

- right now we aren't actually sending a value for outputStyle - look at the network panel and see null for that property

outputStyle

We aren't sending a value for outputStyle.

Easy fix:

this.set('sass', this.get('sassInput'));
this.set('outputStyle', this.selectedOutputStyle);
- we should observe selectedOutputStyle and recompile when it changes

Observe the select menu

We should observe selectedOutputStyle and recompile when that changes:

- can fix this by wrapping save() in a conditional that checks if sassInput is empty
compileOnEntry: function() {
  this.send('compile');
}.observes('sassInput', 'selectedOutputStyle'),
- back in the network panel, you may notice that Ember is making a request to /compile immediately after the page loads

Unnecessary requests

Ember is making a request to /compile immediately after the page loads.

Wrap save() in a conditional that checks if sassInput is blank:

if(! Ember.isBlank(this.get('sassInput'))) {
  this.get('model').save()
    .then(function(response) {
      console.log('Success: ', response);
    });
}
- we're only handling successful responses now

Errors

We should do something with unsuccessful responses.

- save() returns a promise - we've already supplied a success callback argument to then() - now we add an error callback - We need to add var self = this in order to call set() within the error handler

save() returns a promise. We already have the "success" callback argument to then(), so all we have to do is add a "fail" callback:

var self = this;

...

if(! Ember.isBlank(this.get('sassInput'))) {
  this.get('model').save()
    .then(function(response) {
      console.log('Success: ', response);
    },
    function(err) {
      console.log('Error: ', err);
      self.set('css', err.responseText);
    });
}

One more thing

- additional routes - so far we've only used the index route that Ember provides by convention - time to define our own route - Sassmeister has a feature where you can load a previously saved snippet as a github gist - let's do something like that

Routes

Let's show a previously saved snippet of Sass, just like on SassMeister.com.

Add a route for /gist

- we're adding a URL to the router's manifest
var App = Ember.Application.create();

App.Router.map(function() {
  this.resource('gist', { path: '/gist/:gist_id' });
});
- if we change the hash, the browser fires a hashchange event which tells the route to change the state of the app

Prettier URLs

Ember's router uses URL fragments (hashes) to identify URLs.

Our app will now respond to a URL like /#/gist/1.

Hashes are ugly.

Ember makes it easy to change.

App.Router.reopen({
  location: 'history'
});
- this requires a minor change to our server

Prettier URLs

If we visit /gist/1 right now, our server will return 404.

Stop the server and modify the index route to this:

var index = function(req, res) {
  res.render('index', {
    pageTitle: 'Hello, ' + process.env.USER,
    user: process.env.USER
  });
};

app.get('/', index);
app.get('/gist/*', index);

Just as we refactored the /compile route to respond to both POST and PUT, we refactor / so we can reuse the functionality for /gist/1.

A template for every route

We don't have a template for this route, so we won't seeing anything if go to /gist/1.

- because we don't have a model associated with this route
script(type='text/x-handlebars' id='gist')
  .left
    h3 Sass

  pre
    code
      {{sass}}

  .right
    h3 CSS

    pre
      code
        {{css}}

script(src='/vendor/jquery/dist/jquery.min.js')

And a model...

We also don't have a model associated with this route.

Refactor App.Compiler, replacing it with this:

var schema = {
  sass: DS.attr('string'),
  css: DS.attr('string'),
  outputStyle: DS.attr('string'),
  stats: DS.attr()
};

App.Compiler = DS.Model.extend(schema);

App.Gist = DS.Model.extend(schema);

Refactoring code is a crucial practice. Take every opportunity to break your code into reusable chunks. It keeps your codebase clean and maintainable.

The correct endpoint

Still not seeing anything at /gist/1.

Let's have a look at the console.

Ember is trying to load the model with GET /compile, but that route only responds to POST and PUT.

The correct endpoint

Our route is /gist; so Ember passes gist as the type argument to buildURL. We can conditionally return a different URL for different types of models.

App.ApplicationAdapter = DS.RESTAdapter.extend({
  buildURL: function(type, id, record) {
    if(type == 'gist') return 'http://gist.drft.io/gists/' + id + '.json';

    return '/compile';
  }
});

I created an Amazon S3 bucket that has a few JSON files that match the models we defined in our Ember app.

Routes FTW!

Refresh, and we now see our template populated with JSON loaded from a remote server. Whee!

- Remember how Ember gives us an application template for free? - Let’s look a gift horse in the mouth and add our own application template.

Application template

Remember how Ember gives us an `application` template for free? Let's look a gift horse in the mouth and add our own `application` template.

body

  script(type='text/x-handlebars')
    nav
      {{#link-to 'index'}}Home{{/link-to}}
      {{#link-to 'gist' 1}}Gist 1{{/link-to}}
      {{#link-to 'gist' 2}}Gist 2{{/link-to}}
      {{#link-to 'gist' 3}}Gist 3{{/link-to}}

    {{outlet}}
- {{outlet}} is a placeholder for other templates - router will update outlet when the state of the app changes - {{#link-to}} is a built-in helper that builds links... to stuff.

{{outlet}} is a placeholder for other templates.

{{#link-to}} is a built-in Ember helper that builds links... to stuff.

Fin

Congratulations

You've just built a Node.js server with a REST API and an Ember front end. Along the way you used: the command line, npm, Node, bower, Express, LibSass, SCSS, HTTP verbs, ports, TDD, mocha, supertest, JSON, MVC, Jade, HTML, CSS, Ember, Ember Data, Handlebars, AJAX, URLs, and the history API.

And all in under 200 lines of code!