Your first cup of CoffeeScript, Part 3: Using CoffeeScript on the client

This series explores the popular CoffeeScript programming language, which is built on top of JavaScript. CoffeeScript compiles into JavaScript that is efficient and consistent with many best practices. You can run this JavaScript in a web browser or use it with technologies such as Node.js for server applications. Part 1 of this series explained how to get started with CoffeeScript and explained the perks for developers. Part 2 described how to use CoffeeScript to solve several programming problems. This article explains how to create a complete application using CoffeeScript.

Michael Galpin, Software Engineer, Google

Michael Galpin's photoMichael Galpin is a software engineer at Google. He is a co-author of the book Android in iPractice and a frequent contributor to developerWorks. For a sneak peek at what he's up to next, check out his blog or follow him on Twitter @michaelg or Google+ Michael Galpin.



14 February 2012

Also available in Chinese Russian Japanese Vietnamese

Introduction

CoffeeScript is a new programming language built on top of JavaScript. CoffeeScript offers a clean syntax that should appeal to anyone who likes Python or Ruby, and it also provides many functional programming features inspired by languages such as Haskell and Lisp.

In Part 1 of this series, you learned about the benefits of using CoffeeScript instead of using just JavaScript. You set up the development environment and ran scripts. In Part 2, you explored the CoffeeScript programming language by trying many of the features while solving mathematical problems.

In this article, you create a sample CoffeeScript application. Both the client-side and server-side code will be written completely in CoffeeScript. You can also download the source code used in this article.


Search powered by CoffeeScript

Frequently used abbreviations

  • JSON: JavaScript Object Notation
  • UI: User interface

The sample application that you'll create lets the user enter a search term, searching both Google and Twitter, and then display the combined results. The client side of the application gets the data the user entered and sends it to the server for results. When the results are returned from the server, the client side creates UI elements for the results and displays them on the screen. For now, don't worry about how the server side works.

Start by defining the data model for the application. The application will display search results, so define a SearchResult class. Listing 1 shows the definition.

Listing 1. Base SearchResult class
class SearchResult
    constructor: (data) ->
        @title = data.title
        @link = data.link
        @extras = data
    
    toHtml: -> "<a href='#{@link}'>#{@title}</a>"
    toJson: -> JSON.stringify @extras

The SearchResult class is rather simple. It:

  • Starts with a constructor that will define two member variables: title and link.
  • Looks for these two values in the data object that is passed to the constructor.
  • Stores the rest of the data passed to a member variable called extras.

    This will be convenient because you know you'll have two different types of search results in the application (those from Google and those from Twitter).

  • Defines two methods for the SearchResult class:
    • toHtml, which creates a simple HTML string out of the SearchResult instance, taking advantage of CoffeeScript's string interpolation.
    • toJson, for turning the SearchResult object into a JSON string.

The class in Listing 1 provides the basic features of a search result. You'll be getting much more data from search results from both Google and Twitter. Model this by creating subclasses for each type of search result. Listing 2 shows the type of search result from Google.

Listing 2. GoogleSearchResult class
class GoogleSearchResult extends SearchResult
    constructor: (data) ->
        super data
        @content = @extras.content
    toHtml: ->
        "#{super} <div class='snippet'>#{@content}</div>"

Listing 2 shows just how easy object-oriented programming is in CoffeeScript. GoogleSearchResult extends the base SearchResult class from Listing 1. Its constructor calls the constructor of the superclass. If you have ever done class-style inheritance in JavaScript, then you know how tricky this can be. Listing 3 shows the generated JavaScript.

Listing 3. GoogleSearchResult JavaScript
GoogleSearchResult = (function() {
  __extends(GoogleSearchResult, SearchResult);
  function GoogleSearchResult(data) {
    GoogleSearchResult.__super__.constructor.call(this, data);
    this.content = this.extras.content;
  }
  GoogleSearchResult.prototype.toHtml = function() {
    return "" + GoogleSearchResult.__super__.toHtml.apply(this, arguments) 
+ " <div class='snippet'>" + this.content + "</div>";
  };
  return GoogleSearchResult;
})();

To call the constructor of the superclass, you must keep an instance of the superclass in the __super__ variable (the name could be anything), and then explicitly call the constructor function. Going back to Listing 2, you can see how much easier this is in CoffeeScript. Notice that the example defined a new instance variable called content in the GoogleSearchResult class. This is basically a snippet of HTML from the web page that the search result points to. It should not be surprising that the GoogleSearchResult has this but the base class does not. Finally, notice the override of the toHtml method. The example uses the superclass's toHtml method but also appends an extra div with the content snippet. Look at Listing 3 again to see how this call to the superclass's toHtml method is accomplished. Because you have a GoogleSearchResult subclass, you need a TwitterSearchResult subclass as well, as shown in Listing 4.

Listing 4. TwitterSearchResult class
class TwitterSearchResult extends SearchResult
    constructor: (data) ->
        super data
        @source = @extras.from_user
        @link = "http://twitter.com/#{@source}/status/#{@extras.id_str}"
        @title = @extras.text
    toHtml: ->
        "<a href='http://twitter.com/#{@source}'>@#{@source}</a>: #{super}"

The TwitterSearchResult class follows the same pattern as the GoogleSearchResult class in Listing 2. Its constructor leverages the constructor of its superclass. It also:

  • Defines its own member variable called source.
  • Constructs the link member variable using a string template and its member variables.
  • Resets the title member variable to a different field from the input data.
  • Overrides the toHtml method of the superclass, appending a link to the user who created the tweet.

Once again, CoffeeScript's string interpolation makes it easy to use the superclass's toHtml method when creating a new one. To call the superclass's toHtml method, you simply call super. You might be tempted to call super.toHtml instead, but there is no need and doing so would actually cause an error. CoffeeScript implies that you want to call the same method on the superclass, making your life just a bit easier.

You now have the data structures that the application will need, and you can begin writing some of the client-side logic. It would be much easier to test the code with a working back end. Because there's not one, let's use the next best thing: mock data.


Using mock data

When building a client-server application like a modern web application, it often makes sense to create the shared interface where the two parts of the application meet and then create mock data. This lets you develop the client and server portions of the application in parallel. The approach works especially well with CoffeeScript because you can use the same programming language on both the client and server. Listing 5 shows mock search results from Google.

Listing 5. Mock Google search results
mockGoogleData = [
        GsearchResultClass:"GwebSearch",
        link:"http://jashkenas.github.com/coffee-script/",
        url:"http://jashkenas.github.com/coffee-script/",
        visibleUrl:"jashkenas.github.com",
        cacheUrl:"http://www.google.com/search?q\u003dcache:nuWrlCK4-v4J:jashkenas
.github.com",
        title:"\u003cb\u003eCoffeeScript\u003c/b\u003e",
        titleNoFormatting:"CoffeeScript",
        content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a little language that 
compiles into JavaScript. Underneath all of   those embarrassing braces and 
semicolons, JavaScript has always had a \u003cb\u003e...\u003c/b\u003e"
    ,
        GsearchResultClass:"GwebSearch",
        link:"http://en.wikipedia.org/wiki/CoffeeScript",
        url:"http://en.wikipedia.org/wiki/CoffeeScript",
        visibleUrl:"en.wikipedia.org",
        cacheUrl:"http://www.google.com/search?q\u003dcache:wshlXQEIrhIJ
:en.wikipedia.org",
        title:"\u003cb\u003eCoffeeScript\u003c/b\u003e - Wikipedia, the free 
encyclopedia",
        titleNoFormatting:"CoffeeScript - Wikipedia, the free encyclopedia",
        content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a programming language 
that transcompiles to JavaScript. The   language adds syntactic sugar inspired by 
Ruby, Python and Haskell to enhance \u003cb\u003e...\u003c/b\u003e"
    ,
        GsearchResultClass:"GwebSearch",
        link:"http://codelikebozo.com/why-im-switching-to-coffeescript",
        url:"http://codelikebozo.com/why-im-switching-to-coffeescript",
        visibleUrl:"codelikebozo.com",
        cacheUrl:"http://www.google.com/search?q\u003dcache:VDKirttkw30J:
codelikebozo.com",
        title:"Why I\u0026#39;m (Finally) Switching to \u003cb\u003eCoffeeScript
\u003c/b\u003e - Code Like Bozo",
        titleNoFormatting:"Why I\u0026#39;m (Finally) Switching to CoffeeScript - 
Code Like Bozo",
        content:"Sep 5, 2011 \u003cb\u003e...\u003c/b\u003e You may have already heard
about \u003cb\u003eCoffeeScript\u003c/b\u003e and some of the hype surrounding it
but you still have found several reasons to not make the \u003cb\u003e...
\u003c/b\u003e"   
]

It's evident that even mock data is easier to create with the simple syntax of CoffeeScript. The example shows object literals in CoffeeScript. Listing 5 is an array. Extra indentation is used to indicate an object, and each property of the object is indented again. This code compares favorably to JSON. The whitespace takes the place of curly braces. They are like JavaScript literals, where the properties are not in quotation marks. With JSON these properties have to be quoted as well.

Listing 6 shows similar mock data for Twitter search results.

Listing 6. Mock Twitter search results
mockTwitterData = [
        created_at:"Wed, 09 Nov 2011 04:18:49 +0000",
        from_user:"jashkenas",
        from_user_id:123323498,
        from_user_id_str:"123323498",
        geo:null,
        id:134122748057370625,
        id_str:"134122748057370625",
        iso_language_code:"en",
        metadata:
            recent_retweets:4,
            result_type:"popular"
        profile_image_url:"http://a3.twimg.com/profile_images/1185870726/gravatar
_normal.jpg",
        source:"<a href="http://itunes.apple.com/us/app/twitter/id409789998?mt
=12&quot; rel="nofollow">Twitter for Mac</a>",
        text:""CoffeeScript [is] the closest I felt to the power I had twenty 
years ago in Smalltalk" - Ward Cunningham (http://t.co/2Wve2V4l) Nice.",
        to_user_id:null,
        to_user_id_str:null
]

The mock data in Listing 6 is similar to the mock data in Listing 5, but with the Twitter-specific fields. Now you just need to create an interface that returns this mock data. Listing 7 shows another class that does just that.

Listing 7. A mock search engine class
class MockSearch
    search: (query, callback) ->
        results = 
            google: (new GoogleSearchResult obj for obj in mockGoogleData)
            twitter: (new TwitterSearchResult obj for obj in mockTwitterData)
        callback results

The MockSearch class has a single method called search. It takes two parameters: query to use for the search, and a callback function. MockSearch returns results quickly, but a real search will need to go over the network to talk to the servers. To handle this in JavaScript, and to make sure you don't cause the UI to freeze up, you would typically use a callback function.

The next step creates an object called results. You're again using the object literal syntax of CoffeeScript. The results object has two fields: google and twitter. The values of each field are expressed using an array comprehension. The expression will create an array of the appropriate type of SearchResult (GoogleSearchResult for Google and TwitterSearchResult for Twitter). Finally, the callback function is invoked with the results object passed to it.

The mock search is working, so you're ready to write the client-side code related to the UI.


Bringing it all together in the browser

Before glueing the UI to the application code, first take a look at the UI you'll be using. Listing 8 shows a very simple web page.

Listing 8. CoffeeSearch web page
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>CoffeeSearch</title>
    <script type="text/javascript" src="search.js"></script>
</head>
<body>
    <div>
        <label for="searchQuery">Keyword:</label>
        <input type="text" name="searchQuery" id="searchQuery"></input>
        <input type="button" value="Search" onclick="doSearch()"/>
    </div>
    <div class="goog" id="gr"/>
    <div class="twit" id="tr"/>
</body>
</html>

The web page above has a basic form for entering a keyword and passing it to a search engine. It has two sections defined and ready for search results to be added to them. The web page has no JavaScript defined in it. Instead, all of it is in a file called search.js, which will be the compiled version of the CoffeeScript. Notice that when the search button is clicked, a function called doSearch is invoked. This function must be in the search.js file; it's the only thing in the search.js file that you haven't seen yet. Listing 9 shows the definition in CoffeeScript.

Listing 9. Web page's doSearch function
@doSearch = ->
    $ = (id) -> document.getElementById(id)
    kw = $("searchQuery").value
    appender = (id, data) ->
        data.forEach (x) -> 
            $(id).innerHTML += "<p>#{x.toHtml()}</p>"
    ms = new MockSearch
    ms.search kw, (results) ->
        appender("gr", results.google)
        appender("tr", results.twitter)

You might notice that the function has an @ symbol prepended to it (a shortcut for this). When defined at the top level of a script, this becomes the global object. In the case of a script in a web page, the global object is the window object, which will allow it to be referenced in the web page you see in Listing 8.

The doSearch function does a lot in just a few lines of code. The code:

  • Defines a local function called $ that's basically a shortcut to the ever useful document.getElementById function. Use this to get the keyword entered in the search form in Listing 8.
  • Defines another local function called appender, which will take the ID of an element in the DOM and an array. It will then iterate over the array, create an HTML string, and append to the element with the given ID.
  • Creates a MockSearch object and invokes its search method.
  • Passes the keyword from the form and creates a callback function.

    The callback function uses appender to append the search results from Google to one div and the search results from Twitter in the other.

Now you can simply compile all of the code and deploy it. Figure 1 shows the web page with the mock data.

Figure 1. Search page with mock data
Search page with mock data

The example might not look impressive, but it demonstrates all of the functions that are needed on the client side. Though you don't have the server side coded yet, you can be fairly confident that the application will work as long as the server-side code produces data structured like the mock data.


Conclusion

In this article, I took CoffeeScript out of the laboratory and used it to start building something real. Combined with node.js, CoffeeScript gives you the opportunity to write a complete application, client and server both, using the same elegant programming language. You're well positioned to reuse some of the same code when building the server side of the application in the final part of this series. Stay tuned.


Download

DescriptionNameSize
Article source codecs3.zip5KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=792636
ArticleTitle=Your first cup of CoffeeScript, Part 3: Using CoffeeScript on the client
publish-date=02142012