CoffeeScript

how to decrease your daily {} count

Titus Stone

automatic var

x = 10
var x;
x = 10

()'s are optional*

x = add 1, 15
var x = add(1, 15);
* = except when there are no arguments

string interpolation

msg = "Hello #{adjective} world"
var msg = "Hello "+ adjective +" world";

skinny functions
+ automatic return

add = (x,y) -> x + y
var add = function(x, y) {
  return x + y;
};

{}'s are optional:

block is implied by indentation

get_urls_from_sitemap = (domain) ->
  xml = http_get("http://#{domain}/sitemap.xml")
  parse_sitemap(xml)
var getUrlsFromSitemap = function(domain) {
  var xml = httpGet("http://"+ domain +"/sitemap.xml");
  return parseSitemap(xml);
};

Real World:

jasmine tests read way cleaner

it "should be true", ->
  expect(true).toBeTrue()
it("should be true", function(){
  expect(true).toBeTrue();
});

default values

call_search_api = (query, domain=".com") ->
  # ...
var callSearchApi = function(query, domain) {
  if (typeof domain === 'undefined') { domain = '.com'; }
}

nice if/then/unless syntax*

do_something() unless x
if y
  do_this()
else
  do_that()
if (x) { doSomething(); }
if (y) {
  doThis();
} else {
  doThat();
}
* = there is no `end` keyword. This will mess you up all day long.

class keyword

class Vehicle
  move: (feet) ->
    # ...
var Vehicle = function(){};
Vehicle.prototype.move = function(feet) {
  // ...
};

@ references the instance

class Vehicle
  move: (feet) ->
    @open_throttle() if @is_started
var Vehicle = function(){};
Vehicle.prototype.move = function(feet) {
  if (this.isStarted) {
    this.openThrottle();
  }
};

fat arrow maintains context

class Button
  constructor: ->
    @url = '/api/places'

  get_places: (e) ->
    $.getJson @url, (json, xhr) =>
        console.log "#{@url} has been fetched and was #{json}"

...including on class methods

class Button
  constructor: (options={}) ->
    $('.button').click @on_click

  on_click: (e) =>
    if @options.whatever
      # ...

automatic instance assignment

class Whatever
  constructor: (url) ->
    @url = url
class Whatever
  constructor: (@url) ->

inheritance works like a boss

class Vehicle
  constructor: (options={}) ->
  move: (feet) ->
    @open_throttle() if @started

class Car < Vehicle
  move: (feet) ->
    @release_brakes()
    super()

Part 2:

My almost framework-less approach to structured front-end code

Most UI code does the same thing:

Initialize:

  • take in some options (model)
  • find some DOM elements (view)
  • bind some events (view)

Lifecycle:

  • react to events (controller)
  • execute business logic (model)
  • update the UI (view)

take in some options (model)

class NewsletterSubscribe
  constructor: (@options={}) ->

find some DOM elements (view)

constructor: (@options={}) ->
  @init_elements()

  init_elements: ->
    @button = $(@options.button)
    @email_input = $(@options.email_input)

bind some events (view)

constructor: (@options={}) ->
  @bind_events()

  bind_events: ->
    @button.click @on_click

react to some events (controller) and execute business logic (model)

on_click: (e) =>
  post_email @email_input.val()

post_email: (email)
  $.post @options.url, =>
    @success_notification()

update the UI (view)

success_notification: ->
  alert "You are now subscribed!"

Altogether, grouped

class NewsletterSubscribe
  
  # -- setup -----------------

  constructor: (@options={}) ->
    @init_elements()
    @bind_events()

  init_elements: ->
    @button = $(@options.button)
    @email_input = $(@options.email_input)

  bind_events: ->
    @button.click @on_click

  # -- logic ------------------

  post_email: (email)
    $.post @options.url

  # -- view -------------------

  success_notification: ->
    alert "You are now subscribed!"

  # -- events ------------------

  on_click: (e) =>
    post_email @email_input.val(), =>
      @success_notification()

But half of that is boilerplate

  • we know we're usually going to initialize elements
  • we know we're usually going to bind events
  • we know that bound events will be fairly similar

What if we introduced a convention about how we describe elements?

data-is = this element IS this

data-does = this element DOES this

HTML

<div class="newsletter-subscribe">
  <p>Subscribe to our newsletter!</p>
  <label>Email</label>
  <input type="text" data-is="email" placeholder="you@example.com" />
  <button data-does="subscribe">Subscribe</button>
</div>

Initialization


:javascript
  new NewsletterSubscribe({
    email: '[data-is="email"]',
    subscribe: '[data-does="subscribe"]'
  });

A common class could scan for these

class UiElement
  constructor: (@options={}) ->
    for key in @options
      if typeof key === 'string'
        and (key.indexOf('data-is') > -1
        or key.indexOf('data-does') > -1)
          @[key] = $(@options[key])
    @init_elements() if typeof @init_elements === 'function'
    @bind_events() if typeof @bind_events === 'function'

Our NewsletterSubscribe class gets shorter

class NewsletterSubscribe < UiElement bind_events: -> @button.click @on_click post_email: (email) $.post @options.url, => @success_notification() success_notification: -> alert "You are now subscribed!" on_click: (e) => post_email @email.val()