<?xml version="1.0" encoding='utf-8'?>
<?xml-stylesheet type="text/xsl" href="https://raibledesigns.com/roller-ui/styles/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" 
      xmlns:app="http://www.w3.org/2007/app"
      xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">

    <title type="html">Search for [css] in weblog rd</title>
    <subtitle type="html">Search results for [css] within weblog Raible Designs</subtitle>
    <id>https://raibledesigns.com/rd/feed/entries/atom?q=css</id>

    <link rel="self" type="application/atom+xml" 
        href="https://raibledesigns.com/rd/feed/entries/atom?q=css" />

    <link rel="alternate" type="text/html" 
        href="https://raibledesigns.com/rd/search?q=css" />

    <link rel="search" type="application/opensearchdescription+xml" 
        href="https://raibledesigns.com/roller-services/opensearch/rd" />
    <opensearch:Query role="request" searchTerms="css" startPage="1" />

    <link rel="first" type="application/atom+xml" href="https://raibledesigns.com/rd/feed/entries/atom?q=css" />
    <link rel="next" type="application/atom+xml" href="https://raibledesigns.com/rd/feed/entries/atom?q=css&amp;page=1" />
    <updated>2026-05-09T04:29:55-06:00</updated>
    <generator uri="http://roller.apache.org" version="5.0.3 (1388864191739:dave)">Apache Roller</generator>

        <entry>
        <id>https://raibledesigns.com/rd/entry/the_angular_mini_book_1</id>
        <title type="html">The Angular Mini-Book 1.0 is now available!</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/the_angular_mini_book_1"/>
        <published>2021-11-17T14:48:40-07:00</published>
        <updated>2021-11-19T14:09:46-07:00</updated> 
        <category term="/Open Source" label="Open Source" />
        <category term="springboot" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="minibook" scheme="http://roller.apache.org/ns/tags/" />
        <category term="infoq" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular" scheme="http://roller.apache.org/ns/tags/" />
        <category term="typescript" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">I&apos;m pleased to announce that the Angular Mini-Book has been released! You can &lt;a href=&quot;http://www.infoq.com/minibooks/angular-mini-book&quot;&gt;download it in PDF and EPUB formats from InfoQ&lt;/a&gt;.
&lt;p&gt;
&lt;a href=&quot;http://www.infoq.com/minibooks/angular-mini-book&quot; data-href=&quot;https://www.flickr.com/photos/mraible/51687063929/&quot; title=&quot;Angular Mini-Book Cover&quot;&gt;&lt;img srcset=&quot;https://live.staticflickr.com/65535/51687063929_f89f0e0f71_n.jpg 480w, https://live.staticflickr.com/65535/51687063929_f89f0e0f71_n.jpg 800w&quot; sizes=&quot;(max-width: 600px) 480px, 800px&quot; src=&quot;https://live.staticflickr.com/65535/51687063929_f89f0e0f71_n.jpg&quot; class=&quot;picture&quot; alt=&quot;Angular Mini-Book Cover&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;
&lt;/a&gt;
&lt;h3&gt;About this book&lt;/h3&gt;
&lt;p&gt;The Angular Mini-Book is a guide to getting started with Angular. You&apos;ll learn how to develop a bare-bones application, test it, and deploy it. Then you&apos;ll move on to adding Bootstrap, Angular Material, continuous integration, and authentication. Spring Boot is a popular framework for building REST APIs. You&apos;ll learn how to integrate Angular with Spring Boot and use security best practices like HTTPS and a content security policy.
&lt;/p&gt;
&lt;p&gt;
This initial edition (v1.0) uses Angular 12 and Spring Boot 2.5. I do plan on updating it for Angular 13 and Spring Boot 2.6. If you have any tips for upgrading, please let me know!&lt;/p&gt;
&lt;h3&gt;Purpose of the book&lt;/h3&gt;
&lt;p&gt;I think building web and mobile applications with Angular, Bootstrap, and Spring Boot is a great experience. I&apos;d like to encourage more developers to try it.&lt;/p&gt;
&lt;h3&gt;Thanks!&lt;/h3&gt;
&lt;p&gt;I&apos;m incredibly grateful to Trish, Abbie, and Jack. They put up with my late nights and extended screen time while I worked on this book.
&lt;/p&gt;</summary>
        <content type="html">I&apos;m pleased to announce that the Angular Mini-Book has been released! You can &lt;a href=&quot;http://www.infoq.com/minibooks/angular-mini-book&quot;&gt;download it in PDF and EPUB formats from InfoQ&lt;/a&gt;.
&lt;p&gt;
&lt;a href=&quot;http://www.infoq.com/minibooks/angular-mini-book&quot; data-href=&quot;https://www.flickr.com/photos/mraible/51687063929/&quot; title=&quot;Angular Mini-Book Cover&quot;&gt;&lt;img srcset=&quot;https://live.staticflickr.com/65535/51687063929_f89f0e0f71_n.jpg 480w, https://live.staticflickr.com/65535/51687063929_f89f0e0f71_n.jpg 800w&quot; sizes=&quot;(max-width: 600px) 480px, 800px&quot; src=&quot;https://live.staticflickr.com/65535/51687063929_f89f0e0f71_n.jpg&quot; class=&quot;picture&quot; alt=&quot;Angular Mini-Book Cover&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;
&lt;/a&gt;
&lt;h3&gt;About this book&lt;/h3&gt;
&lt;p&gt;The Angular Mini-Book is a guide to getting started with Angular. You&apos;ll learn how to develop a bare-bones application, test it, and deploy it. Then you&apos;ll move on to adding Bootstrap, Angular Material, continuous integration, and authentication. Spring Boot is a popular framework for building REST APIs. You&apos;ll learn how to integrate Angular with Spring Boot and use security best practices like HTTPS and a content security policy.
&lt;/p&gt;
&lt;p&gt;
This initial edition (v1.0) uses Angular 12 and Spring Boot 2.5. I do plan on updating it for Angular 13 and Spring Boot 2.6. If you have any tips for upgrading, please let me know!&lt;/p&gt;
&lt;h3&gt;Purpose of the book&lt;/h3&gt;
&lt;p&gt;I think building web and mobile applications with Angular, Bootstrap, and Spring Boot is a great experience. I&apos;d like to encourage more developers to try it.&lt;/p&gt;
&lt;h3&gt;Thanks!&lt;/h3&gt;
&lt;p&gt;I&apos;m incredibly grateful to Trish, Abbie, and Jack. They put up with my late nights and extended screen time while I worked on this book.
&lt;/p&gt;
&lt;p&gt;
To &lt;a href=&quot;https://twitter.com/mhevery&quot;&gt;Mi&#353;ko Hevery&lt;/a&gt;, thank you for inventing Angular and changing the lives of frontend developers. To the whole Angular team, I appreciate you and all you do for the community. Kudos for making our apps faster with every release!
&lt;/p&gt;
&lt;p&gt;
To &lt;a href=&quot;https://twitter.com/phillip_webb&quot;&gt;Phil Webb&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/david_syer&quot;&gt;Dave Syer&lt;/a&gt;, thanks for creating Spring Boot and simplifying Java for everyone. Hats off to the whole Spring team for their tireless dedication to quality open source projects.
&lt;/p&gt;
&lt;p&gt;
I want to thank this book&apos;s tech editor, &lt;a href=&quot;https://twitter.com/deepu105&quot;&gt;Deepu K Sasidharan&lt;/a&gt;. I looked to him for his deep experience with both TypeScript and JavaScript. Many sections are more streamlined because of his advice.
&lt;/p&gt;
&lt;p&gt;
This book&apos;s copy editor, Maureen Spencer, helped correct my grammar and make this book easier on the eyes. I&apos;m thankful for your help, Maureen.
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/reverentgeek&quot;&gt;David Neal&lt;/a&gt; created the cover image and all of the illustrations. He&apos;s awesome! You should consider &lt;a href=&quot;https://reverentgeek.com/shop/&quot;&gt;buying his stuff&lt;/a&gt; and follow him for his dad jokes.
&lt;p&gt;
Finally, my compliments to you, potential reader. It&apos;s a heckuva time to be writing code. Enjoy your learning adventures! &amp;#x1F603;
&lt;/p&gt;
&lt;h3&gt;Fun facts&lt;/h3&gt;
&lt;p&gt;Creating this book was more difficult than I imagined. As you can see from the timeline below, I didn&apos;t do a whole lot in 2020. I ramped things up this spring and tried to get &apos;er done before Angular 13 and Spring Boot 2.6 were released. At the very least, I succeeded on the second goal, even if only by a couple days.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First commit: June 25, 2020&lt;/li&gt;
&lt;li&gt;Outline created: September 15, 2020&lt;/li&gt;
&lt;li&gt;Started writing: October 3, 2020&lt;/li&gt;
&lt;li&gt;Upgraded to Angular 12 and Spring Boot 2.4.1: May 4, 2021&lt;/li&gt;
&lt;li&gt;Finished first draft: June 30, 2021&lt;/li&gt;
&lt;li&gt;Build date: November 16, 2021&lt;/li&gt;
&lt;li&gt;Total commits: 108&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This book was authored with &lt;a href=&quot;https://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt; and I appreciate the assistance I received from &lt;a href=&quot;https://twitter.com/Mogztter&quot;&gt;Guillaume Grossetie&lt;/a&gt;.
&lt;p&gt;The source code repo contains all the demos in addition to the book&apos;s code. The &lt;a href=&quot;https://github.com/AlDanial/cloc&quot;&gt;Count Lines of Code&lt;/a&gt; report (excluding generated files and directories) shows that it&apos;s mostly words, followed by example code:&lt;/p&gt;
&lt;pre&gt;
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
AsciiDoc                         14           1651             20           5312
HTML                             38            211              0           2349
TypeScript                       86            322            299           2057
JSON                             27              0              0           1346
CSS                              11            265            250           1288
YAML                             10              8             89            654
Kotlin                           18             87              4            499
JavaScript                       12             35             48            355
Gradle                            6             43              4            227
Bourne Shell                      2             50            144            225
&lt;/pre&gt;
&lt;p&gt;I&apos;m proud to have built the v1.0 version of the book on my parent&apos;s 49th anniversary. Happy Anniversary Mom and Dad!! &amp;#x1F973;&lt;/p&gt;
&lt;p&gt;To send us feedback or issues, e-mail InfoQ at feedback@infoq.com, email me at matt@raibledesigns.com, or hit me up on Twitter &lt;a href=&quot;https://twitter.com/mraible&quot;&gt;@mraible&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/announcing_angular_crud_2_0</id>
        <title type="html">Announcing Angular CRUD 2.0</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/announcing_angular_crud_2_0"/>
        <published>2021-09-27T16:29:15-06:00</published>
        <updated>2021-09-27T22:31:13-06:00</updated> 
        <category term="/Open Source" label="Open Source" />
        <category term="opensource" scheme="http://roller.apache.org/ns/tags/" />
        <category term="crud" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;Friends shouldn&apos;t let friends write CRUD apps. At least, not by hand. That&apos;s why I used a schematic called Angular CRUD in one of my last &lt;a href=&quot;https://developer.okta.com/blog/2020/01/06/crud-angular-9-spring-boot-2&quot;&gt;Angular + Spring Boot blog posts&lt;/a&gt;.  That post is kinda old (January 2020), but the combination of Angular and Spring Boot remains popular. That&apos;s why I decided to turn the series into an Angular Mini-Book for InfoQ.&lt;/p&gt;
&lt;p&gt;The book has five different sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build an Angular App&lt;/li&gt;
&lt;li&gt;Integrate Angular with Spring Boot&lt;/li&gt;
&lt;li&gt;Beautiful Angular Apps with Bootstrap&lt;/li&gt;
&lt;li&gt;Angular Deployment&lt;/li&gt;
&lt;li&gt;Angular and Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;
My goal with the book is to show you everything you need to get your Angular + Spring Boot app to production. But, what about &lt;a href=&quot;https://jhipster.tech&quot;&gt;JHipster&lt;/a&gt;? &lt;/p&gt;
&lt;p&gt;
Of course, you can just use JHipster, but I&apos;ve found that a lot of beginners are intimidated by all the code it generates. That&apos;s why I wanted to create a bare-bones Angular guide that uses Spring Boot for its API.
&lt;/p&gt;
&lt;p&gt;
I published the news on Twitter at the end of June.
&lt;/p&gt;
&lt;div style=&quot;margin: 0 auto; max-width: 600px&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I&amp;#39;m happy to announce that I&amp;#39;ve finished the first draft of the &lt;a href=&quot;https://twitter.com/angular?ref_src=twsrc%5Etfw&quot;&gt;@Angular&lt;/a&gt; Mini-Book! After the editing process, you&amp;#39;ll be able to find it at a local &lt;a href=&quot;https://twitter.com/InfoQ?ref_src=twsrc%5Etfw&quot;&gt;@InfoQ&lt;/a&gt; website.&lt;br&gt;&lt;br&gt;Hopefully, they&amp;#39;ll create a better cover for it. &amp;#x1F605; &lt;a href=&quot;https://twitter.com/hashtag/angular?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#angular&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/springboot?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#springboot&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/asciidoctor?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#asciidoctor&lt;/a&gt; &lt;a href=&quot;https://t.co/el20GCS24s&quot;&gt;pic.twitter.com/el20GCS24s&lt;/a&gt;&lt;/p&gt;&amp;mdash; Matt Raible (@mraible) &lt;a href=&quot;https://twitter.com/mraible/status/1410413147765325826?ref_src=twsrc%5Etfw&quot;&gt;July 1, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;p&gt;
As part of creating the book, I updated &lt;a href=&quot;https://twitter.com/ManfredSteyer&quot;&gt;Manfred Styer&apos;s&lt;/a&gt; &lt;a href=&quot;https://github.com/manfredsteyer/angular-crud&quot;&gt;Angular CRUD&lt;/a&gt; project and released &lt;a href=&quot;https://github.com/manfredsteyer/angular-crud/releases/tag/v2.0.0&quot;&gt;v2.0&lt;/a&gt;. The 2.0 release adds support for Bootstrap and Angular Material for CSS framework aficionados, like me. 
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;Friends shouldn&apos;t let friends write CRUD apps. At least, not by hand. That&apos;s why I used a schematic called Angular CRUD in one of my last &lt;a href=&quot;https://developer.okta.com/blog/2020/01/06/crud-angular-9-spring-boot-2&quot;&gt;Angular + Spring Boot blog posts&lt;/a&gt;.  That post is kinda old (January 2020), but the combination of Angular and Spring Boot remains popular. That&apos;s why I decided to turn the series into an Angular Mini-Book for InfoQ.&lt;/p&gt;
&lt;p&gt;The book has five different sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build an Angular App&lt;/li&gt;
&lt;li&gt;Integrate Angular with Spring Boot&lt;/li&gt;
&lt;li&gt;Beautiful Angular Apps with Bootstrap&lt;/li&gt;
&lt;li&gt;Angular Deployment&lt;/li&gt;
&lt;li&gt;Angular and Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;
My goal with the book is to show you everything you need to get your Angular + Spring Boot app to production. But, what about &lt;a href=&quot;https://jhipster.tech&quot;&gt;JHipster&lt;/a&gt;? &lt;/p&gt;
&lt;p&gt;
Of course, you can just use JHipster, but I&apos;ve found that a lot of beginners are intimidated by all the code it generates. That&apos;s why I wanted to create a bare-bones Angular guide that uses Spring Boot for its API.
&lt;/p&gt;
&lt;p&gt;
I published the news on Twitter at the end of June.
&lt;/p&gt;
&lt;div style=&quot;margin: 0 auto; max-width: 600px&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I&amp;#39;m happy to announce that I&amp;#39;ve finished the first draft of the &lt;a href=&quot;https://twitter.com/angular?ref_src=twsrc%5Etfw&quot;&gt;@Angular&lt;/a&gt; Mini-Book! After the editing process, you&amp;#39;ll be able to find it at a local &lt;a href=&quot;https://twitter.com/InfoQ?ref_src=twsrc%5Etfw&quot;&gt;@InfoQ&lt;/a&gt; website.&lt;br&gt;&lt;br&gt;Hopefully, they&amp;#39;ll create a better cover for it. &amp;#x1F605; &lt;a href=&quot;https://twitter.com/hashtag/angular?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#angular&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/springboot?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#springboot&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/asciidoctor?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#asciidoctor&lt;/a&gt; &lt;a href=&quot;https://t.co/el20GCS24s&quot;&gt;pic.twitter.com/el20GCS24s&lt;/a&gt;&lt;/p&gt;&amp;mdash; Matt Raible (@mraible) &lt;a href=&quot;https://twitter.com/mraible/status/1410413147765325826?ref_src=twsrc%5Etfw&quot;&gt;July 1, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;p&gt;
As part of creating the book, I updated &lt;a href=&quot;https://twitter.com/ManfredSteyer&quot;&gt;Manfred Styer&apos;s&lt;/a&gt; &lt;a href=&quot;https://github.com/manfredsteyer/angular-crud&quot;&gt;Angular CRUD&lt;/a&gt; project and released &lt;a href=&quot;https://github.com/manfredsteyer/angular-crud/releases/tag/v2.0.0&quot;&gt;v2.0&lt;/a&gt;. The 2.0 release adds support for Bootstrap and Angular Material for CSS framework aficionados, like me. 
&lt;/p&gt;
&lt;p&gt;
You can see the &lt;a href=&quot;https://github.com/manfredsteyer/angular-crud#tutorial-getting-started&quot;&gt;getting started tutorial&lt;/a&gt; for how to use it in your projects. To inspire you, I&apos;ve included some screenshots of the new Bootstrap and Angular Material support.
&lt;/p&gt;
&lt;h3&gt;Angular CRUD + Bootstrap&lt;/h3&gt;
&lt;table style=&quot;width: 100%; margin-bottom: 20px&quot;&gt;
&lt;tr&gt;
  &lt;td style=&quot;vertical-align: top&quot;&gt;&lt;a href=&quot;https://imgur.com/8KICSze.png&quot;&gt;&lt;img src=&quot;https://camo.githubusercontent.com/70cef827ab68234b0cf51046eacf6d9f8c1b2699b878a5428328eeb70c7052e4/68747470733a2f2f696d6775722e636f6d2f384b4943537a652e706e67&quot; alt=&quot;Bootstrap List&quot; data-canonical-src=&quot;https://imgur.com/8KICSze.png&quot; style=&quot;max-width: 100%;&quot;&gt;&lt;/a&gt;&lt;/td&gt;
  &lt;td style=&quot;vertical-align: top&quot;&gt;&lt;a href=&quot;https://camo.githubusercontent.com/40fec5afdc6e795f15eec13e5fb15c9a3e089a644ecaa8e06ca60734aa6b8297/68747470733a2f2f696d6775722e636f6d2f5658446f5562422e706e67&quot;&gt;&lt;img src=&quot;https://camo.githubusercontent.com/40fec5afdc6e795f15eec13e5fb15c9a3e089a644ecaa8e06ca60734aa6b8297/68747470733a2f2f696d6775722e636f6d2f5658446f5562422e706e67&quot; alt=&quot;Bootstrap Detail View&quot; data-canonical-src=&quot;https://imgur.com/VXDoUbB.png&quot; style=&quot;max-width: 100%;&quot;&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h3&gt;Angular CRUD + Angular Material&lt;/h3&gt;
&lt;p&gt;This theme is based on &lt;a href=&quot;https://zoaibkhan.com/blog/create-a-responsive-sidebar-menu-with-angular-material/&quot;&gt;Create a responsive sidebar menu with Angular Material&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/zoaibdev&quot;&gt;Zoaib Khan&lt;/a&gt;.
&lt;table style=&quot;width: 100%; margin-bottom: 20px&quot;&gt;
&lt;tr&gt;
  &lt;td style=&quot;vertical-align: top&quot;&gt;&lt;a href=&quot;https://camo.githubusercontent.com/a69145407e91ac02664c2f40782a7a3f7f517d2a0829a4d9e18eae90723cb9eb/68747470733a2f2f696d6775722e636f6d2f663466554543452e706e67&quot;&gt;&lt;img src=&quot;https://camo.githubusercontent.com/a69145407e91ac02664c2f40782a7a3f7f517d2a0829a4d9e18eae90723cb9eb/68747470733a2f2f696d6775722e636f6d2f663466554543452e706e67&quot; alt=&quot;Angular Material List&quot; data-canonical-src=&quot;https://imgur.com/f4fUECE.png&quot; style=&quot;max-width: 100%;&quot;&gt;&lt;/a&gt;&lt;/td&gt;
  &lt;td style=&quot;vertical-align: top&quot;&gt;&lt;a href=&quot;https://camo.githubusercontent.com/754ee14d548dab0c2a7eb3840a128b5e1a42fb0578d36c61387cb76914e53e14/68747470733a2f2f696d6775722e636f6d2f56547669754d532e706e67&quot;&gt;&lt;img src=&quot;https://camo.githubusercontent.com/754ee14d548dab0c2a7eb3840a128b5e1a42fb0578d36c61387cb76914e53e14/68747470733a2f2f696d6775722e636f6d2f56547669754d532e706e67&quot; alt=&quot;Angular Material Detail View&quot; data-canonical-src=&quot;https://imgur.com/VTviuMS.png&quot; style=&quot;max-width: 100%;&quot;&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h3&gt;Introduction to Angular CRUD Video&lt;/h3&gt;
&lt;p&gt;
I also published an &lt;a href=&quot;https://youtu.be/3mqWrmZtefE&quot;&gt;Introduction to Angular CRUD video&lt;/a&gt; that shows how it works.
&lt;/p&gt;
&lt;div style=&quot;text-align: center&quot;&gt;
&lt;iframe width=&quot;700&quot; height=&quot;394&quot; style=&quot;max-width: 100%&quot; src=&quot;https://www.youtube.com/embed/3mqWrmZtefE&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;
You might ask, &quot;When will the Angular Mini-Book be published?&quot;
&lt;/p&gt;
&lt;p&gt;
We&apos;re about halfway through tech editing. I hope to finish that in the next month, pass it on to copy editing, and publish before the end of the year.&lt;/p&gt;
&lt;p&gt;Keep an eye on &lt;a href=&quot;https://www.infoq.com/minibooks/&quot;&gt;InfoQ&apos;s Mini-Books&lt;/a&gt; and follow me &lt;a href=&quot;https://twitter.com/mraible&quot;&gt;@mraible&lt;/a&gt; on Twitter for updates. 
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/getting_started_with_angular_cli</id>
        <title type="html">Getting Started + Testing with Angular CLI and Angular 2 (RC5)</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/getting_started_with_angular_cli"/>
        <published>2016-08-23T17:18:41-06:00</published>
        <updated>2017-04-28T20:38:39-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jasmine" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="karma" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="typescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular-cli" scheme="http://roller.apache.org/ns/tags/" />
        <category term="protractor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I started creating Angular 2 applications when it was in beta (&lt;a
    href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;back in March&lt;/a&gt;). To keep up with Angular 2&apos;s
    changes,
    I wrote a &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;tutorial about developing with
        RC1&lt;/a&gt; in June. Earlier this month, RC5 was released
    and &lt;a href=&quot;https://angular.io/docs/ts/latest/cookbook/rc4-to-rc5.html&quot;&gt;many things changed once again&lt;/a&gt;. I think
    Scott Davis sums it up nicely in a tweet.&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; max-width: 500px&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;They keep saying &quot;Release Candidate&quot;, but I
        don&apos;t think it means what they think it means...&lt;br&gt;&lt;br&gt;/cc &lt;a
            href=&quot;https://twitter.com/hashtag/angular2?src=hash&quot;&gt;#angular2&lt;/a&gt; &lt;a
            href=&quot;https://twitter.com/hashtag/rc5?src=hash&quot;&gt;#rc5&lt;/a&gt; &lt;a href=&quot;https://t.co/WmNalTYgTN&quot;&gt;https://t.co/WmNalTYgTN&lt;/a&gt;
    &lt;/p&gt;&amp;mdash; Scott Davis (@scottdavis99) &lt;a href=&quot;https://twitter.com/scottdavis99/status/763399251229417472&quot;&gt;August
        10, 2016&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;p&gt;To keep up with the rapid pace of change in Angular 2, I decided to write another tutorial, this time using &lt;a
    href=&quot;https://github.com/angular/angular-cli&quot;&gt;Angular CLI&lt;/a&gt;. The biggest change I found since
    writing the last tutorial is testing infrastructure changes. Since &lt;a
        href=&quot;https://angular.io/docs/ts/latest/testing/&quot;&gt;Angular&apos;s Testing documentation&lt;/a&gt; hasn&apos;t been updated
    recently, hopefully this tutorial will help.&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;I started creating Angular 2 applications when it was in beta (&lt;a
    href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;back in March&lt;/a&gt;). To keep up with Angular 2&apos;s
    changes,
    I wrote a &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;tutorial about developing with
        RC1&lt;/a&gt; in June. Earlier this month, RC5 was released
    and &lt;a href=&quot;https://angular.io/docs/ts/latest/cookbook/rc4-to-rc5.html&quot;&gt;many things changed once again&lt;/a&gt;. I think
    Scott Davis sums it up nicely in a tweet.&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; max-width: 500px&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;They keep saying &quot;Release Candidate&quot;, but I
        don&apos;t think it means what they think it means...&lt;br&gt;&lt;br&gt;/cc &lt;a
            href=&quot;https://twitter.com/hashtag/angular2?src=hash&quot;&gt;#angular2&lt;/a&gt; &lt;a
            href=&quot;https://twitter.com/hashtag/rc5?src=hash&quot;&gt;#rc5&lt;/a&gt; &lt;a href=&quot;https://t.co/WmNalTYgTN&quot;&gt;https://t.co/WmNalTYgTN&lt;/a&gt;
    &lt;/p&gt;&amp;mdash; Scott Davis (@scottdavis99) &lt;a href=&quot;https://twitter.com/scottdavis99/status/763399251229417472&quot;&gt;August
        10, 2016&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;p&gt;To keep up with the rapid pace of change in Angular 2, I decided to write another tutorial, this time using &lt;a
    href=&quot;https://github.com/angular/angular-cli&quot;&gt;Angular CLI&lt;/a&gt;. The biggest change I found since
    writing the last tutorial is testing infrastructure changes. Since &lt;a
        href=&quot;https://angular.io/docs/ts/latest/testing/&quot;&gt;Angular&apos;s Testing documentation&lt;/a&gt; hasn&apos;t been updated
    recently, hopefully this tutorial will help.&lt;/p&gt;
&lt;p&gt;Below is a table of contents in case you want to skip right to a particular section.&lt;/p&gt;

&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
    &lt;ul&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_create_your_project&quot;&gt;Create your project&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_run_the_application&quot;&gt;Run the application&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_add_a_search_feature&quot;&gt;Add a search feature&lt;/a&gt;
            &lt;ul class=&quot;sectlevel2&quot;&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_the_basics&quot;&gt;The Basics&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_the_backend&quot;&gt;The Backend&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_add_an_edit_feature&quot;&gt;Add an edit feature&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_testing&quot;&gt;Testing&lt;/a&gt;
            &lt;ul class=&quot;sectlevel2&quot;&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_unit_test_the_searchservice&quot;&gt;Unit test the SearchService&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_unit_test_the_searchcomponent&quot;&gt;Unit test the SearchComponent&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_integration_test_the_search_ui&quot;&gt;Integration test the search UI&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_testing_the_search_feature&quot;&gt;Testing the search feature&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_testing_the_edit_feature&quot;&gt;Testing the edit feature&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_continuous_integration&quot;&gt;Continuous Integration&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_source_code&quot;&gt;Source code&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
&lt;/div&gt;

&lt;h3 id=&quot;_what_you_ll_build&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_what_you_ll_build&quot;&gt;&lt;/a&gt;What you&apos;ll build&lt;/h3&gt;
&lt;p&gt;You&apos;ll build a simple web application with Angular CLI, a new tool for Angular 2 development. You&apos;ll
    create an application with search and edit features.&lt;/p&gt;

&lt;h3 id=&quot;_what_you_ll_need&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_what_you_ll_need&quot;&gt;&lt;/a&gt;What you&apos;ll need&lt;/h3&gt;

&lt;ul&gt;
    &lt;li&gt;About 30-40 minutes.
    &lt;/li&gt;
    &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ
        IDEA&lt;/a&gt; and its
        &lt;a href=&quot;https://plugins.jetbrains.com/plugin/8395?pr=idea&quot;&gt;Angular 2 TypeScript Live
            Templates
            plugin&lt;/a&gt;.
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and npm installed. I recommend using &lt;a
        href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/angular/angular-cli&quot;&gt;Angular CLI&lt;/a&gt; installed. If you don&apos;t have
        Angular CLI installed, install it using &lt;code&gt;npm install -g angular-cli@latest&lt;/code&gt;.
    &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The latest release of Angular CLI (beta 10) uses Angular 2 RC4. Because of this, I used
    the master branch of Angular CLI to create this tutorial. To do this, clone &lt;a
        href=&quot;https://github.com/angular/angular-cli&quot;&gt;angular-cli&lt;/a&gt; and
    run &lt;code&gt;npm link&lt;/code&gt; in the directory you cloned it into. If you have issues,
    see &lt;a href=&quot;https://github.com/angular/angular-cli/issues/1773&quot;&gt;#1733&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
    &lt;a href=&quot;https://augury.angular.io/&quot;&gt;Angular Augury&lt;/a&gt; is a Google Chrome Dev Tools
    extension for debugging Angular 2 applications.
    I haven&apos;t needed it much myself, but I can see how it might come in handy.&lt;/p&gt;

&lt;h3 id=&quot;_create_your_project&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_create_your_project&quot;&gt;&lt;/a&gt;Create your project&lt;/h3&gt;
&lt;p&gt;Create a new project using the &lt;code&gt;ng new&lt;/code&gt; command:&lt;/p&gt;

&lt;pre&gt;ng new ng2-demo&lt;/pre&gt;

&lt;p&gt;This will create a &lt;code&gt;ng2-demo&lt;/code&gt; project and run &lt;code&gt;npm install&lt;/code&gt; in it. It takes
    about a minute to complete, but will vary based on your internet connection speed.&lt;/p&gt;

&lt;pre&gt;[mraible:~/dev] 45s $ ng new ng2-demo
installing ng2
  create .editorconfig
  create README.md
  create src/app/app.component.css
  create src/app/app.component.html
  create src/app/app.component.spec.ts
  create src/app/app.component.ts
  create src/app/environment.ts
  create src/app/index.ts
  create src/app/shared/index.ts
  create src/favicon.ico
  create src/index.html
  create src/main.ts
  create src/system-config.ts
  create src/tsconfig.json
  create src/typings.d.ts
  create angular-cli-build.js
  create angular-cli.json
  create config/environment.dev.ts
  create config/environment.js
  create config/environment.prod.ts
  create config/karma-test-shim.js
  create config/karma.conf.js
  create config/protractor.conf.js
  create e2e/app.e2e-spec.ts
  create e2e/app.po.ts
  create e2e/tsconfig.json
  create e2e/typings.d.ts
  create .gitignore
  create package.json
  create public/.npmignore
  create tslint.json
  create typings.json
Successfully initialized git.
- Installing packages for tooling via npm
  -- es6-shim (global)
  -- angular-protractor (global dev)
  -- jasmine (global dev)
  -- selenium-webdriver (global dev)

Installed packages for tooling via npm.
[mraible:~/dev] 1m5s $&lt;/pre&gt;

&lt;p&gt;You can see the what version of Angular CLI you&apos;re using with &lt;code&gt;ng --version&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;$ ng --version
angular-cli: local (v1.0.0-beta.11-webpack.2, branch: master)
node: 4.4.7
os: darwin x64&lt;/pre&gt;

&lt;h3 id=&quot;_run_the_application&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_run_the_application&quot;&gt;&lt;/a&gt;Run the application&lt;/h3&gt;

&lt;p&gt;The project is configured with a simple web server for development. To start it, run:&lt;/p&gt;

&lt;pre&gt;ng serve&lt;/pre&gt;

&lt;p&gt;You should see a screen like the one below at &lt;a href=&quot;http://localhost:4200&quot;&gt;http://localhost:4200&lt;/a&gt;.&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/default-homepage.png&quot;
         alt=&quot;Default Homepage&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;You can make sure your new project&apos;s tests pass, run &lt;code&gt;ng test&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;$ ng test
Built project successfully. Stored in &quot;dist/&quot;.
...
Chrome 52.0.2743 (Mac OS X 10.11.6): Executed 2 of 2 SUCCESS (0.039 secs / 0.012 secs)&lt;/pre&gt;

&lt;h3 id=&quot;_add_a_search_feature&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_add_a_search_feature&quot;&gt;&lt;/a&gt;Add a search feature&lt;/h3&gt;
&lt;p&gt;To add a search feature, open the project in an IDE or your favorite text editor. For IntelliJ IDEA, use
    File &amp;gt; New Project &amp;gt; Static Web and point to the &lt;code&gt;ng2-demo&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3 id=&quot;_the_basics&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_the_basics&quot;&gt;&lt;/a&gt;The Basics&lt;/h3&gt;
&lt;p&gt;In a terminal window, cd into your project&apos;s directory and run the following command. This will
    create a search component.&lt;/p&gt;
&lt;pre&gt;$ ng g component search
installing component
  create src/app/search/search.component.css
  create src/app/search/search.component.html
  create src/app/search/search.component.spec.ts
  create src/app/search/search.component.ts
  create src/app/search/index.ts&lt;/pre&gt;

&lt;div class=&quot;quote&quot;&gt;
    &lt;p style=&quot;margin-top: 0&quot;&gt;&lt;strong&gt;Adding a Search Route&lt;/strong&gt;&lt;br&gt;

        In previous versions of CLI, you could generate a route
        &lt;strong&gt;and&lt;/strong&gt; a component. However, since beta 8, route generation has been
        disabled. This will likely be re-enabled in a future release.&lt;/p&gt;

    &lt;p&gt;The &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/router.html&quot;&gt;Router
        documentation&lt;/a&gt;
        for Angular 2 RC5 provides the information you need to
        setup a route to the &lt;code&gt;SearchComponent&lt;/code&gt; you just generated. Here&apos;s a quick
        summary:&lt;/p&gt;

    &lt;p&gt;Create &lt;code&gt;src/app/app.routing.ts&lt;/code&gt; to define your routes.&lt;/p&gt;

    &lt;pre class=&quot;brush: js&quot;&gt;
import { Routes, RouterModule } from &apos;@angular/router&apos;;
import { SearchComponent } from &apos;./search/index&apos;;

const appRoutes: Routes = [
  { path: &apos;search&apos;, component: SearchComponent },
  { path: &apos;&apos;, redirectTo: &apos;/search&apos;, pathMatch: &apos;full&apos; }
];

export const appRoutingProviders: any[] = [];

export const routing = RouterModule.forRoot(appRoutes);
&lt;/pre&gt;
    &lt;p class=&quot;alert alert-warning&quot;&gt;
        Without the last path to redirect, there&apos;s a &lt;a
        href=&quot;http://stackoverflow.com/questions/38998085/error-cannot-match-any-routes-in-angular-2-rc5-with-angular-cli&quot;&gt;Cannot
        match any routes: &apos;&apos;&lt;/a&gt; console error.
    &lt;/p&gt;
    &lt;p&gt;In &lt;code&gt;src/app/app.module.ts&lt;/code&gt;, import the two constants you exported and
        configure them in &lt;code&gt;@NgModule&lt;/code&gt;:&lt;/p&gt;

    &lt;pre class=&quot;brush: js&quot;&gt;
import { routing, appRoutingProviders } from &apos;./app.routing&apos;;

import { SearchComponent } from &apos;./search/search.component&apos;;

@NgModule({
  ...
  imports: [
    ...
    routing
  ],
  providers: [appRoutingProviders],
  ...
})
export class AppModule { }
&lt;/pre&gt;
    &lt;p&gt;In &lt;code&gt;src/app/app.component.html&lt;/code&gt;, add a &lt;code&gt;RouterOutlet&lt;/code&gt; to display routes.&lt;/p&gt;

    &lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;!-- Routed views go here --&amp;gt;
&amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;
&lt;/pre&gt;
    &lt;p style=&quot;margin-bottom: 0&quot;&gt;Now that you have routing setup, you can continue writing the search feature.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To allow navigation to the &lt;code&gt;SearchComponent&lt;/code&gt;, you can add a link in &lt;code&gt;src/app/app.component.html&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;nav&amp;gt;
  &amp;lt;a routerLink=&quot;/search&quot; routerLinkActive=&quot;active&quot;&amp;gt;Search&amp;lt;/a&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;src/app/search/search.component.html&lt;/code&gt; and replace its default HTML with the following:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;h2&amp;gt;Search&amp;lt;/h2&amp;gt;
&amp;lt;form&amp;gt;
  &amp;lt;input type=&quot;search&quot; name=&quot;query&quot; &amp;#91;(ngModel)&amp;#93;=&quot;query&quot; (keyup.enter)=&quot;search()&quot;&amp;gt;
  &amp;lt;button type=&quot;button&quot; (click)=&quot;search()&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;pre&amp;gt;{{searchResults | json}}&amp;lt;/pre&amp;gt;
&lt;/pre&gt;
&lt;p&gt;If you still have &lt;code&gt;ng serve&lt;/code&gt; running, your browser should refresh automatically.
    If not, navigate to &lt;a href=&quot;http://localhost:4200&quot;&gt;http://localhost:4200&lt;/a&gt;, and you should see
    the search form.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/search-without-css.png&quot;
         alt=&quot;Search component&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;If you want to add CSS for this components, open &lt;code&gt;src/app/search/search.component.css&lt;/code&gt; and
    add some CSS. For example:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
:host {
  display: block;
  padding: 0 20px;
}
&lt;/pre&gt;
&lt;p&gt;This section has shown you how to generate a new component to a basic Angular 2 application
    with Angular CLI. The next section shows you how to create a use a JSON file and &lt;code&gt;localStorage&lt;/code&gt; to
    create a fake API.&lt;/p&gt;

&lt;h3 id=&quot;_the_backend&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_the_backend&quot;&gt;&lt;/a&gt;The Backend&lt;/h3&gt;
&lt;p&gt;To get search results, create a &lt;code&gt;SearchService&lt;/code&gt; that makes HTTP requests to a JSON
    file. Start by generating a new service.&lt;/p&gt;
&lt;pre&gt;ng g service search&lt;/pre&gt;
&lt;p&gt;Move the generated &lt;code&gt;search.service.ts&lt;/code&gt; and its test to &lt;code&gt;app/shared/search&lt;/code&gt;. You
    will likely need to create this directory.&lt;/p&gt;
&lt;p&gt;Then, create &lt;code&gt;src/app/shared/search/data/people.json&lt;/code&gt; to hold your data.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Peyton Manning&quot;,
    &quot;phone&quot;: &quot;(303) 567-8910&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;1234 Main Street&quot;,
      &quot;city&quot;: &quot;Greenwood Village&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80111&quot;
    }
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Demaryius Thomas&quot;,
    &quot;phone&quot;: &quot;(720) 213-9876&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;5555 Marion Street&quot;,
      &quot;city&quot;: &quot;Denver&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80202&quot;
    }
  },
  {
    &quot;id&quot;: 3,
    &quot;name&quot;: &quot;Von Miller&quot;,
    &quot;phone&quot;: &quot;(917) 323-2333&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;14 Mountain Way&quot;,
      &quot;city&quot;: &quot;Vail&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;81657&quot;
    }
  }
]
&lt;/pre&gt;
&lt;p&gt;Modify &lt;code&gt;src/app/shared/search/search.service.ts&lt;/code&gt; and provide &lt;code&gt;Http&lt;/code&gt; as
    a dependency in its constructor. In this same file, create a &lt;code&gt;getAll()&lt;/code&gt; method to gather all the people. Also,
    define the &lt;code&gt;Address&lt;/code&gt; and &lt;code&gt;Person&lt;/code&gt; classes that JSON will be marshalled to.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Injectable } from &apos;@angular/core&apos;;
import { Http, Response } from &apos;@angular/http&apos;;

@Injectable()
export class SearchService {
  constructor(private http: Http) {}

  getAll() {
    return this.http.get(&apos;app/shared/search/data/people.json&apos;).map((res: Response) =&gt; res.json());
  }
}

export class Address {
  street: string;
  city: string;
  state: string;
  zip: string;

  constructor(obj?: any) {
    this.street = obj &amp;&amp; obj.street || null;
    this.city = obj &amp;&amp; obj.city || null;
    this.state = obj &amp;&amp; obj.state || null;
    this.zip = obj &amp;&amp; obj.zip || null;
  }
}

export class Person {
  id: number;
  name: string;
  phone: string;
  address: Address;

  constructor(obj?: any) {
    this.id = obj &amp;&amp; Number(obj.id) || null;
    this.name = obj &amp;&amp; obj.name || null;
    this.phone = obj &amp;&amp; obj.phone || null;
    this.address = obj &amp;&amp; obj.address || null;
  }
}
&lt;/pre&gt;
&lt;p&gt;To make these classes available for consumption by your components, edit &lt;code&gt;src/app/shared/index.ts&lt;/code&gt;
    and add the following:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;export * from &apos;./search/search.service&apos;;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;search.component.ts&lt;/code&gt;, add imports for these classes.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Person, SearchService } from &apos;../shared/index&apos;;&lt;/pre&gt;
&lt;p&gt;You can now add &lt;code&gt;query&lt;/code&gt; and &lt;code&gt;searchResults&lt;/code&gt; variables. While you&apos;re
    there, modify the constructor to inject the &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
export class SearchComponent implements OnInit {
  query: string;
  searchResults: Array&amp;lt;Person&gt;;

  constructor(private searchService: SearchService) {}
&lt;/pre&gt;
&lt;p&gt;Then implement the &lt;code&gt;search()&lt;/code&gt; method to call the service&apos;s &lt;code&gt;getAll()&lt;/code&gt; method.&lt;/p&gt;

&lt;pre class=&quot;brush: js&quot;&gt;
search(): void {
  this.searchService.getAll().subscribe(
    data =&gt; { this.searchResults = data; },
    error =&gt; console.log(error)
  );
}
&lt;/pre&gt;
&lt;p&gt;At this point, you&apos;ll likely see the following message in your browser&apos;s console.&lt;/p&gt;
&lt;pre&gt;ORIGINAL EXCEPTION: No provider for SearchService!&lt;/pre&gt;
&lt;p&gt;To fix the &quot;No provider&quot; error from above, update &lt;code&gt;app.component.ts&lt;/code&gt; to import the &lt;code&gt;SearchService&lt;/code&gt;
    and add the service to the list of providers.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { SearchService } from &apos;./shared/index&apos;;

@Component({
  ...
  styleUrls: [&apos;app.component.css&apos;],
  viewProviders: [SearchService]
})
&lt;/pre&gt;
&lt;p&gt;Now clicking the search button should work. To make the results look better, remove the
    &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag and replace it with a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;table *ngIf=&quot;searchResults&quot;&amp;gt;
  &amp;lt;thead&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;Phone&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;Address&amp;lt;/th&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;
  &amp;lt;tbody&amp;gt;
  &amp;lt;tr *ngFor=&quot;let person of searchResults; let i=index&quot;&amp;gt;
    &amp;lt;td&amp;gt;{{person.name}}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{person.phone}}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{person.address.street}}&amp;lt;br/&amp;gt;
      {{person.address.city}}, {{person.address.state}} {{person.address.zip}}
    &amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Then add some additional CSS to improve its table layout.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
table {
  margin-top: 10px;
  border-collapse: collapse;
}

th {
  text-align: left;
  border-bottom: 2px solid #ddd;
  padding: 8px;
}

td {
  border-top: 1px solid #ddd;
  padding: 8px;
}
&lt;/pre&gt;
&lt;p&gt;Now the search results look better.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img
        src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/search-results.png&quot;
        alt=&quot;Search Results&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;But wait, we still don&apos;t have search functionality! To add a search feature, add a
    &lt;code&gt;search()&lt;/code&gt; method to &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
search(q: string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&gt; {
    let results: any = [];
    data.map(item =&gt; {
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}
&lt;/pre&gt;
&lt;p&gt;Then refactor &lt;code&gt;SearchComponent&lt;/code&gt; to call this method with its &lt;code&gt;query&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
search(): void {
  this.searchService.search(this.query).subscribe(
    data =&gt; { this.searchResults = data; },
    error =&gt; console.log(error)
  );
}
&lt;/pre&gt;
&lt;p&gt;Now search results will be filtered by the query value you type in.&lt;/p&gt;
&lt;p&gt;This section showed you how to fetch and display search results. The next section builds on
    this and shows how to edit and save a record.&lt;/p&gt;

&lt;h3 id=&quot;_add_an_edit_feature&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_add_an_edit_feature&quot;&gt;&lt;/a&gt;Add an edit feature&lt;/h3&gt;

&lt;p&gt;Modify &lt;code&gt;search.component.html&lt;/code&gt; to add a click handler for editing a person.&lt;/p&gt;

&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;td&amp;gt;&amp;lt;a (click)=&quot;onSelect(person)&quot;&amp;gt;{{person.name}}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&lt;/pre&gt;

&lt;div class=&quot;alert alert-warning&quot;&gt;
    &lt;p&gt;In previous versions of Angular 2, you could embed a link with parameters directly into the HTML.
        For example:&lt;/p&gt;
    &lt;pre&gt;&amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/edit&apos;, person.id&amp;#93;&quot;&amp;gt;&lt;/pre&gt;

    &lt;p&gt;Unfortunately, this doesn&apos;t work with RC5. Another issue is adding &lt;code&gt;href=&quot;&quot;&lt;/code&gt; causes
        the page to refresh. Without &lt;code&gt;href&lt;/code&gt;, the link doesn&apos;t look like a link.
        If you know of a solution to this problem, please send me a pull request.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Then add &lt;code&gt;onSelect(person)&lt;/code&gt; to &lt;code&gt;search.component.ts&lt;/code&gt;. You&apos;ll need to import
    &lt;code&gt;Router&lt;/code&gt; and set it as a local variable to make this work.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Router } from &apos;@angular/router&apos;;
...
export class SearchComponent implements OnInit {
  ...

  constructor(private searchService: SearchService, private router: Router) { }

  ...

  onSelect(person: Person) {
    this.router.navigate([&apos;/edit&apos;, person.id]);
  }
}
&lt;/pre&gt;
&lt;p&gt;Run the following command to generate an &lt;code&gt;EditComponent&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;$ ng g component edit
installing component
  create src/app/edit/edit.component.css
  create src/app/edit/edit.component.html
  create src/app/edit/edit.component.spec.ts
  create src/app/edit/edit.component.ts
  create src/app/edit/index.ts&lt;/pre&gt;
&lt;p&gt;Add a route for this component in &lt;code&gt;app.routing.ts&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { EditComponent } from &apos;./edit/index&apos;;

const appRoutes: Routes = [
  { path: &apos;search&apos;, component: SearchComponent },
  { path: &apos;edit/:id&apos;, component: EditComponent },
  { path: &apos;&apos;, redirectTo: &apos;/search&apos;, pathMatch: &apos;full&apos; }
];
&lt;/pre&gt;
&lt;p&gt;Update &lt;code&gt;src/app/edit/edit.component.html&lt;/code&gt; to display an editable form. You might notice
    I&apos;ve added &lt;code&gt;id&lt;/code&gt; attributes to most elements. This is to
    make things easier when writing integration tests with Protractor.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;div *ngIf=&quot;person&quot;&amp;gt;
  &amp;lt;h3&amp;gt;{{editName}}&amp;lt;/h3&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Id:&amp;lt;/label&amp;gt;
    {{person.id}}
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Name:&amp;lt;/label&amp;gt;
    &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editName&quot; name=&quot;name&quot; id=&quot;name&quot; placeholder=&quot;name&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Phone:&amp;lt;/label&amp;gt;
    &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editPhone&quot; name=&quot;phone&quot; id=&quot;phone&quot; placeholder=&quot;Phone&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;fieldset&amp;gt;
    &amp;lt;legend&amp;gt;Address:&amp;lt;/legend&amp;gt;
    &amp;lt;address&amp;gt;
      &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editAddress.street&quot; id=&quot;street&quot;&amp;gt;&amp;lt;br/&amp;gt;
      &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editAddress.city&quot; id=&quot;city&quot;&amp;gt;,
      &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editAddress.state&quot; id=&quot;state&quot; size=&quot;2&quot;&amp;gt;
      &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editAddress.zip&quot; id=&quot;zip&quot; size=&quot;5&quot;&amp;gt;
    &amp;lt;/address&amp;gt;
  &amp;lt;/fieldset&amp;gt;
  &amp;lt;button (click)=&quot;save()&quot; id=&quot;save&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
  &amp;lt;button (click)=&quot;cancel()&quot; id=&quot;cancel&quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Modify &lt;code&gt;EditComponent&lt;/code&gt; to import model and service classes and to use the
    &lt;code&gt;SearchService&lt;/code&gt; to get data.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Component, OnInit, OnDestroy } from &apos;@angular/core&apos;;
import { Address, Person, SearchService } from &apos;../shared/index&apos;;
import { Subscription } from &apos;rxjs&apos;;
import { ActivatedRoute, Router } from &apos;@angular/router&apos;;

@Component({
  selector: &apos;app-edit&apos;,
  templateUrl: &apos;edit.component.html&apos;,
  styleUrls: [&apos;edit.component.css&apos;]
})
export class EditComponent implements OnInit, OnDestroy {
  person: Person;
  editName: string;
  editPhone: string;
  editAddress: Address;

  sub: Subscription;

  constructor(private route: ActivatedRoute,
              private router: Router,
              private service: SearchService) {
  }

  ngOnInit() {
    this.sub = this.route.params.subscribe(params =&gt; {
      let id = + params[&apos;id&apos;]; // (+) converts string &apos;id&apos; to a number
      this.service.get(id).subscribe(person =&gt; {
        if (person) {
          this.editName = person.name;
          this.editPhone = person.phone;
          this.editAddress = person.address;
          this.person = person;
        } else {
          this.gotoList();
        }
      });
    });
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }

  cancel() {
    this.router.navigate([&apos;/search&apos;]);
  }

  save() {
    this.person.name = this.editName;
    this.person.phone = this.editPhone;
    this.person.address = this.editAddress;
    this.service.save(this.person);
    this.gotoList();
  }

  gotoList() {
    if (this.person) {
      this.router.navigate([&apos;/search&apos;, {term: this.person.name} ]);
    } else {
      this.router.navigate([&apos;/search&apos;]);
    }
  }
}
&lt;/pre&gt;
&lt;p&gt;Modify &lt;code&gt;SearchService&lt;/code&gt; to contain functions for finding a person by their id, and saving
    them. While you&apos;re in there, modify the &lt;code&gt;search()&lt;/code&gt; method to
    be aware of updated objects in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
search(q: string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&gt; {
    let results: any = [];
    data.map(item =&gt; {
      // check for item in localStorage
      if (localStorage[&apos;person&apos; + item.id]) {
        item = JSON.parse(localStorage[&apos;person&apos; + item.id]);
      }
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}

get(id: number) {
  return this.getAll().map(all =&gt; {
    if (localStorage[&apos;person&apos; + id]) {
      return JSON.parse(localStorage[&apos;person&apos; + id]);
    }
    return all.find(e =&gt; e.id === id);
  });
}

save(person: Person) {
  localStorage[&apos;person&apos; + person.id] = JSON.stringify(person);
}
&lt;/pre&gt;
&lt;p&gt;You can add CSS to &lt;code&gt;src/app/edit/edit.component.css&lt;/code&gt; if you want to make the form look a
    bit better.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
:host {
  display: block;
  padding: 0 20px;
}

button {
  margin-top: 10px;
}
&lt;/pre&gt;
&lt;p&gt;At this point, you should be able to search for a person and update their information.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img src=&quot;https://rawgit.com/mraible/ng2-demo/master//src/assets/images/edit-form.png&quot;
         alt=&quot;Edit form&quot;
         width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;The &amp;lt;form&amp;gt; in &lt;code&gt;src/app/edit/edit.component.html&lt;/code&gt; calls a &lt;code&gt;save()&lt;/code&gt;
    function to update a person&apos;s data. You already implemented this above.
    The function calls a &lt;code&gt;gotoList()&lt;/code&gt; function that appends the person&apos;s name to the URL when
    sending the user back to the search screen.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
gotoList() {
  if (this.person) {
    this.router.navigate([&apos;/search&apos;, {term: this.person.name} ]);
  } else {
    this.router.navigate([&apos;/search&apos;]);
  }
}
&lt;/pre&gt;
&lt;p&gt;Since the &lt;code&gt;SearchComponent&lt;/code&gt; doesn&apos;t execute a search automatically when you execute this
    URL, add the following logic to do so in its constructor.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Router, ActivatedRoute } from &apos;@angular/router&apos;;
import { Subscription } from &apos;rxjs&apos;;
...
  sub: Subscription;

  constructor(private searchService: SearchService, private router: Router, private route: ActivatedRoute) {
    this.sub = this.route.params.subscribe(params =&gt; {
      if (params[&apos;term&apos;]) {
        this.query = decodeURIComponent(params[&apos;term&apos;]);
        this.search();
      }
    });
  }
&lt;/pre&gt;
&lt;p&gt;You&apos;ll want to implement &lt;code&gt;OnDestroy&lt;/code&gt; and define the &lt;code&gt;ngOnDestroy&lt;/code&gt; method to
    clean up this subscription.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Component, OnInit, OnDestroy } from &apos;@angular/core&apos;;

export class SearchComponent implements OnInit, OnDestroy {
...
  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}
&lt;/pre&gt;
&lt;p&gt;After making all these changes, you should be able to search/edit/update a person&apos;s information. If
    it works - nice job!&lt;/p&gt;

&lt;h3 id=&quot;_testing&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_testing&quot;&gt;&lt;/a&gt;Testing&lt;/h3&gt;

&lt;p&gt;Now that you&apos;ve built an application, it&apos;s important to test it to ensure it works. The best reason for
    writing tests is to automate your testing. Without tests, you&apos;ll likely be testing manually. This manual testing will
    take longer and longer as your application grows.&lt;/p&gt;
&lt;p&gt;In this section, you&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing
    controllers and &lt;a href=&quot;https://angular.github.io/protractor/&quot;&gt;Protractor&lt;/a&gt; for
    integration testing. Angular&apos;s testing documentation lists &lt;a
        href=&quot;https://angular.io/docs/ts/latest/guide/testing.html&quot;&gt;good reasons to test&lt;/a&gt;, but doesn&apos;t currently have many examples.&lt;/p&gt;

&lt;h3 id=&quot;_unit_test_the_searchservice&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_unit_test_the_searchservice&quot;&gt;&lt;/a&gt;Unit test
    the SearchService&lt;/h3&gt;
&lt;p&gt;Modify &lt;code&gt;src/app/shared/search/search.service.spec.ts&lt;/code&gt; and setup the test&apos;s infrastructure
    using &lt;a href=&quot;https://angular.io/docs/js/latest/api/http/testing/MockBackend-class.html&quot;&gt;MockBackend&lt;/a&gt;
    and &lt;a href=&quot;https://angular.io/docs/ts/latest/api/http/index/BaseRequestOptions-class.html&quot;&gt;BaseRequestOptions&lt;/a&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { MockBackend } from &apos;@angular/http/testing&apos;;
import { Http, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions } from &apos;@angular/http&apos;;
import { SearchService } from &apos;./search.service&apos;;
import { tick, fakeAsync } from &apos;@angular/core/testing/fake_async&apos;;
import { inject, TestBed } from &apos;@angular/core/testing/test_bed&apos;;

describe(&apos;SearchService&apos;, () =&gt; {
  beforeEach(() =&gt; {

    TestBed.configureTestingModule({
      providers: [
        {
          provide: Http, useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) =&gt; {
          return new Http(backend, defaultOptions);
        }, deps: [MockBackend, BaseRequestOptions]
        },
        {provide: SearchService, useClass: SearchService},
        {provide: MockBackend, useClass: MockBackend},
        {provide: BaseRequestOptions, useClass: BaseRequestOptions}
      ]
    });
  });
});
&lt;/pre&gt;
&lt;p&gt;If you run &lt;code&gt;ng test&lt;/code&gt;, you will likely see some errors about the test stubs that Angular
    CLI created for you. You can ignore these for now.&lt;/p&gt;
&lt;pre&gt;ERROR in [default] /Users/mraible/ng2-demo/src/app/edit/edit.component.spec.ts:10:20
Supplied parameters do not match any signature of call target.

ERROR in [default] /Users/mraible/ng2-demo/src/app/search/search.component.spec.ts:10:20
Supplied parameters do not match any signature of call target.&lt;/pre&gt;
&lt;p&gt;Add the first test of &lt;code&gt;getAll()&lt;/code&gt; to &lt;code&gt;search.service.spec.ts&lt;/code&gt;. This test shows
    how &lt;code&gt;MockBackend&lt;/code&gt; can be used to mock results and set the response.&lt;/p&gt;
&lt;p class=&quot;alert alert-info&quot;&gt;
    &lt;strong&gt;TIP:&lt;/strong&gt;
    When you are testing code that returns either a Promise or an RxJS Observable, you can use
    the &lt;code&gt;fakeAsync&lt;/code&gt; helper to test that code as if it were synchronous.
    Promises are be fulfilled and Observables are notified immediately after you call &lt;code&gt;tick()&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;The test below should be on the same level as &lt;code&gt;beforeEach&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should retrieve all search results&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService: SearchService, mockBackend: MockBackend) =&gt; {
    let res: Response;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.getAll().subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);
&lt;/pre&gt;
&lt;p&gt;Notice that tests continually run as you add them when using &lt;code&gt;ng test&lt;/code&gt;. You can run tests
    once by using &lt;code&gt;ng test --watch=false&lt;/code&gt;.
    You will likely see &quot;Executed 5 of 5 &lt;span style=&quot;color: red&quot;&gt;(1 FAILED)&lt;/span&gt;&quot; in your terminal.
    Add a couple more tests for filtering by search term and fetching by id.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should filter by search term&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService: SearchService, mockBackend: MockBackend) =&gt; {
    let res;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;john&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);

it(&apos;should fetch by id&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService: SearchService, mockBackend: MockBackend) =&gt; {
    let res;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;id&quot;: 1, &quot;name&quot;: &quot;John Elway&quot;}, {&quot;id&quot;: 2, &quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;2&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;Gary Kubiak&apos;);
  }))
);
&lt;/pre&gt;
&lt;h3 id=&quot;_unit_test_the_searchcomponent&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_unit_test_the_searchcomponent&quot;&gt;&lt;/a&gt;Unit
    test the SearchComponent&lt;/h3&gt;
&lt;p&gt;To unit test the &lt;code&gt;SearchComponent&lt;/code&gt;, create a &lt;code&gt;MockSearchProvider&lt;/code&gt; that has
    &lt;a  href=&quot;http://angular-tips.com/blog/2014/03/introduction-to-unit-test-spies/&quot;&gt;spies&lt;/a&gt;.
    These allow you to &lt;em&gt;spy&lt;/em&gt; on functions to check if they were called.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;src/app/shared/search/mocks/search.service.ts&lt;/code&gt; and populate it with spies for each
    method, as well as methods to set the response and subscribe to results.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { SpyObject } from &apos;./helper&apos;;
import { SearchService } from &apos;../search.service&apos;;
import Spy = jasmine.Spy;

export class MockSearchService extends SpyObject {
  getAllSpy: Spy;
  getByIdSpy: Spy;
  searchSpy: Spy;
  saveSpy: Spy;
  fakeResponse: any;

  constructor() {
    super( SearchService );

    this.fakeResponse = null;
    this.getAllSpy = this.spy(&apos;getAll&apos;).andReturn(this);
    this.getByIdSpy = this.spy(&apos;get&apos;).andReturn(this);
    this.searchSpy = this.spy(&apos;search&apos;).andReturn(this);
    this.saveSpy = this.spy(&apos;save&apos;).andReturn(this);
  }

  subscribe(callback: any) {
    callback(this.fakeResponse);
  }

  setResponse(json: any): void {
    this.fakeResponse = json;
  }
}
&lt;/pre&gt;
&lt;p&gt;In this same directory, create a &lt;code&gt;helper.ts&lt;/code&gt; class to implement the
    &lt;code&gt;SpyObject&lt;/code&gt; that &lt;code&gt;MockSearchService&lt;/code&gt; extends.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {StringMapWrapper} from &apos;@angular/core/src/facade/collection&apos;;

export interface GuinessCompatibleSpy extends jasmine.Spy {
  /** By chaining the spy with and.returnValue, all calls to the function will return a specific
   * value. */
  andReturn(val: any): void;
  /** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied
   * function. */
  andCallFake(fn: Function): GuinessCompatibleSpy;
  /** removes all recorded calls */
  reset();
}

export class SpyObject {
  static stub(object = null, config = null, overrides = null) {
    if (!(object instanceof SpyObject)) {
      overrides = config;
      config = object;
      object = new SpyObject();
    }

    let m = StringMapWrapper.merge(config, overrides);
    StringMapWrapper.forEach(m, (value, key) =&amp;gt; { object.spy(key).andReturn(value); });
    return object;
  }

  constructor(type = null) {
    if (type) {
      for (let prop in type.prototype) {
        let m = null;
        try {
          m = type.prototype&amp;#91;prop&amp;#93;;
        } catch (e) {
          // As we are creating spys for abstract classes,
          // these classes might have getters that throw when they are accessed.
          // As we are only auto creating spys for methods, this
          // should not matter.
        }
        if (typeof m === &apos;function&apos;) {
          this.spy(prop);
        }
      }
    }
  }

  spy(name) {
    if (!this&amp;#91;name&amp;#93;) {
      this&amp;#91;name&amp;#93; = this._createGuinnessCompatibleSpy(name);
    }
    return this&amp;#91;name&amp;#93;;
  }

  prop(name, value) { this&amp;#91;name&amp;#93; = value; }

  /** @internal */
  _createGuinnessCompatibleSpy(name): GuinessCompatibleSpy {
    let newSpy: GuinessCompatibleSpy = &amp;lt;any&amp;gt;jasmine.createSpy(name);
    newSpy.andCallFake = &amp;lt;any&amp;gt;newSpy.and.callFake;
    newSpy.andReturn = &amp;lt;any&amp;gt;newSpy.and.returnValue;
    newSpy.reset = &amp;lt;any&amp;gt;newSpy.calls.reset;
    // revisit return null here (previously needed for rtts_assert).
    newSpy.and.returnValue(null);
    return newSpy;
  }
}
&lt;/pre&gt;
&lt;p&gt;Alongside, create &lt;code&gt;routes.ts&lt;/code&gt; to mock Angular&apos;s &lt;code&gt;Router&lt;/code&gt; and &lt;code&gt;ActivatedRoute&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { ActivatedRoute, Params } from &apos;@angular/router&apos;;
import { Observable } from &apos;rxjs&apos;;

export class MockActivatedRoute extends ActivatedRoute {
  params: Observable&amp;lt;Params&amp;gt;

  constructor(parameters?: { [key: string]: any; }) {
    super();
    this.params = Observable.of(parameters);
  }
}

export class MockRouter {
  navigate = jasmine.createSpy(&apos;navigate&apos;);
}
&lt;/pre&gt;
&lt;p&gt;With mocks in place, you can &lt;code&gt;TestBed.configureTestingModule()&lt;/code&gt; to setup &lt;code&gt;SearchComponent&lt;/code&gt;
    to use these as providers.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { ActivatedRoute, Router } from &apos;@angular/router&apos;;
import { MockActivatedRoute, MockRouter } from &apos;../shared/search/mocks/routes&apos;;
import { MockSearchService } from &apos;../shared/search/mocks/search.service&apos;;
import { SearchComponent } from &apos;./search.component&apos;;
import { TestBed } from &apos;@angular/core/testing/test_bed&apos;;
import { FormsModule } from &apos;@angular/forms&apos;;
import { SearchService } from &apos;../shared/search/search.service&apos;;

describe(&apos;Component: Search&apos;, () =&gt; {
  let mockSearchService: MockSearchService;
  let mockActivatedRoute: MockActivatedRoute;
  let mockRouter: MockRouter;

  beforeEach(() =&gt; {
    mockSearchService = new MockSearchService();
    mockActivatedRoute = new MockActivatedRoute({&apos;term&apos;: &apos;peyton&apos;});
    mockRouter = new MockRouter();

    TestBed.configureTestingModule({
      declarations: [SearchComponent],
      providers: [
        {provide: SearchService, useValue: mockSearchService},
        {provide: ActivatedRoute, useValue: mockActivatedRoute},
        {provide: Router, useValue: mockRouter}
      ],
      imports: [FormsModule]
    });
  });
});
&lt;/pre&gt;
&lt;p&gt;Add two tests, one to verify a search term is used when it&apos;s set on the component, and a second
    to verify search is called when a term is passed in as a route parameter.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should search when a term is set and search() is called&apos;, () =&gt; {
  let fixture = TestBed.createComponent(SearchComponent);
  let searchComponent = fixture.debugElement.componentInstance;
  searchComponent.query = &apos;M&apos;;
  searchComponent.search();
  expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;M&apos;);
});

it(&apos;should search automatically when a term is on the URL&apos;, () =&gt; {
  let fixture = TestBed.createComponent(SearchComponent);
  fixture.detectChanges();
  expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;peyton&apos;);
});
&lt;/pre&gt;

&lt;p&gt;After adding these tests, you should see the first instance of all tests passing (Executed 8 of 8
    &lt;span style=&quot;color: green&quot;&gt;SUCCESS&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;Update the test for &lt;code&gt;EditComponent&lt;/code&gt;, verifying fetching a single record works. Notice
    how you can access the component directly with &lt;code&gt;fixture.debugElement.componentInstance&lt;/code&gt;, or
    its rendered version with &lt;code&gt;fixture.debugElement.nativeElement&lt;/code&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { MockSearchService } from &apos;../shared/search/mocks/search.service&apos;;
import { EditComponent } from &apos;./edit.component&apos;;
import { TestBed } from &apos;@angular/core/testing/test_bed&apos;;
import { SearchService } from &apos;../shared/search/search.service&apos;;
import { MockRouter, MockActivatedRoute } from &apos;../shared/search/mocks/routes&apos;;
import { ActivatedRoute, Router } from &apos;@angular/router&apos;;
import { FormsModule } from &apos;@angular/forms&apos;;

describe(&apos;Component: Edit&apos;, () =&gt; {
  let mockSearchService: MockSearchService;
  let mockActivatedRoute: MockActivatedRoute;
  let mockRouter: MockRouter;

  beforeEach(() =&gt; {
    mockSearchService = new MockSearchService();
    mockActivatedRoute = new MockActivatedRoute({&apos;id&apos;: 1});
    mockRouter = new MockRouter();

    TestBed.configureTestingModule({
      declarations: [EditComponent],
      providers: [
        {provide: SearchService, useValue: mockSearchService},
        {provide: ActivatedRoute, useValue: mockActivatedRoute},
        {provide: Router, useValue: mockRouter}
      ],
      imports: [FormsModule]
    });
  });

  it(&apos;should fetch a single record&apos;, () =&gt; {
    const fixture = TestBed.createComponent(EditComponent);

    let person = {name: &apos;Emmanuel Sanders&apos;, address: {city: &apos;Denver&apos;}};
    mockSearchService.setResponse(person);

    fixture.detectChanges();
    // verify service was called
    expect(mockSearchService.getByIdSpy).toHaveBeenCalledWith(1);

    // verify data was set on component when initialized
    let editComponent = fixture.debugElement.componentInstance;
    expect(editComponent.editAddress.city).toBe(&apos;Denver&apos;);

    // verify HTML renders as expected
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector(&apos;h3&apos;).innerHTML).toBe(&apos;Emmanuel Sanders&apos;);
  });
});
&lt;/pre&gt;
&lt;p&gt;You should see &quot;Executed 8 of 8 &lt;span style=&quot;color: green&quot;&gt;SUCCESS&lt;/span&gt; (0.238 secs / 0.259
    secs)&quot; in the shell window that&apos;s running &lt;code&gt;ng test&lt;/code&gt;. If you don&apos;t, try cancelling the command and
    restarting.&lt;/p&gt;
&lt;h3 id=&quot;_integration_test_the_search_ui&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_integration_test_the_search_ui&quot;&gt;&lt;/a&gt;Integration test the search UI&lt;/h3&gt;
&lt;p&gt;To test if the application works end-to-end, you can write tests with &lt;a
    href=&quot;http://angular.github.io/protractor&quot;&gt;Protractor&lt;/a&gt;. These are also known as integration
    tests, since they test the &lt;em&gt;integration&lt;/em&gt; between all layers of your application.&lt;/p&gt;
&lt;p&gt;To verify end-to-end tests work in the project before you begin, run the following commands in three
    different console windows.&lt;/p&gt;
&lt;pre&gt;ng serve
ng e2e&lt;/pre&gt;
&lt;p&gt;All tests should pass.&lt;/p&gt;
&lt;pre&gt;$ ng e2e

&amp;gt; ng2-demo@0.0.0 pree2e /Users/mraible/dev/ng2-demo
&amp;gt; webdriver-manager update

Updating selenium standalone to version 2.52.0
downloading https://selenium-release.storage.googleapis.com/2.52/selenium-server-standalone-2.52.0.jar...
Updating chromedriver to version 2.21
downloading https://chromedriver.storage.googleapis.com/2.21/chromedriver_mac32.zip...
chromedriver_2.21mac32.zip downloaded to /Users/mraible/dev/ng2-demo/node_modules/protractor/selenium/chromedriver_2.21mac32.zip
selenium-server-standalone-2.52.0.jar downloaded to /Users/mraible/dev/ng2-demo/node_modules/protractor/selenium/selenium-server-standalone-2.52.0.jar

&amp;gt; ng2-demo@0.0.0 e2e /Users/mraible/dev/ng2-demo
&amp;gt; protractor &quot;config/protractor.conf.js&quot;

[00:01:07] I/direct - Using ChromeDriver directly...
[00:01:07] I/launcher - Running 1 instances of WebDriver
Spec started

  ng2-demo App
    &amp;#10004; should display message saying app works

Executed 1 of 1 spec SUCCESS in 0.684 sec.
[00:01:09] I/launcher - 0 instance(s) of WebDriver still running
[00:01:09] I/launcher - chrome #01 passed

All end-to-end tests pass.&lt;/pre&gt;
&lt;h3 id=&quot;_testing_the_search_feature&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_testing_the_search_feature&quot;&gt;&lt;/a&gt;Testing the
    search feature&lt;/h3&gt;
&lt;p&gt;Create end-to-end tests in &lt;code&gt;e2e/search.e2e-spec.ts&lt;/code&gt; to verify the search feature works.
    Populate it with the following code:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Search&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/search&apos;);
    element(by.linkText(&apos;Search&apos;)).click();
  });

  it(&apos;should have an input and search button&apos;, () =&gt; {
    expect(element(by.css(&apos;app-root app-search form input&apos;)).isPresent()).toEqual(true);
    expect(element(by.css(&apos;app-root app-search form button&apos;)).isPresent()).toEqual(true);
  });

  it(&apos;should allow searching&apos;, () =&gt; {
    let searchButton = element(by.css(&apos;button&apos;));
    let searchBox = element(by.css(&apos;input&apos;));
    searchBox.sendKeys(&apos;M&apos;);
    searchButton.click().then(() =&gt; {
      var list = element.all(by.css(&apos;app-search table tbody tr&apos;));
      expect(list.count()).toBe(3);
    });
  });
});
&lt;/pre&gt;
&lt;h3 id=&quot;_testing_the_edit_feature&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_testing_the_edit_feature&quot;&gt;&lt;/a&gt;Testing the edit feature&lt;/h3&gt;
&lt;p&gt;Create a &lt;code&gt;e2e/edit.e2e-spec.ts&lt;/code&gt; test to verify the &lt;code&gt;EditComponent&lt;/code&gt; renders a
    person&apos;s information and that their information can be updated.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Edit&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/edit/1&apos;);
  });

  let name = element(by.id(&apos;name&apos;));
  let street = element(by.id(&apos;street&apos;));
  let city = element(by.id(&apos;city&apos;));

  it(&apos;should allow viewing a person&apos;, () =&gt; {
    expect(element(by.css(&apos;h3&apos;)).getText()).toEqual(&apos;Peyton Manning&apos;);
    expect(name.getAttribute(&apos;value&apos;)).toEqual(&apos;Peyton Manning&apos;);
    expect(street.getAttribute(&apos;value&apos;)).toEqual(&apos;1234 Main Street&apos;);
    expect(city.getAttribute(&apos;value&apos;)).toEqual(&apos;Greenwood Village&apos;);
  });

  it(&apos;should allow updating a name&apos;, function () {
    let save = element(by.id(&apos;save&apos;));
    // send individual characters since sendKeys passes partial values sometimes
    // https://github.com/angular/protractor/issues/698
    &apos; Won!&apos;.split(&apos;&apos;).forEach((c) =&gt; name.sendKeys(c));
    save.click();
    // verify one element matched this change
    var list = element.all(by.css(&apos;app-search table tbody tr&apos;));
    expect(list.count()).toBe(1);
  });
});
&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;ng e2e&lt;/code&gt; to verify all your end-to-end tests pass. You should see a success message
    similar to the one below in your terminal window.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img  src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/protractor-success.png&quot;
          alt=&quot;Protractor success&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;If you made it this far and have all your specs passing - congratulations! You&apos;re well on your way to
    writing quality code with Angular 2 and verifying it works.&lt;/p&gt;
&lt;p&gt;You can see the test coverage of your project by opening &lt;code&gt;coverage/index.html&lt;/code&gt; in your
    browser. You might notice that the new components and service could use some additional coverage. If you feel
    the need to improve this coverage, please send me a pull request!&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img
        src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/test-coverage.png&quot;
        alt=&quot;Test coverage&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;h3 id=&quot;_continuous_integration&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_continuous_integration&quot;&gt;&lt;/a&gt;Continuous Integration&lt;/h3&gt;
&lt;p&gt;At the time of this writing, Angular CLI did not have any continuous integration support. However, it&apos;s
    easy to add with &lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis CI&lt;/a&gt;. If you&apos;ve checked in your project to GitHub,
    you can easily use Travis CI.
    Simply login and enable builds for the GitHub repo you created the project in. Then add the following
    &lt;code&gt;.travis.yml&lt;/code&gt;
    in your root directory and &lt;code&gt;git push&lt;/code&gt;. This will trigger the first build.&lt;/p&gt;
&lt;pre&gt;
language: node_js
sudo: true

cache:
  directories:
    - node
    - node_modules

dist: trusty

node_js:
  - &apos;5.6.0&apos;

branches:
  only:
  - master

before_install:
 - npm install -g angular-cli
 - export CHROME_BIN=/usr/bin/google-chrome
 - export DISPLAY=:99.0
 - sh -e /etc/init.d/xvfb start
 - sudo apt-get update
 - sudo apt-get install -y libappindicator1 fonts-liberation
 - wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
 - sudo dpkg -i google-chrome*.deb

script:
 - ng test --watch false # http://mseemann.de/frontend/2016/05/31/setup-angular-2-app-part-1.html
 - ng serve &amp;
 - ng e2e

notifications:
  webhooks:
    on_success: change  # options: [always|never|change] default: always
    on_failure: always  # options: [always|never|change] default: always
    on_start: false     # default: false
&lt;/pre&gt;
&lt;div class=&quot;paragraph&quot;&gt;
    &lt;p&gt;&lt;a href=&quot;https://travis-ci.org/mraible/ng2-demo/builds/154182594&quot;&gt;Here&lt;/a&gt; is a build showing all unit
        and integration tests passing.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;_source_code&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_source_code&quot;&gt;&lt;/a&gt;Source code&lt;/h3&gt;
&lt;p&gt;A completed project with this code in it is available on GitHub at &lt;a
    href=&quot;https://github.com/mraible/ng2-demo&quot;&gt;https://github.com/mraible/ng2-demo&lt;/a&gt;.
    If you have ideas for improvements, please leave a comment or send a pull request.
&lt;/p&gt;
&lt;p&gt;
    This tutorial was originally written using &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt;.
    This means you can &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fng2-demo%2F%2FREADME.adoc&quot;&gt;read it using DocGist&lt;/a&gt; if you like.
&lt;/p&gt;
&lt;h3 id=&quot;_summary&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_summary&quot;&gt;&lt;/a&gt;Summary&lt;/h3&gt;
&lt;p&gt;I hope you&apos;ve enjoyed this in-depth tutorial on how to get started with Angular 2 and Angular CLI.
    Angular CLI takes much of the pain out of setting up an Angular 2 project and using Typescript. I expect great
    things from Angular CLI, mostly because the Angular 2 setup process can be tedious and CLI greatly simplifies things. &lt;/p&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/upcoming_events_devoxx4kids_denver_testing</id>
        <title type="html">Upcoming Events: Devoxx4Kids Denver, Testing Angular 2, DevoxxUS CFP and VJUG24</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/upcoming_events_devoxx4kids_denver_testing"/>
        <published>2016-08-12T15:29:01-06:00</published>
        <updated>2016-08-12T21:31:20-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="angular" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx4kids" scheme="http://roller.apache.org/ns/tags/" />
        <category term="vjug24" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxxus" scheme="http://roller.apache.org/ns/tags/" />
        <category term="denver" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;It&apos;s been awhile since I&apos;ve posted anything on this here blog. That usually means one thing - I&apos;ve been off having fun! That couldn&apos;t be more true this summer. The day after my &lt;a href=&quot;//raibledesigns.com/rd/entry/a_delightful_trip_to_devoxx&quot;&gt;last post&lt;/a&gt;, I began traveling and haven&apos;t stopped since. In fact, this weekend will be the first weekend I&apos;ve been home &lt;em&gt;since&lt;/em&gt; writing that post. Hawaii, Montana, Denver, Montana, Colorado Springs and Utah - it&apos;s been a fabulous summer. I&apos;ll write more about those adventures soon.&lt;/p&gt;
&lt;p&gt;In the meantime, I wanted to mention some upcoming events you might be interested in:&lt;/p&gt;
&lt;ul&gt;
     &lt;li style=&quot;margin-bottom: 5px&quot;&gt;&lt;strong&gt;September 10:&lt;/strong&gt; Devoxx4Kids Denver has an upcoming workshop on &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/events/232720431/&quot;&gt;Exploring JavaScript&lt;/a&gt; with the world famous &lt;a href=&quot;https://twitter.com/venkat_s&quot;&gt;Dr. Venkat Subramaniam&lt;/a&gt;. If you know Venkat, you know this is a session you shouldn&apos;t miss. Your kids will love it, you&apos;ll get a lot of good laughs and everyone is sure to have a good time. Make sure and &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/events/232720431/&quot;&gt;RSVP&lt;/a&gt; soon so you get in before this baby fills up!&lt;/li&gt;
     &lt;li style=&quot;margin-bottom: 5px&quot;&gt;&lt;strong&gt;August 22:&lt;/strong&gt; HTML5 Denver has a sessions on &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/events/228220656/&quot;&gt;ES6 vs. Typescript and Testing Angular 2 Applications&lt;/a&gt;. The first session will be delivered by my good friend Geoffrey Filippi and I&apos;ll be performing the second act with the help of &lt;a href=&quot;https://github.com/angular/angular-cli&quot;&gt;angular-cli&lt;/a&gt;.&lt;/li&gt;
&lt;li style=&quot;margin-bottom: 5px&quot;&gt;
&lt;a class=&quot;picture&quot; href=&quot;https://c3.staticflickr.com/9/8576/28835656442_50394481d7_c.jpg&quot; rel=&quot;lightbox[devoxxus]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/28835656442/in/datetaken-public/&quot; title=&quot;Devoxx US&quot;&gt;&lt;img src=&quot;https://c3.staticflickr.com/9/8576/28835656442_50394481d7.jpg&quot; width=&quot;250&quot; alt=&quot;Devoxx US&quot; style=&quot;border: 1px solid black; margin: 10px; margin-top: 5px&quot;&gt;&lt;/a&gt;
&lt;strong&gt;September 1:&lt;/strong&gt; One of my favorite conferences, &lt;a href=&quot;https://devoxx.be/&quot;&gt;Devoxx&lt;/a&gt;, is coming to the US! &lt;a href=&quot;http://devoxx.us/&quot;&gt;DevoxxUS&lt;/a&gt; recently &lt;a href=&quot;http://us13.campaign-archive2.com/?u=ed850ea0ed618350b3fc5901f&amp;id=18fd4297cc&quot;&gt;announced&lt;/a&gt; that registration is open. Even more interesting is that the &lt;abbr title=&quot;Call For Papers&quot;&gt;CFP&lt;/abbr&gt; begins September 1st. I&apos;m biased because I&apos;m on the program committee, but I&apos;d love to see your ideas for great talks!&lt;/li&gt;
&lt;li style=&quot;margin-bottom: 5px&quot;&gt;&lt;strong&gt;September 27:&lt;/strong&gt; Our good friends from &lt;a href=&quot;http://virtualjug.com/&quot;&gt;vJUG&lt;/a&gt; are hosting the first &lt;a href=&quot;http://virtualjug.com/vjug24/&quot;&gt;24 hour Virtual Java Conference&lt;/a&gt; in the world! I&apos;ll be speaking about the &lt;a href=&quot;https://virtualjug.com/vjug24-session-the-art-of-angular-in-2016-by-matt-raible/&quot;&gt;Art of Angular in 2016&lt;/a&gt; at 10pm EDT.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;September:&lt;/strong&gt; I&apos;m looking for new clients. My current contracts end on August 31 and I&apos;m searching for the next cool team to work with. My expertise: Java, JavaScript and I&apos;m &lt;em&gt;really&lt;/em&gt; good at CSS. This is a hard combination to find! &lt;a href=&quot;http://raibledesigns.com/rd/page/contact&quot;&gt;LMK if you have a need&lt;/a&gt;.
&lt;/ul&gt;
&lt;p&gt;I hope to see you at one of these events! </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/testing_angular_2_0_rc1</id>
        <title type="html">Testing Angular 2.0 RC1 Applications</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/testing_angular_2_0_rc1"/>
        <published>2016-06-06T09:57:13-06:00</published>
        <updated>2016-06-21T05:06:24-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="protractor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="karma" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jasmine" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;As mentioned &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;on Friday&lt;/a&gt;, there&apos;s been
    quite a bit that&apos;s changed with Angular 2 between its
    Beta 9 and RC 1 releases. This article is an update to the
    &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;Testing Angular 2 Applications&lt;/a&gt; I wrote in
    March.
    That tutorial was based on Angular 2.0 Beta 9. Rather than simply updating that
    tutorial and blog post for 2.0 RC1, I decided to create a new version for posterity&apos;s sake. The 2.0 Beta 9 version
    will
    remain on my blog and I&apos;ve &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/releases/tag/2.0.0-beta.9&quot;&gt;tagged
        the source on GitHub&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;If you&apos;ve already read the first version of &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;Testing
    Angular 2 Applications&lt;/a&gt;,
    checkout the &lt;a
        href=&quot;https://github.com/mraible/angular2-tutorial/pull/2/files#diff-c5cd85f8ff52aad4b08a3dd38575dddf&quot;&gt;
        diff of the Asciidoctor version&lt;/a&gt; to see what&apos;s changed.&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing controllers and &lt;a
                href=&quot;http://www.protractortest.org/&quot;&gt;Protractor&lt;/a&gt; for
                integration testing. See Angular 2&apos;s &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/testing.html&quot;&gt;guide
                    to unit testing&lt;/a&gt;
                if you&apos;d like more information on testing and why it&apos;s important.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The best reason for writing tests is to automate your testing. Without tests, you&apos;ll likely be testing
                manually.
                This manual testing will take longer and longer as your codebase grows.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&gt;
            &lt;ul&gt;
                &lt;li&gt;About 15-30 minutes.&lt;/li&gt;
                &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ
                    IDEA&lt;/a&gt;.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; installed.&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and npm installed. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;get_the_tutorial_project&quot;&gt;Get the tutorial project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;angular2-tutorial repository&lt;/a&gt;, checkout the &lt;code&gt;testing-start&lt;/code&gt; branch, and install its dependencies.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mraible/angular2-tutorial.git
cd angular2-tutorial
git checkout testing-start
npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you haven&apos;t completed the &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;Getting
                Started with Angular 2.0 RC1&lt;/a&gt; tutorial,
                you should peruse it so you understand how this application works.
                You can also simply start the app with &lt;code&gt;npm start&lt;/code&gt; and view it in your browser at &lt;a
                    href=&quot;http://localhost:5555/&quot; class=&quot;bare&quot;&gt;http://localhost:5555/&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</summary>
        <content type="html">&lt;p&gt;As mentioned &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;on Friday&lt;/a&gt;, there&apos;s been
    quite a bit that&apos;s changed with Angular 2 between its
    Beta 9 and RC 1 releases. This article is an update to the
    &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;Testing Angular 2 Applications&lt;/a&gt; I wrote in
    March.
    That tutorial was based on Angular 2.0 Beta 9. Rather than simply updating that
    tutorial and blog post for 2.0 RC1, I decided to create a new version for posterity&apos;s sake. The 2.0 Beta 9 version
    will
    remain on my blog and I&apos;ve &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/releases/tag/2.0.0-beta.9&quot;&gt;tagged
        the source on GitHub&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;If you&apos;ve already read the first version of &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;Testing
    Angular 2 Applications&lt;/a&gt;,
    checkout the &lt;a
        href=&quot;https://github.com/mraible/angular2-tutorial/pull/2/files#diff-c5cd85f8ff52aad4b08a3dd38575dddf&quot;&gt;
        diff of the Asciidoctor version&lt;/a&gt; to see what&apos;s changed.&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing controllers and &lt;a
                href=&quot;http://www.protractortest.org/&quot;&gt;Protractor&lt;/a&gt; for
                integration testing. See Angular 2&apos;s &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/testing.html&quot;&gt;guide
                    to unit testing&lt;/a&gt;
                if you&apos;d like more information on testing and why it&apos;s important.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The best reason for writing tests is to automate your testing. Without tests, you&apos;ll likely be testing
                manually.
                This manual testing will take longer and longer as your codebase grows.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&gt;
            &lt;ul&gt;
                &lt;li&gt;About 15-30 minutes.&lt;/li&gt;
                &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ
                    IDEA&lt;/a&gt;.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; installed.&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and npm installed. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;get_the_tutorial_project&quot;&gt;Get the tutorial project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;angular2-tutorial repository&lt;/a&gt;, checkout the &lt;code&gt;testing-start&lt;/code&gt; branch, and install its dependencies.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mraible/angular2-tutorial.git
cd angular2-tutorial
git checkout testing-start
npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you haven&apos;t completed the &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;Getting
                Started with Angular 2.0 RC1&lt;/a&gt; tutorial,
                you should peruse it so you understand how this application works.
                You can also simply start the app with &lt;code&gt;npm start&lt;/code&gt; and view it in your browser at &lt;a
                    href=&quot;http://localhost:5555/&quot; class=&quot;bare&quot;&gt;http://localhost:5555/&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;unit_test_the_searchservice&quot;&gt;Unit test the SearchService&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Create &lt;code&gt;src/client/app/shared/search/search.service.spec.ts&lt;/code&gt; and setup the test&apos;s
                infrastructure using &lt;a
                    href=&quot;https://angular.io/docs/js/latest/api/http/testing/MockBackend-class.html&quot;&gt;MockBackend&lt;/a&gt;
                and &lt;a href=&quot;https://angular.io/docs/js/latest/api/http/BaseRequestOptions-class.html&quot;&gt;BaseRequestOptions&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {
  beforeEachProviders,
  it,
  describe,
  expect,
  inject,
  fakeAsync,
  tick
} from &apos;@angular/core/testing&apos;;
import { MockBackend } from &apos;@angular/http/testing&apos;;
import { provide } from &apos;@angular/core&apos;;
import { Http, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions } from &apos;@angular/http&apos;;
import { SearchService } from &apos;./search.service&apos;;

export function main() {
  describe(&apos;Search Service&apos;, () =&gt; {
    beforeEachProviders(() =&gt; {
      return [BaseRequestOptions, MockBackend, SearchService,
        provide(Http, {
          useFactory: (backend:ConnectionBackend, defaultOptions:BaseRequestOptions) =&gt; {
            return new Http(backend, defaultOptions);
          }, deps: [MockBackend, BaseRequestOptions]
        }),
      ];
    });
  });
}

&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you run &lt;code&gt;npm test&lt;/code&gt;, all tests will pass, but you don&apos;t see &quot;Search Service&quot; as a listed
                test. You can fix this by adding the first test of &lt;code&gt;getAll()&lt;/code&gt;.
                This test shows how &lt;code&gt;MockBackend&lt;/code&gt; can be used to mock results and set the response.&lt;/p&gt;
        &lt;/div&gt;
        &lt;p class=&quot;alert alert-success&quot;&gt;&lt;b&gt;TIP&lt;/b&gt;: When you are testing code that returns either a Promise or an RxJS
            Observable, you can use the &lt;code&gt;fakeAsync&lt;/code&gt; helper to test that code as
            if it were synchronous.
            Promises are be fulfilled and Observables are notified immediately after you call &lt;code&gt;tick()&lt;/code&gt;.&lt;/p&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The test below should be on the same level as &lt;code&gt;beforeEachProviders&lt;/code&gt;.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;it(&apos;should retrieve all search results&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&amp;gt; {
    var res:Response;
    mockBackend.connections.subscribe(c =&amp;gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.getAll().subscribe((response) =&amp;gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Running &lt;code&gt;npm test&lt;/code&gt; should result in &quot;12 tests completed&quot;. Add a couple more tests for
                filtering by search term and fetching by id.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should filter by search term&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&gt; {
    var res;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;john&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);

it(&apos;should fetch by id&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&gt; {
    var res;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;id&quot;: 1, &quot;name&quot;: &quot;John Elway&quot;}, {&quot;id&quot;: 2, &quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;2&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;Gary Kubiak&apos;);
  }))
);
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you want to have tests continually run as you add them, you can run the following commands in separate
                shell windows.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;npm run build.test.watch
npm run karma.start&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;p&gt;NOTE: See &lt;a
            href=&quot;https://www.jetbrains.com/help/idea/15.0/running-unit-tests-on-karma.html?origin=old_help&quot;&gt;Running
            Unit Tests on Karma&lt;/a&gt; to
            learn how to run your tests from IntelliJ IDEA.&lt;/p&gt;

    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;unit_test_the_searchcomponent&quot;&gt;Unit test the SearchComponent&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To unit test the &lt;code&gt;SearchComponent&lt;/code&gt;, create a &lt;code&gt;MockSearchProvider&lt;/code&gt; that has &lt;a
                href=&quot;http://angular-tips.com/blog/2014/03/introduction-to-unit-test-spies/&quot;&gt;spies&lt;/a&gt;.
                These allow you to &lt;em&gt;spy&lt;/em&gt; on functions to check if they were called.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Create &lt;code&gt;src/client/app/shared/search/mocks/search.service.ts&lt;/code&gt; and populate it with spies for
                each method, as well as methods to set the response and subscribe to results.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { provide } from &apos;@angular/core&apos;;
import { SpyObject } from &apos;./helper&apos;;

import { SearchService } from &apos;../search.service&apos;;
import Spy = jasmine.Spy;

export class MockSearchService extends SpyObject {
  getAllSpy:Spy;
  getByIdSpy:Spy;
  searchSpy:Spy;
  saveSpy:Spy;
  fakeResponse:any;

  constructor() {
    super(SearchService);

    this.fakeResponse = null;
    this.getAllSpy = this.spy(&apos;getAll&apos;).andReturn(this);
    this.getByIdSpy = this.spy(&apos;get&apos;).andReturn(this);
    this.searchSpy = this.spy(&apos;search&apos;).andReturn(this);
    this.saveSpy = this.spy(&apos;save&apos;).andReturn(this);
  }

  subscribe(callback:any) {
    callback(this.fakeResponse);
  }

  setResponse(json:any):void {
    this.fakeResponse = json;
  }

  getProviders():Array&amp;lt;any&amp;gt; {
    return [provide(SearchService, {useValue: this})];
  }
}
&lt;/pre&gt;
                &lt;p&gt;
                    In this same directory, create a &lt;code&gt;helper.ts&lt;/code&gt; class to implement the
                    &lt;code&gt;SpyObject&lt;/code&gt;
                    that &lt;code&gt;MockSearchService&lt;/code&gt; extends.
                &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {StringMapWrapper} from &apos;@angular/core/src/facade/collection&apos;;

export interface GuinessCompatibleSpy extends jasmine.Spy {
  /** By chaining the spy with and.returnValue, all calls to the function will return a specific
   * value. */
  andReturn(val: any): void;
  /** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied
   * function. */
  andCallFake(fn: Function): GuinessCompatibleSpy;
  /** removes all recorded calls */
  reset();
}

export class SpyObject {
  static stub(object = null, config = null, overrides = null) {
    if (!(object instanceof SpyObject)) {
      overrides = config;
      config = object;
      object = new SpyObject();
    }

    var m = StringMapWrapper.merge(config, overrides);
    StringMapWrapper.forEach(m, (value, key) =&amp;gt; { object.spy(key).andReturn(value); });
    return object;
  }

  constructor(type = null) {
    if (type) {
      for (var prop in type.prototype) {
        var m = null;
        try {
          m = type.prototype&amp;#91;prop&amp;#93;;
        } catch (e) {
          // As we are creating spys for abstract classes,
          // these classes might have getters that throw when they are accessed.
          // As we are only auto creating spys for methods, this
          // should not matter.
        }
        if (typeof m === &apos;function&apos;) {
          this.spy(prop);
        }
      }
    }
  }

  spy(name) {
    if (!this&amp;#91;name&amp;#93;) {
      this&amp;#91;name&amp;#93; = this._createGuinnessCompatibleSpy(name);
    }
    return this&amp;#91;name&amp;#93;;
  }

  prop(name, value) { this&amp;#91;name&amp;#93; = value; }

  /** @internal */
  _createGuinnessCompatibleSpy(name): GuinessCompatibleSpy {
    var newSpy: GuinessCompatibleSpy = &amp;lt;any&amp;gt;jasmine.createSpy(name);
    newSpy.andCallFake = &amp;lt;any&amp;gt;newSpy.and.callFake;
    newSpy.andReturn = &amp;lt;any&amp;gt;newSpy.and.returnValue;
    newSpy.reset = &amp;lt;any&amp;gt;newSpy.calls.reset;
    // revisit return null here (previously needed for rtts_assert).
    newSpy.and.returnValue(null);
    return newSpy;
  }
}
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Alongside, create &lt;code&gt;routes.ts&lt;/code&gt; to mock Angular&apos;s &lt;code&gt;RouteSegment&lt;/code&gt; and passing
                parameters between components.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { RouteSegment } from &apos;@angular/router&apos;;

export class MockRouteSegment implements RouteSegment {
  urlSegments:any;
  parameters:any;
  outlet:string;
  _type:any;
  _componentFactory:any;
  type:any;
  stringifiedUrlSegments:string;

  constructor(parameters?:{ [key:string]:any; }) {
    this.parameters = parameters;
  }

  getParam(param:string) {
    return this.parameters[param];
  }
}
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;With mocks in place, you can create a spec for &lt;code&gt;SearchComponent&lt;/code&gt; that uses these as providers.
                Create a file at &lt;code&gt;src/search/components/search.component.spec.ts&lt;/code&gt; and populate it with the
                following code.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { provide } from &apos;@angular/core&apos;;
import { TestComponentBuilder } from &apos;@angular/compiler/testing&apos;;
import {
  it,
  describe,
  expect,
  inject,
  beforeEachProviders,
} from &apos;@angular/core/testing&apos;;

import { RouteSegment } from &apos;@angular/router&apos;;
import { MockRouteSegment } from &apos;../shared/search/mocks/routes&apos;;
import { MockSearchService } from &apos;../shared/search/mocks/search.service&apos;;

import { SearchComponent } from &apos;./search.component&apos;;

export function main() {
  describe(&apos;Search component&apos;, () =&gt; {
    var mockSearchService:MockSearchService;

    beforeEachProviders(() =&gt; {
      mockSearchService = new MockSearchService();

      return [
        mockSearchService.getProviders(),
        provide(RouteSegment, { useValue: new MockRouteSegment({ &apos;term&apos;: &apos;peyton&apos; }) })
      ];
    });
  });
}
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Add two tests, one to verify a search term is used when it&apos;s set on the component and a second to verify
                search is called when a term is passed in as a route
                parameter.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should search when a term is set and search() is called&apos;, inject([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
  return tcb.createAsync(SearchComponent).then((fixture) =&gt; {
    let searchComponent = fixture.debugElement.componentInstance;
    searchComponent.query = &apos;M&apos;;
    searchComponent.search();
    expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;M&apos;);
  });
}));

it(&apos;should search automatically when a term is on the URL&apos;, inject([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
  return tcb.createAsync(SearchComponent).then((fixture) =&gt; {
    fixture.detectChanges();
    expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;peyton&apos;);
  });
}));
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Add a spec for the &lt;code&gt;EditComponent&lt;/code&gt; as well, verifying fetching a single record works. Notice
                how you can access the component directly with
                &lt;code&gt;fixture.debugElement.componentInstance&lt;/code&gt;, or its rendered version with &lt;code&gt;fixture.debugElement.nativeElement&lt;/code&gt;.
                Create a file at &lt;code&gt;src/search/components/edit.component.spec.ts&lt;/code&gt; and populate it with the code
                below.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { provide } from &apos;@angular/core&apos;;
import { TestComponentBuilder } from &apos;@angular/compiler/testing&apos;;
import {
  it,
  describe,
  expect,
  inject,
  beforeEachProviders,
} from &apos;@angular/core/testing&apos;;

import { RouteSegment } from &apos;@angular/router&apos;;
import { ROUTER_FAKE_PROVIDERS } from &apos;@angular/router/testing&apos;;
import { MockRouteSegment } from &apos;../shared/search/mocks/routes&apos;;
import { MockSearchService } from &apos;../shared/search/mocks/search.service&apos;;

import { EditComponent } from &apos;./edit.component&apos;;

export function main() {
  describe(&apos;Edit component&apos;, () =&gt; {
    var mockSearchService:MockSearchService;

    beforeEachProviders(() =&gt; {
      mockSearchService = new MockSearchService();

      return [
        mockSearchService.getProviders(),
        ROUTER_FAKE_PROVIDERS,
        provide(RouteSegment, { useValue: new MockRouteSegment({ &apos;id&apos;: &apos;1&apos; }) })
      ];
    });

    it(&apos;should fetch a single record&apos;, inject([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
      return tcb.createAsync(EditComponent).then((fixture) =&gt; {
        let person = {name: &apos;Emmanuel Sanders&apos;, address: {city: &apos;Denver&apos;}};
        mockSearchService.setResponse(person);

        fixture.detectChanges();
        // verify service was called
        expect(mockSearchService.getByIdSpy).toHaveBeenCalledWith(1);

        // verify data was set on component when initialized
        let editComponent = fixture.debugElement.componentInstance;
        expect(editComponent.editAddress.city).toBe(&apos;Denver&apos;);

        // verify HTML renders as expected
        var compiled = fixture.debugElement.nativeElement;
        expect(compiled.querySelector(&apos;h3&apos;)).toHaveText(&apos;Emmanuel Sanders&apos;);
      });
    }));
  });
}
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should see &quot;&lt;span style=&quot;color: green&quot;&gt;&amp;#10004; 22 tests completed&lt;/span&gt;&quot; in the shell window that&apos;s
                running &lt;code&gt;npm run karma.start&lt;/code&gt;. If you don&apos;t, try cancelling the command and restarting.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;integration_test_the_search_ui&quot;&gt;Integration test the search UI&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To test if the application works end-to-end, you can write tests with &lt;a
                href=&quot;http://angular.github.io/protractor&quot;&gt;Protractor&lt;/a&gt;. These are also known as integration tests,
                since they test the &lt;em&gt;integration&lt;/em&gt; between all layers of your application.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To verify end-to-end tests work in the project before you begin, run the following commands in three
                different console windows.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;# npm run webdriver-update &amp;lt;- You will need to run this the first time
npm run webdriver-start
npm run serve.e2e
npm run e2e&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should receive an error stating that the &quot;nav text for About&quot; is incorrect.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;protractor-nav-error&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;

                &lt;a href=&quot;https://c6.staticflickr.com/8/7393/27377971101_9a4bdfee36_c.jpg&quot;
                   title=&quot;Protractor nav test error&quot; rel=&quot;lightbox[testing-angular2.0rc1]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/27377971101/in/datetaken-public/&quot;&gt;&lt;img
                    src=&quot;https://c6.staticflickr.com/8/7393/27377971101_9a4bdfee36_z.jpg&quot; width=&quot;640&quot;
                    alt=&quot;Protractor nav test error&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;This happens because we added a Search link to the navbar and didn&apos;t update the test (in &lt;code&gt;app.component.e2e.ts&lt;/code&gt;)
                that looks for the last child. &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;it(&apos;should have correct nav text for About&apos;, () =&amp;gt; {
    expect(element(by.css(&apos;sd-app sd-navbar nav a:last-child&apos;)).getText()).toEqual(&apos;ABOUT&apos;);
});
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Replace this test with the one below, and add a new one to verify the Search link is last.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;it(&apos;should have correct nav text for About&apos;, () =&amp;gt; {
  expect(element(by.css(&apos;sd-app sd-navbar nav a:nth-child(2)&apos;)).getText()).toEqual(&apos;ABOUT&apos;);
});

it(&apos;should have correct nav text for Search&apos;, () =&amp;gt; {
  expect(element(by.css(&apos;sd-app sd-navbar nav a:last-child&apos;)).getText()).toEqual(&apos;SEARCH&apos;);
});
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Now when you run &lt;code&gt;npm run e2e&lt;/code&gt;, all specs should pass.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;_testing_the_search_feature&quot;&gt;Testing the search feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a new &lt;code&gt;search.component.e2e-spec.ts&lt;/code&gt; spec in the same directory as your &lt;code&gt;SearchComponent&lt;/code&gt;.
                    Add tests to verify elements are rendered correctly and
                    search works. At the time of this writing, Protractor&apos;s &lt;code&gt;by.model&lt;/code&gt; and
                    &lt;code&gt;by.repeater&lt;/code&gt; don&apos;t work with Angular 2. For this reason, I used &lt;code&gt;by.css&lt;/code&gt; to
                    verify the HTML renders as expected.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Search&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/search&apos;);
  });

  it(&apos;should have an input and search button&apos;, () =&gt; {
    expect(element(by.css(&apos;sd-app sd-search form input&apos;)).isPresent()).toEqual(true);
    expect(element(by.css(&apos;sd-app sd-search form button&apos;)).isPresent()).toEqual(true);
  });

  it(&apos;should allow searching&apos;, () =&gt; {
    let searchButton = element(by.css(&apos;button&apos;));
    let searchBox = element(by.css(&apos;input&apos;));
    searchBox.sendKeys(&apos;M&apos;);
    searchButton.click().then(() =&gt; {
      // doesn&apos;t work as expected - results in 0
      //expect(element.all(by.repeater(&apos;person of searchResults&apos;)).count()).toEqual(3);
      var list = element.all(by.css(&apos;sd-search table tbody tr&apos;));
      expect(list.count()).toBe(3);
    });
  });
});
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;_testing_the_edit_feature&quot;&gt;Testing the edit feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a &lt;code&gt;edit.component.e2e-spec.ts&lt;/code&gt; spec to verify the &lt;code&gt;EditComponent&lt;/code&gt;
                    renders a person&apos;s information and that you can update their information.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Edit&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/edit/1&apos;);
  });

  let name = element(by.id(&apos;name&apos;));
  let street = element(by.id(&apos;street&apos;));
  let city = element(by.id(&apos;city&apos;));

  it(&apos;should allow viewing a person&apos;, () =&gt; {
    expect(element(by.css(&apos;h3&apos;)).getText()).toEqual(&apos;Peyton Manning&apos;);
    expect(name.getAttribute(&apos;value&apos;)).toEqual(&apos;Peyton Manning&apos;);
    expect(street.getAttribute(&apos;value&apos;)).toEqual(&apos;1234 Main Street&apos;);
    expect(city.getAttribute(&apos;value&apos;)).toEqual(&apos;Greenwood Village&apos;);
  });

  it(&apos;should allow updating a name&apos;, function () {
    let save = element(by.id(&apos;save&apos;));
    // send individual characters since sendKeys passes partial values sometimes
    // https://github.com/angular/protractor/issues/698
    &apos; Won!&apos;.split(&apos;&apos;).forEach((c) =&gt; name.sendKeys(c));
    save.click();
    // verify one element matched this change
    var list = element.all(by.css(&apos;sd-search table tbody tr&apos;));
    expect(list.count()).toBe(1);
  });
});
&lt;/pre&gt;
                    &lt;p&gt;
                        Run &lt;code&gt;npm run e2e&lt;/code&gt; to verify all your end-to-end tests pass. You might receive a
                        failure for the &quot;Home&quot; test.
                    &lt;/p&gt;
                    &lt;div style=&quot;text-align: center&quot;&gt;
                        &lt;a href=&quot;https://c1.staticflickr.com/8/7227/27173055360_80b1055f07_c.jpg&quot;
                           data-href=&quot;https://www.flickr.com/photos/mraible/27173055360/in/datetaken-public/&quot;
                           title=&quot;Protractor home error&quot; rel=&quot;lightbox[testing-angular2.0rc1]&quot;&gt;&lt;img
                            src=&quot;https://c1.staticflickr.com/8/7227/27173055360_80b1055f07_z.jpg&quot; width=&quot;640&quot;
                            alt=&quot;Protractor home error&quot;&gt;&lt;/a&gt;
                    &lt;/div&gt;
                    &lt;p&gt;If you do, open &lt;code&gt;src/client/app/+home/home.component.e2e-spec.ts&lt;/code&gt; and change line 17
                        from this:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
element(by.css(&apos;sd-home form input&apos;)).sendKeys(&apos;Tim Berners-Lee&apos;);
&lt;/pre&gt;
                    &lt;p&gt;To this:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
let input = element(by.css(&apos;sd-home form input&apos;));
&apos;Tim Berners-Lee&apos;.split(&apos;&apos;).forEach((c) =&gt; input.sendKeys(c));
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Run &lt;code&gt;npm run e2e&lt;/code&gt; again. You should see a success message similar to the one below in your
                    terminal window.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;protractor-success&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://c1.staticflickr.com/8/7345/26840174984_01e6906f98_c.jpg&quot;
                       title=&quot;Protractor success&quot; rel=&quot;lightbox[testing-angular2.0rc1]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/26840174984/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://c1.staticflickr.com/8/7345/26840174984_01e6906f98_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Protractor success&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;If you made it this far and have all 13 specs passing - congratulations!
                    You&apos;re well on your way to writing quality code with Angular 2 and verifying it works.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
   &lt;h3 id=&quot;_continuous_integration&quot;&gt;Continuous Integration&lt;/h3&gt;
    &lt;div class=&quot;paragraph&quot;&gt;
        &lt;p&gt;The angular2-seed project ships with a &lt;code&gt;.travis.yml&lt;/code&gt; that you can use to run continuous integration for this application
            through &lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis CI&lt;/a&gt;. To enable builds on Travis CI, login and enable builds for the
            GitHub repo you created the project in. Then trigger your first build with a &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;paragraph&quot;&gt;
        &lt;p&gt;When I first tried this, I received a &lt;a href=&quot;https://travis-ci.org/mraible/angular2-tutorial/jobs/135517549#L1189&quot;&gt;failure&lt;/a&gt; because
            Protractor on Travis CI is &lt;a href=&quot;https://github.com/mgechev/angular2-seed/issues/970&quot;&gt;unable to navigate directly&lt;/a&gt;
            to the search and edit components. I was able to workaround this by modifying &lt;code&gt;search.component.e2e-spec.ts&lt;/code&gt; to start
            at the top and navigate to the component.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;listingblock&quot;&gt;
        &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;beforeEach(() =&amp;gt; {
  browser.get(&apos;/&apos;);
  element(by.linkText(&apos;SEARCH&apos;)).click();
});&lt;/pre&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;paragraph&quot;&gt;
        &lt;p&gt;I did something similar with &lt;code&gt;edit.component.e2e-spec.ts&lt;/code&gt;:&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;listingblock&quot;&gt;
        &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;beforeEach(() =&amp;gt; {
  browser.get(&apos;/&apos;);
  element(by.linkText(&apos;SEARCH&apos;)).click();
  let search = element(by.css(&apos;sd-search form input&apos;));
  &apos;Man&apos;.split(&apos;&apos;).forEach((c) =&amp;gt; search.sendKeys(c));
  element(by.css(&apos;sd-search form button&apos;)).click();
  element(by.linkText(&apos;Peyton Manning&apos;)).click();
});&lt;/pre&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;paragraph&quot;&gt;
        &lt;p&gt;After making these changes, &lt;a href=&quot;https://travis-ci.org/mraible/angular2-tutorial/builds/135598015&quot;&gt;all e2e tests passed in Travis CI&lt;/a&gt;.&lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;source_code&quot;&gt;Source code&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;A completed project with this code in it is available on GitHub at &lt;a
                href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;https://github.com/mraible/angular2-tutorial&lt;/a&gt;.
                If you have ideas for improvements, please leave a comment or send a pull request.
            &lt;/p&gt;
            &lt;p&gt;
                I wrote this tutorial in &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt; so you can also &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/blob/master/TESTING.adoc&quot;&gt;read this tutorial on GitHub&lt;/a&gt; or
                &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fangular2-tutorial%2F%2FTESTING.adoc&quot;&gt;using DocGist&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on testing Angular 2.0 RC1 applications. You can see
                the test coverage of your project by running
                &lt;code&gt;npm run serve.coverage&lt;/code&gt;. You&apos;ll notice that the new components and service could use some
                additional coverage. I&apos;ll leave that as a task
                for the reader.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;test-coverage&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;
                &lt;a href=&quot;https://c3.staticflickr.com/8/7381/27350245922_7e184fc630_c.jpg&quot; title=&quot;Test Coverage&quot;
                   rel=&quot;lightbox[testing-angular2.0rc1]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/27350245922/in/datetaken-public/&quot;&gt;&lt;img
                    src=&quot;https://c3.staticflickr.com/8/7381/27350245922_7e184fc630_z.jpg&quot; width=&quot;640&quot;
                    alt=&quot;Test Coverage&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I learned a lot about testing from &lt;a href=&quot;https://www.ng-book.com/2/&quot;&gt;ng-book 2&lt;/a&gt; and
                its Testing chapter. If you have any Angular 2 testing tips and tricks you&apos;d like to share, I&apos;d love to
                hear about them.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1</id>
        <title type="html">Getting Started with Angular 2.0 RC1</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1"/>
        <published>2016-06-03T07:16:18-06:00</published>
        <updated>2016-08-23T23:09:58-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <category term="typescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;A few months ago, I wrote a tutorial on &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting
    Started with Angular 2&lt;/a&gt;. That tutorial was based on Angular 2.0.0 Beta 9. Rather than simply updating that
    tutorial
    and blog post for 2.0.0 RC1, I decided to create a new version for posterity&apos;s sake. The 2.0 Beta 9 version will
    remain on my blog and I&apos;ve &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/releases/tag/2.0.0-beta.9&quot;&gt;tagged
        the source on GitHub&lt;/a&gt;.
    This is an updated version of Getting Started with Angular 2, complete with the
    &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/router.html&quot;&gt;largely undocumented component router&lt;/a&gt;,
    and lazy-loaded components.
&lt;/p&gt;
&lt;p&gt;If you&apos;d just like to see what&apos;s changed since the last release of this tutorial, you can
    &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/pull/2&quot;&gt;view the pull request on GitHub&lt;/a&gt;. Note that I did
    &lt;a href=&quot;https://help.github.com/articles/syncing-a-fork/&quot;&gt;sync my angular2-tutorial project&lt;/a&gt; with &lt;a
        href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt;. This made it fairly easy to upgrade,
    believe it or not. My upgrade notes are &lt;a href=&quot;&quot;&gt;in a gist&lt;/a&gt;. The best diff to read to see what changed is
    likely &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/pull/2/files#diff-7bd4a925c695c2eb0eced3872b9b965f&quot;&gt;
    the diff of this tutorial&lt;/a&gt;.
&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll build a simple web application with Angular 2 and TypeScript. You&apos;ll add search and edit features
                with mock data.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&gt;
            &lt;ul&gt;
                &lt;li&gt;About 15-30 minutes.
                &lt;/li&gt;
                &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ
                    IDEA&lt;/a&gt;.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; installed.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and npm installed. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;create_your_project&quot;&gt;Create your project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt; repository using git:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mgechev/angular2-seed.git angular2-tutorial
cd angular2-tutorial&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</summary>
        <content type="html">&lt;p&gt;A few months ago, I wrote a tutorial on &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting
    Started with Angular 2&lt;/a&gt;. That tutorial was based on Angular 2.0.0 Beta 9. Rather than simply updating that
    tutorial
    and blog post for 2.0.0 RC1, I decided to create a new version for posterity&apos;s sake. The 2.0 Beta 9 version will
    remain on my blog and I&apos;ve &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/releases/tag/2.0.0-beta.9&quot;&gt;tagged
        the source on GitHub&lt;/a&gt;.
    This is an updated version of Getting Started with Angular 2, complete with the
    &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/router.html&quot;&gt;largely undocumented component router&lt;/a&gt;,
    and lazy-loaded components.
&lt;/p&gt;
&lt;p&gt;If you&apos;d just like to see what&apos;s changed since the last release of this tutorial, you can
    &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/pull/2&quot;&gt;view the pull request on GitHub&lt;/a&gt;. Note that I did
    &lt;a href=&quot;https://help.github.com/articles/syncing-a-fork/&quot;&gt;sync my angular2-tutorial project&lt;/a&gt; with &lt;a
        href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt;. This made it fairly easy to upgrade,
    believe it or not. My upgrade notes are &lt;a href=&quot;https://gist.github.com/mraible/d9754864249e1b4bfa344ce80074d73d&quot;&gt;in a gist&lt;/a&gt;. The best diff to read to see what changed is
    likely &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/pull/2/files#diff-7bd4a925c695c2eb0eced3872b9b965f&quot;&gt;
    the diff of this tutorial&lt;/a&gt;.
&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll build a simple web application with Angular 2 and TypeScript. You&apos;ll add search and edit features
                with mock data.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&gt;
            &lt;ul&gt;
                &lt;li&gt;About 15-30 minutes.
                &lt;/li&gt;
                &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ
                    IDEA&lt;/a&gt;.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; installed.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and npm installed. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;create_your_project&quot;&gt;Create your project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt; repository using git:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mgechev/angular2-seed.git angular2-tutorial
cd angular2-tutorial&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;p&gt;
            NOTE: The angular2-seed project requires node v4.x.x or higher and npm 2.14.7. I used node v4.2.6 and npm
            3.9.3.
        &lt;/p&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Install the project&apos;s dependencies:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;run_the_application&quot;&gt;Run the application&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The project is configured with a simple web server for development. To start it, run:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm start&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should see a screen like the one below at &lt;a href=&quot;http://localhost:5555&quot;&gt;http://localhost:5555&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;default-homepage&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;
                &lt;a href=&quot;https://farm2.staticflickr.com/1560/25886693062_4dd41acd3d_c.jpg&quot; title=&quot;Default homepage&quot;
                   rel=&quot;lightbox[getting-started-with-angular2]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/25886693062/in/datetaken-public/&quot;&gt;&lt;img
                    src=&quot;https://farm2.staticflickr.com/1560/25886693062_4dd41acd3d_z.jpg&quot; width=&quot;640&quot;
                    alt=&quot;Default homepage&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You can see your new project&apos;s test coverage by running &lt;code&gt;npm test&lt;/code&gt;:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;=============================== Coverage summary ===============================
Statements   : 96.36% ( 159/165 )
Branches     : 62.5% ( 120/192 )
Functions    : 100% ( 35/35 )
Lines        : 100% ( 129/129 )
================================================================================&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;add_a_search_feature&quot;&gt;Add a search feature&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To add a search feature, open the project in an IDE or your favorite text editor. For IntelliJ IDEA, use
                File &amp;gt; New Project &amp;gt; Static Web and point to the directory you cloned angular2-seed to.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;the_basics&quot;&gt;The Basics&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a &lt;code&gt;src/client/app/+search&lt;/code&gt; folder and a &lt;code&gt;search.component.html&lt;/code&gt; file in it. The + prefix is an indicator
                    to Angular that you want to lazy-load the components in this directory. Populate the HTML file you created with the following:
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;h2&amp;gt;Search&amp;lt;/h2&amp;gt;
&amp;lt;form&amp;gt;
  &amp;lt;input type=&quot;search&quot; [(ngModel)]=&quot;query&quot; (keyup.enter)=&quot;search()&quot;&amp;gt;
  &amp;lt;button type=&quot;button&quot; (click)=&quot;search()&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;div *ngIf=&quot;loading&quot;&amp;gt;loading...&amp;lt;/div&amp;gt;
&amp;lt;pre&amp;gt;{{searchResults | json}}&amp;lt;/pre&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+search/search.component.ts&lt;/code&gt; to define the
                    &lt;code&gt;SearchComponent&lt;/code&gt; and point to this template.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Component } from &apos;@angular/core&apos;;
import { CORE_DIRECTIVES, FORM_DIRECTIVES } from &apos;@angular/common&apos;;
import { ROUTER_DIRECTIVES } from &apos;@angular/router&apos;;

@Component({
  selector: &apos;sd-search&apos;,
  moduleId: module.id,
  templateUrl: &apos;search.component.html&apos;,
  directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, ROUTER_DIRECTIVES]
})
export class SearchComponent {
  loading: boolean;
  query: string;
  searchResults: any;

  constructor() {
    console.log(&apos;initialized search component&apos;);
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+search/index.ts&lt;/code&gt; to export &lt;code&gt;SearchComponent&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;/**
* This barrel file provides the export for the lazy loaded SearchComponent.
*/
export * from &apos;./search.component&apos;;
&lt;/pre&gt;
&lt;p&gt;Update &lt;code&gt;src/client/app/app.component.ts&lt;/code&gt; to import this component and include its route.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { SearchComponent } from &apos;./+search/index&apos;;

@Routes([
  { path: &apos;/&apos;, component: HomeComponent },
  { path: &apos;/about&apos;, component: AboutComponent },
  { path: &apos;/search&apos;, component: SearchComponent }
])
&lt;/pre&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Your browser should refresh automatically, thanks to &lt;a href=&quot;http://browsersync.io&quot;&gt;Browsersync&lt;/a&gt;.
                    Navigate to
                    &lt;a href=&quot;http://localhost:5555/search&quot;&gt;http://localhost:5555/search&lt;/a&gt; and you should see the
                    search component.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;search-component&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1552/25374766404_b0830ff897_c.jpg&quot; title=&quot;Search component&quot;
                       rel=&quot;lightbox[getting-started-with-angular2]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25374766404/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://farm2.staticflickr.com/1552/25374766404_b0830ff897_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Search component&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You can see it needs a bit of styling. Angular 2 allows you to provide styles specific for your
                    component using a &lt;code&gt;styleUrls&lt;/code&gt;
                    property on your component.
                    Add this property to &lt;code&gt;search.component.ts&lt;/code&gt; like you see below.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;templateUrl: &apos;search.component.html&apos;,
styleUrls: [&apos;search.component.css&apos;],
directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, ROUTER_DIRECTIVES]
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+search/search.component.css&lt;/code&gt; and add some CSS.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;:host {
  display: block;
  padding: 0 16px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;There, that looks better!&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://c2.staticflickr.com/8/7433/26828737033_fd196ee3f3_c.jpg&quot;
                       title=&quot;Search component with styling&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25886691122/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://c2.staticflickr.com/8/7433/26828737033_fd196ee3f3_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Search component with styling&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Finally, update &lt;code&gt;src/client/app/shared/navbar/navbar.component.html&lt;/code&gt; to include a link to the search
                    route.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;nav&amp;gt;
  &amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/&apos;&amp;#93;&quot;&amp;gt;HOME&amp;lt;/a&amp;gt;
  &amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/about&apos;&amp;#93;&quot;&amp;gt;ABOUT&amp;lt;/a&amp;gt;
  &amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/search&apos;&amp;#93;&quot;&amp;gt;SEARCH&amp;lt;/a&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This section has shown you how to add a new component to a basic Angular 2 application.
                    The next section shows you how to create a use a JSON file and &lt;code&gt;localStorage&lt;/code&gt; to create a
                    fake API.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;the_backend&quot;&gt;The Backend&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To get search results, create a &lt;code&gt;SearchService&lt;/code&gt; that makes HTTP requests to a JSON file.
                    Start
                    by creating &lt;code&gt;src/client/app/shared/search/data/people.json&lt;/code&gt; to hold your data.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Peyton Manning&quot;,
    &quot;phone&quot;: &quot;(303) 567-8910&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;1234 Main Street&quot;,
      &quot;city&quot;: &quot;Greenwood Village&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80111&quot;
    }
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Demaryius Thomas&quot;,
    &quot;phone&quot;: &quot;(720) 213-9876&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;5555 Marion Street&quot;,
      &quot;city&quot;: &quot;Denver&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80202&quot;
    }
  },
  {
    &quot;id&quot;: 3,
    &quot;name&quot;: &quot;Von Miller&quot;,
    &quot;phone&quot;: &quot;(917) 323-2333&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;14 Mountain Way&quot;,
      &quot;city&quot;: &quot;Vail&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;81657&quot;
    }
  }
]
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/shared/search/search.service.ts&lt;/code&gt; and provide &lt;code&gt;Http&lt;/code&gt; as a
                    dependency in its constructor.
                    In this same file, define the &lt;code&gt;Address&lt;/code&gt; and &lt;code&gt;Person&lt;/code&gt; classes that JSON will be
                    marshalled to.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Injectable } from &apos;@angular/core&apos;;
import { Http, Response } from &apos;@angular/http&apos;;

@Injectable()
export class SearchService {
  constructor(private http:Http) {}

  getAll() {
    return this.http.get(&apos;app/shared/search/data/people.json&apos;).map((res:Response) =&amp;gt; res.json());
  }
}

export class Address {
  street:string;
  city:string;
  state:string;
  zip:string;

  constructor(obj?:any) {
    this.street = obj &amp;amp;&amp;amp; obj.street || null;
    this.city = obj &amp;amp;&amp;amp; obj.city || null;
    this.state = obj &amp;amp;&amp;amp; obj.state || null;
    this.zip = obj &amp;amp;&amp;amp; obj.zip || null;
  }
}

export class Person {
  id:number;
  name:string;
  phone:string;
  address:Address;

  constructor(obj?:any) {
    this.id = obj &amp;amp;&amp;amp; Number(obj.id) || null;
    this.name = obj &amp;amp;&amp;amp; obj.name || null;
    this.phone = obj &amp;amp;&amp;amp; obj.phone || null;
    this.address = obj &amp;amp;&amp;amp; obj.address || null;
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;In &lt;code&gt;search.component.ts&lt;/code&gt;, add imports for these classes.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Person, SearchService } from &apos;../shared/index&apos;;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You can now add a type to the searchResults variable. While you&apos;re there, modify the constructor to
                    inject the
                    &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;searchResults: Array&amp;lt;Person&amp;gt;;

constructor(public searchService: SearchService) {}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then implement the &lt;code&gt;search()&lt;/code&gt; method to call the service&apos;s &lt;code&gt;getAll()&lt;/code&gt; method.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(): void {
  this.searchService.getAll().subscribe(
    data =&amp;gt; {this.searchResults = data;},
    error =&amp;gt; console.log(error)
  );
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;At this point, you&apos;ll likely see the following message in your browser&apos;s console.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre&gt;ORIGINAL EXCEPTION: No provider for SearchService!&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This happens because the app hasn&apos;t provided this service to components. To fix this, create
                    &lt;code&gt;src/client/app/shared/search/index.ts&lt;/code&gt; and populate it with the following.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;/**
 * This barrel file provides the export for the shared SearchService.
 */
export * from &apos;./search.service&apos;;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;
                    Then add a reference to this file in &lt;code&gt;src/client/app/shared/index.ts&lt;/code&gt; so its
                    included in the shared export.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;/**
 * This barrel file provides the exports for the shared resources (services, components).
 */
export * from &apos;./name-list/index&apos;;
export * from &apos;./navbar/index&apos;;
export * from &apos;./toolbar/index&apos;;
export * from &apos;./search/index&apos;;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To fix the &quot;No provider&quot; error from above, update &lt;code&gt;app.component.ts&lt;/code&gt; to import the
                    &lt;code&gt;SearchService&lt;/code&gt; and add the service to the list of provider.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { NameListService, NavbarComponent, ToolbarComponent, SearchService } from &apos;./shared/index&apos;;

@Component({
  selector: &apos;sd-app&apos;,
  viewProviders: [NameListService, SearchService, HTTP_PROVIDERS]
  moduleId: module.id,
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now clicking the search button should work. To make the results look better,
                    remove the &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag in &lt;code&gt;search.component.html&lt;/code&gt;
                    and replace it with a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;table *ngIf=&quot;searchResults&quot;&amp;gt;
  &amp;lt;thead&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;Phone&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;Address&amp;lt;/th&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;
  &amp;lt;tbody&amp;gt;
  &amp;lt;tr *ngFor=&quot;let person of searchResults; let i=index&quot;&amp;gt;
    &amp;lt;td&amp;gt;{{person.name}}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{person.phone}}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{person.address.street}}&amp;lt;br/&amp;gt;
      {{person.address.city}}, {{person.address.state}} {{person.address.zip}}
    &amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then add some additional CSS for this component in &lt;code&gt;search.component.css&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;table {
  margin-top: 10px;
  border-collapse: collapse;
}

th {
  text-align: left;
  border-bottom: 2px solid #ddd;
  padding: 8px;
}

td {
  border-top: 1px solid #ddd;
  padding: 8px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now the search results look better.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;search-results&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1677/25706917270_bf07c541e4_c.jpg&quot; title=&quot;Search results&quot;
                       rel=&quot;lightbox[getting-started-with-angular2]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25706917270/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://farm2.staticflickr.com/1677/25706917270_bf07c541e4_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Search results&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;But wait, we still don&apos;t have search functionality! To add a search feature, add a
                    &lt;code&gt;search()&lt;/code&gt; method to
                    &lt;code&gt;search.service.ts&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(q:string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&amp;gt; {
    let results:any = [];
    data.map(item =&amp;gt; {
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then refactor &lt;code&gt;SearchComponent&lt;/code&gt; to call this method with its &lt;code&gt;query&lt;/code&gt; variable.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(): void {
  this.searchService.search(this.query).subscribe(
    data =&amp;gt; {this.searchResults = data;},
    error =&amp;gt; console.log(error)
  );
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now search results will be filtered by the query value you type in.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This section showed you how to fetch and display search results. The next section builds on this and
                    shows how to edit and save a record.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;add_an_edit_feature&quot;&gt;Add an edit feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Modify &lt;code&gt;search.component.html&lt;/code&gt; to add a link for editing a person.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;td&amp;gt;&amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/edit&apos;, person.id&amp;#93;&quot;&amp;gt;{{person.name}}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+edit/edit.component.html&lt;/code&gt; to display an editable form. You might
                    notice I&apos;ve added &lt;code&gt;id&lt;/code&gt;
                    attributes to most elements. This is to
                    make things easier when writing integration tests with Protractor.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;div *ngIf=&quot;person&quot;&amp;gt;
  &amp;lt;h3&amp;gt;{{editName}}&amp;lt;/h3&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Id:&amp;lt;/label&amp;gt;
    {{person.id}}
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Name:&amp;lt;/label&amp;gt;
    &amp;lt;input [(ngModel)]=&quot;editName&quot; id=&quot;name&quot; placeholder=&quot;name&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Phone:&amp;lt;/label&amp;gt;
    &amp;lt;input [(ngModel)]=&quot;editPhone&quot; id=&quot;phone&quot; placeholder=&quot;Phone&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;fieldset&amp;gt;
    &amp;lt;legend&amp;gt;Address:&amp;lt;/legend&amp;gt;
    &amp;lt;address&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.street&quot; id=&quot;street&quot;&amp;gt;&amp;lt;br/&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.city&quot; id=&quot;city&quot;&amp;gt;,
      &amp;lt;input [(ngModel)]=&quot;editAddress.state&quot; id=&quot;state&quot; size=&quot;2&quot;&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.zip&quot; id=&quot;zip&quot; size=&quot;5&quot;&amp;gt;
    &amp;lt;/address&amp;gt;
  &amp;lt;/fieldset&amp;gt;
  &amp;lt;button (click)=&quot;save()&quot; id=&quot;save&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
  &amp;lt;button (click)=&quot;cancel()&quot; id=&quot;cancel&quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create an &lt;code&gt;EditComponent&lt;/code&gt; in &lt;code&gt;src/search/components/edit.component.ts&lt;/code&gt; that
                    references this template and handles communication with the &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Component, OnInit } from &apos;@angular/core&apos;;
import { Person, Address, SearchService } from &apos;../shared/index&apos;;
import { RouteSegment, Router } from &apos;@angular/router&apos;;

@Component({
  selector: &apos;sd-edit&apos;,
  moduleId: module.id,
  templateUrl: &apos;edit.component.html&apos;,
  styleUrls: [&apos;edit.component.css&apos;]
})
export class EditComponent implements OnInit {

  person: Person;
  editName: string;
  editPhone: string;
  editAddress: Address;

  constructor(
    private _service: SearchService,
    private _router: Router,
    private _routeSegment: RouteSegment
  ) { }

  ngOnInit() {
    let id = +this._routeSegment.getParam(&apos;id&apos;);
    this._service.get(id).subscribe(person =&gt; {
      if (person) {
        this.editName = person.name;
        this.editPhone = person.phone;
        this.editAddress = person.address;
        this.person = person;
      } else {
        this.gotoList();
      }
    });
  }

  cancel() {
    this._router.navigate([&apos;/search&apos;]);
  }

  save() {
    this.person.name = this.editName;
    this.person.phone = this.editPhone;
    this.person.address = this.editAddress;
    this._service.save(this.person);
    this.gotoList();
  }

  gotoList() {
    if (this.person) {
      this._router.navigate([&apos;/search&apos;, {term: this.person.name} ]);
    } else {
      this._router.navigate([&apos;/search&apos;]);
    }
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+edit/index.ts&lt;/code&gt; to export &lt;code&gt;EditComponent&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;/**
 * This barrel file provides the export for the lazy loaded EditComponent.
 */
export * from &apos;./edit.component&apos;;
&lt;/pre&gt;
                &lt;p&gt;Modify &lt;code&gt;SearchService&lt;/code&gt; to contain functions for finding a person by their id, and saving
                    them. While you&apos;re in there, modify
                    the &lt;code&gt;search()&lt;/code&gt; method to
                    be aware of updated objects in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(q:string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&amp;gt; {
    let results = [];
    data.map(item =&amp;gt; {
      // check for item in localStorage
      if (localStorage[&apos;person&apos; + item.id]) {
        item = JSON.parse(localStorage[&apos;person&apos; + item.id]);
      }
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}

get(id: number) {
  return this.getAll().map(all =&amp;gt; {
    if (localStorage[&apos;person&apos; + id]) {
      return JSON.parse(localStorage[&apos;person&apos; + id]);
    }
    return all.find(e =&amp;gt; e.id === id);
  });
}

save(person: Person) {
  localStorage[&apos;person&apos; + person.id] = JSON.stringify(person);
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To make the app aware of this new component, add an import and route configuration in &lt;code&gt;app.component.ts&lt;/code&gt;.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { EditComponent } from &apos;../+edit/index&apos;;

@Routes([
  { path: &apos;/&apos;, component: HomeComponent },
  { path: &apos;/about&apos;, component: AboutComponent },
  { path: &apos;/search&apos;, component: SearchComponent },
  { path: &apos;/edit/:id&apos;, component: EditComponent }
])
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then create &lt;code&gt;src/client/app/+edit/edit.component.css&lt;/code&gt; to make the form look a bit better.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;:host {
  display: block;
  padding: 0 16px;
}

button {
  margin-top: 10px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;At this point, you should be able to search for a person and update their information.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;edit-form&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1603/25886692692_42abb78ef6_c.jpg&quot; title=&quot;Edit component&quot;
                       rel=&quot;lightbox[getting-started-with-angular2]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25886692692/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://farm2.staticflickr.com/1603/25886692692_42abb78ef6_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Edit component&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;The &amp;lt;form&amp;gt; in &lt;code&gt;src/client/app/+edit/edit.component.html&lt;/code&gt; calls a
                    &lt;code&gt;save()&lt;/code&gt; function to update a person&apos;s
                    data. You already implemented this above.
                    The function calls a &lt;code&gt;gotoList()&lt;/code&gt; function that appends the person&apos;s name to the URL when
                    sending the user back to the
                    search screen.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;gotoList() {
  if (this.person) {
    this._router.navigate([&apos;/search&apos;, {term: this.person.name} ]);
  } else {
    this._router.navigate([&apos;/search&apos;]);
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Since the &lt;code&gt;SearchComponent&lt;/code&gt; doesn&apos;t execute a search automatically when you execute this
                    URL, add the following logic to do
                    so in its constructor.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;constructor(public searchService: SearchService, routeSegment: RouteSegment) {
  if (routeSegment.getParam(&apos;term&apos;)) {
    this.query = decodeURIComponent(routeSegment.getParam(&apos;term&apos;));
    this.search();
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You&apos;ll need to import &lt;code&gt;RouteSegment&lt;/code&gt; in order for everything to compile.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre class=&quot;brush: js&quot;&gt;import { ROUTER_DIRECTIVES, RouteSegment } from &apos;@angular/router&apos;;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;After making all these changes, you should be able to search/edit/update a person&apos;s information. If
                    it works - nice job!&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;source_code&quot;&gt;Source code&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;A completed project with this code in it is available on GitHub at &lt;a
                href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;https://github.com/mraible/angular2-tutorial&lt;/a&gt;.
                If you have ideas for improvements, please leave a comment or send a pull request.
            &lt;/p&gt;
            &lt;p&gt;
                I originally wrote this tutorial in &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt; because it has a
                slick feature where you
                can include the source code from files rather than copying and pasting. Since GitHub
                &lt;a href=&quot;https://github.com/github/markup/issues/172&quot;&gt;doesn&apos;t support includes&lt;/a&gt;, I changed things so the
                code is now embedded in the document. You can now &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/blob/master/README.adoc&quot;&gt;read this tutorial on GitHub&lt;/a&gt; or
                &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fangular2-tutorial%2F%2FREADME.adoc&quot;&gt;using DocGist&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on how to get started with Angular 2.0 RC1. In a future
                tutorial, I&apos;ll show you &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_0_rc1&quot;&gt;how to write unit tests and integration tests&lt;/a&gt; for this application.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;I used a number of resources while creating this application. &lt;a href=&quot;https://www.ng-book.com/2/&quot;&gt;ng-book
                    2&lt;/a&gt; was an invaluable resource
                    and I highly recommend it if you&apos;re learning Angular 2. I found Chariot Solution&apos;s article on
                    &lt;a href=&quot;http://chariotsolutions.com/blog/post/angular2-observables-http-separating-services-components/&quot;&gt;Angular2
                        Observables, Http, and
                        separating services and components&lt;/a&gt;
                    to be quite helpful. Finally, the &lt;a href=&quot;https://github.com/angular/angular-cli&quot;&gt;angular-cli&lt;/a&gt;
                    project was a big help, especially its
                    &lt;code&gt;ng generate route &amp;lt;object&amp;gt;&lt;/code&gt; feature.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Kudos to all the pioneers in Angular 2 land that&apos;ve been using it and writing about it on blogs and
                    Stack Overflow.
                    Getting started with Angular 2 would&apos;ve been a real pain without your trailblazing.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/getting_started_with_angular_2</id>
        <title type="html">Getting Started with Angular 2</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/getting_started_with_angular_2"/>
        <published>2016-03-23T09:23:53-06:00</published>
        <updated>2016-06-03T13:19:37-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="typescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p class=&quot;alert alert-info&quot;&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;Click here&lt;/a&gt; to see an updated version of this tutorial that&apos;s been upgraded for Angular 2.0 RC1.&lt;/p&gt;
&lt;p&gt;I was hired by one of my current clients in November to help them develop a project management
    application with AngularJS. I&apos;m proud to say we&apos;ve built the application, it looks great, and it&apos;s
    scheduled to be released next month. The team had lots of experience with ExtJS, but was new to AngularJS.
    While using AngularJS worked, they&apos;re keen on moving to Angular 2 shortly after it&apos;s released.
&lt;/p&gt;
&lt;p&gt;
    To help them learn Angular 2, I decided to write a couple tutorials similar to the AngularJS tutorials
    I wrote last year. In this tutorial, I did my best to keep the functionality and features similar
    to &lt;a href=&quot;http://raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting Started with AngularJS&lt;/a&gt;
    so you can compare &lt;a href=&quot;https://github.com/mraible/angular-tutorial&quot;&gt;the code&lt;/a&gt; between the two.
&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll build a simple web application with Angular 2 and TypeScript. You&apos;ll add search and edit features with mock data.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&gt;
            &lt;ul&gt;
                &lt;li&gt;About 15-30 minutes.
                &lt;/li&gt;
                &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ IDEA&lt;/a&gt;.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; installed.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and npm installed. I recommend using &lt;a href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&quot;create_your_project&quot;&gt;Create your project&lt;/h3&gt;
&lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt; repository using git:&lt;/p&gt;
&lt;pre&gt;git clone https://github.com/mgechev/angular2-seed.git angular2-tutorial
cd angular2-tutorial&lt;/pre&gt;</summary>
        <content type="html">&lt;p class=&quot;alert alert-info&quot;&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;Click here&lt;/a&gt; to see an updated version of this tutorial that&apos;s been upgraded for Angular 2.0 RC1.&lt;/p&gt;
&lt;p&gt;I was hired by one of my current clients in November to help them develop a project management
    application with AngularJS. I&apos;m proud to say we&apos;ve built the application, it looks great, and it&apos;s
    scheduled to be released next month. The team had lots of experience with ExtJS, but was new to AngularJS.
    While using AngularJS worked, they&apos;re keen on moving to Angular 2 shortly after it&apos;s released.
&lt;/p&gt;
&lt;p&gt;
    To help them learn Angular 2, I decided to write a couple tutorials similar to the AngularJS tutorials
    I wrote last year. In this tutorial, I did my best to keep the functionality and features similar
    to &lt;a href=&quot;http://raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting Started with AngularJS&lt;/a&gt;
    so you can compare &lt;a href=&quot;https://github.com/mraible/angular-tutorial&quot;&gt;the code&lt;/a&gt; between the two.
&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll build a simple web application with Angular 2 and TypeScript. You&apos;ll add search and edit features with mock data.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&gt;
            &lt;ul&gt;
                &lt;li&gt;About 15-30 minutes.
                &lt;/li&gt;
                &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ IDEA&lt;/a&gt;.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; installed.
                &lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and npm installed. I recommend using &lt;a href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;create_your_project&quot;&gt;Create your project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt; repository using git:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mgechev/angular2-seed.git angular2-tutorial
cd angular2-tutorial&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;p&gt;
            NOTE: The angular2-seed project requires node v4.x.x or higher and npm 2.14.7. I used node v4.2.6 and npm 3.6.0.
        &lt;/p&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Install &lt;code&gt;ts-node&lt;/code&gt; for TypeScript:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm install -g ts-node&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Install the project&apos;s dependencies:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;run_the_application&quot;&gt;Run the application&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The project is configured with a simple web server for development. To start it, run:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm start&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should see a screen like the one below at &lt;a href=&quot;http://localhost:5555&quot;&gt;http://localhost:5555&lt;/a&gt;.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;default-homepage&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;
                &lt;a href=&quot;https://farm2.staticflickr.com/1560/25886693062_4dd41acd3d_c.jpg&quot; title=&quot;Default homepage&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25886693062/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1560/25886693062_4dd41acd3d_z.jpg&quot; width=&quot;640&quot; alt=&quot;Default homepage&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You can see your new project&apos;s test coverage by running &lt;code&gt;npm test&lt;/code&gt;:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;=============================== Coverage summary ===============================
Statements : 86.11% ( 93/108 )
Branches : 48.28% ( 70/145 )
Functions : 100% ( 25/25 )
Lines : 94.32% ( 83/88 )
================================================================================&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;add_a_search_feature&quot;&gt;Add a search feature&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To add a search feature, open the project in an IDE or your favorite text editor. For IntelliJ IDEA, use
                File &amp;gt; New Project &amp;gt; Static Web and point to the directory you cloned angular2-seed to.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;the_basics&quot;&gt;The Basics&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a file at &lt;code&gt;src/search/components/search.component.html&lt;/code&gt; with the following HTML:&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;h2&amp;gt;Search&amp;lt;/h2&amp;gt;
&amp;lt;form&amp;gt;
  &amp;lt;input type=&quot;search&quot; [(ngModel)]=&quot;query&quot; (keyup.enter)=&quot;search()&quot;&amp;gt;
  &amp;lt;button type=&quot;button&quot; (click)=&quot;search()&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;div *ngIf=&quot;loading&quot;&amp;gt;loading...&amp;lt;/div&amp;gt;
&amp;lt;pre&amp;gt;{{searchResults | json}}&amp;lt;/pre&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/search/components/search.component.ts&lt;/code&gt; to define the &lt;code&gt;SearchComponent&lt;/code&gt; and point to this template.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {Component} from &apos;angular2/core&apos;;
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from &apos;angular2/common&apos;;
import {ROUTER_DIRECTIVES} from &apos;angular2/router&apos;;

@Component({
  selector: &apos;sd-search&apos;,
  moduleId: module.id,
  templateUrl: &apos;./search.component.html&apos;,
  directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, ROUTER_DIRECTIVES]
})
export class SearchComponent {
  loading: boolean;
  query: string;
  searchResults: any;

  constructor() {
    console.log(&apos;initialized search component&apos;);
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Update &lt;code&gt;src/app/components/app.component.ts&lt;/code&gt; to import this component and include its route.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {SearchComponent} from &apos;../../search/components/search.component&apos;;

@RouteConfig([
  { path: &apos;/&apos;,      name: &apos;Home&apos;,  component: HomeComponent  },
  { path: &apos;/about&apos;, name: &apos;About&apos;, component: AboutComponent },
  { path: &apos;/search&apos;, name: &apos;Search&apos;, component: SearchComponent }
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Your browser should refresh automatically, thanks to &lt;a href=&quot;http://browsersync.io&quot;&gt;Browsersync&lt;/a&gt;. Navigate to
                    &lt;a href=&quot;http://localhost:5555/search&quot;&gt;http://localhost:5555/search&lt;/a&gt; and you should see the search component.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;search-component&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1552/25374766404_b0830ff897_c.jpg&quot; title=&quot;Search component&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25374766404/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1552/25374766404_b0830ff897_z.jpg&quot; width=&quot;640&quot; alt=&quot;Search component&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You can see it needs a bit of styling. Angular 2 allows you to provide styles specific for your component using a &lt;code&gt;styleUrls&lt;/code&gt;
                    property on your component.
                    Add this property to &lt;code&gt;search.component.ts&lt;/code&gt; you see below.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;templateUrl: &apos;./search.component.html&apos;,
styleUrls: [&apos;./search.component.css&apos;],
directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, ROUTER_DIRECTIVES]
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/search/components/search.component.css&lt;/code&gt; and add some CSS.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;:host {
  display: block;
  padding: 0 16px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;There, that looks better!&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1563/25886691122_c209b90e33_c.jpg&quot; title=&quot;Search component with styling&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25886691122/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1563/25886691122_c209b90e33_z.jpg&quot; width=&quot;640&quot; alt=&quot;Search component with styling&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Finally, update &lt;code&gt;src/app/components/navbar.component.html&lt;/code&gt; to include a link to the search route.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;nav&amp;gt;
  &amp;lt;a [routerLink]=&quot;[&apos;Home&apos;]&quot;&amp;gt;HOME&amp;lt;/a&amp;gt;
  &amp;lt;a [routerLink]=&quot;[&apos;About&apos;]&quot;&amp;gt;ABOUT&amp;lt;/a&amp;gt;
  &amp;lt;a [routerLink]=&quot;[&apos;Search&apos;]&quot;&amp;gt;SEARCH&amp;lt;/a&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This section has shown you how to add a new component to a basic Angular 2 application.
                    The next section shows you how to create a use a JSON file and &lt;code&gt;localStorage&lt;/code&gt; to create a fake API.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;the_backend&quot;&gt;The Backend&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To get search results, create a &lt;code&gt;SearchService&lt;/code&gt; that makes HTTP requests to a JSON file. Start
                    by creating &lt;code&gt;src/shared/data/people.json&lt;/code&gt; to hold your data.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Peyton Manning&quot;,
    &quot;phone&quot;: &quot;(303) 567-8910&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;1234 Main Street&quot;,
      &quot;city&quot;: &quot;Greenwood Village&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80111&quot;
    }
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Demaryius Thomas&quot;,
    &quot;phone&quot;: &quot;(720) 213-9876&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;5555 Marion Street&quot;,
      &quot;city&quot;: &quot;Denver&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80202&quot;
    }
  },
  {
    &quot;id&quot;: 3,
    &quot;name&quot;: &quot;Von Miller&quot;,
    &quot;phone&quot;: &quot;(917) 323-2333&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;14 Mountain Way&quot;,
      &quot;city&quot;: &quot;Vail&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;81657&quot;
    }
  }
]
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/shared/services/search.service.ts&lt;/code&gt; and provide &lt;code&gt;Http&lt;/code&gt; as a dependency in its constructor.
                    In this same file, define the &lt;code&gt;Address&lt;/code&gt; and &lt;code&gt;Person&lt;/code&gt; classes that JSON will be marshalled to.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {Injectable} from &apos;angular2/core&apos;;
import {Http, Response} from &apos;angular2/http&apos;;

@Injectable()
export class SearchService {
  constructor(private http:Http) {}

  getAll() {
    return this.http.get(&apos;shared/data/people.json&apos;).map((res:Response) =&amp;gt; res.json());
  }
}

export class Address {
  street:string;
  city:string;
  state:string;
  zip:string;

  constructor(obj?:any) {
    this.street = obj &amp;amp;&amp;amp; obj.street || null;
    this.city = obj &amp;amp;&amp;amp; obj.city || null;
    this.state = obj &amp;amp;&amp;amp; obj.state || null;
    this.zip = obj &amp;amp;&amp;amp; obj.zip || null;
  }
}

export class Person {
  id:number;
  name:string;
  phone:string;
  address:Address;

  constructor(obj?:any) {
    this.id = obj &amp;amp;&amp;amp; Number(obj.id) || null;
    this.name = obj &amp;amp;&amp;amp; obj.name || null;
    this.phone = obj &amp;amp;&amp;amp; obj.phone || null;
    this.address = obj &amp;amp;&amp;amp; obj.address || null;
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;In &lt;code&gt;search.component.ts&lt;/code&gt;, add imports for these classes.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {Person, SearchService} from &apos;../../shared/services/search.service&apos;;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You can now add a type to the searchResults variable. While you&apos;re there, modify the constructor to inject the
                    &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;searchResults: Array&amp;lt;Person&amp;gt;;

constructor(public searchService: SearchService) {}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then implement the &lt;code&gt;search()&lt;/code&gt; method to call the service&apos;s &lt;code&gt;getAll()&lt;/code&gt; method.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(): void {
  this.searchService.getAll().subscribe(
    data =&amp;gt; {this.searchResults = data;},
    error =&amp;gt; console.log(error)
  );
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;At this point, you&apos;ll likely see the following message in your browser&apos;s console.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre&gt;EXCEPTION: No provider for SearchService! (SearchComponent -&amp;gt; SearchService)&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This happens because the app hasn&apos;t provided this service to components. To fix this, modify
                    &lt;code&gt;app.component.ts&lt;/code&gt; to import this component and add the service to the list of providers.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {NameListService} from &apos;../../shared/services/name-list.service&apos;;
import {SearchService} from &apos;../../shared/services/search.service&apos;;

@Component({
  selector: &apos;sd-app&apos;,
  viewProviders: [NameListService, SearchService],
  moduleId: module.id,
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Next, you&apos;ll likely get an error about the &lt;code&gt;Http&lt;/code&gt; dependency in &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre&gt;EXCEPTION: No provider for Http! (SearchComponent -&amp;gt; SearchService -&amp;gt; Http)&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To solve this problem, modify &lt;code&gt;src/main.ts&lt;/code&gt; to import the &lt;code&gt;Http&lt;/code&gt; service and make it
                    available to the app.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {HTTP_PROVIDERS} from &apos;angular2/http&apos;;

bootstrap(AppComponent, [
  HTTP_PROVIDERS, ROUTER_PROVIDERS,
  provide(APP_BASE_HREF, { useValue: &apos;&amp;lt;%= APP_BASE %&amp;gt;&apos; })
]);
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now the page will load without errors. However, when you click on the button, you&apos;ll see the following error.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre&gt;ORIGINAL EXCEPTION: TypeError: this.http.get(...).map is not a function&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;I was stuck here for quite some time when I first encountered this issue. I was able to solve it
                    with a simple import in &lt;code&gt;main.ts&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre class=&quot;brush: js&quot;&gt;import &apos;rxjs/add/operator/map&apos;;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now clicking the search button should work. To make the results look better,
                    remove the &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag in &lt;code&gt;search.component.html&lt;/code&gt;
                    and replace it with a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;table *ngIf=&quot;searchResults&quot;&amp;gt;
  &amp;lt;thead&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;Phone&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;Address&amp;lt;/th&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;
  &amp;lt;tbody&amp;gt;
  &amp;lt;tr *ngFor=&quot;#person of searchResults; #i=index&quot;&amp;gt;
    &amp;lt;td&amp;gt;{{person.name}}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{person.phone}}&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;{{person.address.street}}&amp;lt;br/&amp;gt;
      {{person.address.city}}, {{person.address.state}} {{person.address.zip}}
    &amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then add some additional CSS for this component in &lt;code&gt;search.component.css&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;table {
  margin-top: 10px;
  border-collapse: collapse;
}

th {
  text-align: left;
  border-bottom: 2px solid #ddd;
  padding: 8px;
}

td {
  border-top: 1px solid #ddd;
  padding: 8px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now the search results look better.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;search-results&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1677/25706917270_bf07c541e4_c.jpg&quot; title=&quot;Search results&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25706917270/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1677/25706917270_bf07c541e4_z.jpg&quot; width=&quot;640&quot; alt=&quot;Search results&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;But wait, we still don&apos;t have search functionality! To add a search feature, add a &lt;code&gt;search()&lt;/code&gt; method to
                    &lt;code&gt;search.service.ts&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(q:string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&amp;gt; {
    let results = [];
    data.map(item =&amp;gt; {
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then refactor &lt;code&gt;SearchComponent&lt;/code&gt; to call this method with its &lt;code&gt;query&lt;/code&gt; variable.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(): void {
  this.searchService.search(this.query).subscribe(
    data =&amp;gt; {this.searchResults = data;},
    error =&amp;gt; console.log(error)
  );
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now search results will be filtered by the query value you type in.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This section showed you how to fetch and display search results. The next section builds on this and shows how to edit and save a record.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;add_an_edit_feature&quot;&gt;Add an edit feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Modify &lt;code&gt;search.component.html&lt;/code&gt; to add a link for editing a person.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;td&amp;gt;&amp;lt;a [routerLink]=&quot;[&apos;Edit&apos;, { id: person.id }]&quot;&amp;gt;{{person.name}}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/search/components/edit.component.html&lt;/code&gt; to display an editable form. You might notice I&apos;ve added &lt;code&gt;id&lt;/code&gt;
                    attributes to most elements. This is to
                    make things easier when writing integration tests with Protractor.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;div *ngIf=&quot;person&quot;&amp;gt;
  &amp;lt;h3&amp;gt;{{editName}}&amp;lt;/h3&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Id:&amp;lt;/label&amp;gt;
    {{person.id}}
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Name:&amp;lt;/label&amp;gt;
    &amp;lt;input [(ngModel)]=&quot;editName&quot; id=&quot;name&quot; placeholder=&quot;name&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Phone:&amp;lt;/label&amp;gt;
    &amp;lt;input [(ngModel)]=&quot;editPhone&quot; id=&quot;phone&quot; placeholder=&quot;Phone&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;fieldset&amp;gt;
    &amp;lt;legend&amp;gt;Address:&amp;lt;/legend&amp;gt;
    &amp;lt;address&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.street&quot; id=&quot;street&quot;&amp;gt;&amp;lt;br/&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.city&quot; id=&quot;city&quot;&amp;gt;,
      &amp;lt;input [(ngModel)]=&quot;editAddress.state&quot; id=&quot;state&quot; size=&quot;2&quot;&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.zip&quot; id=&quot;zip&quot; size=&quot;5&quot;&amp;gt;
    &amp;lt;/address&amp;gt;
  &amp;lt;/fieldset&amp;gt;
  &amp;lt;button (click)=&quot;save()&quot; id=&quot;save&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
  &amp;lt;button (click)=&quot;cancel()&quot; id=&quot;cancel&quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create an &lt;code&gt;EditComponent&lt;/code&gt; in &lt;code&gt;src/search/components/edit.component.ts&lt;/code&gt; that references this template and handles communication with the &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {Component, OnInit} from &apos;angular2/core&apos;;
import {Person, Address, SearchService} from &apos;../../shared/services/search.service&apos;;
import {RouteParams, Router} from &apos;angular2/router&apos;;
import {CanDeactivate, ComponentInstruction} from &apos;angular2/router&apos;;

@Component({
  selector: &apos;sd-edit&apos;,
  moduleId: module.id,
  templateUrl: &apos;./edit.component.html&apos;,
  styleUrls: [&apos;./edit.component.css&apos;]
})
export class EditComponent implements OnInit, CanDeactivate {

  person: Person;
  editName: string;
  editPhone: string;
  editAddress: Address;

  constructor(
    private _service: SearchService,
    private _router: Router,
    private _routeParams: RouteParams
  ) { }

  ngOnInit() {
    let id = +this._routeParams.get(&apos;id&apos;);
    this._service.get(id).subscribe(person =&amp;gt; {
      if (person) {
        this.editName = person.name;
        this.editPhone = person.phone;
        this.editAddress = person.address;
        this.person = person;
      } else {
        this.gotoList();
      }
    });
  }

  routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction): any {
    if (!this.person || this.person.name === this.editName || this.person.phone === this.editPhone
      || this.person.address === this.editAddress) {
      return true;
    }

    return new Promise&amp;lt;boolean&amp;gt;((resolve, reject) =&amp;gt; resolve(window.confirm(&apos;Discard changes?&apos;)));
  }

  cancel() {
    this._router.navigate([&apos;Search&apos;]);
  }

  save() {
    this.person.name = this.editName;
    this.person.phone = this.editPhone;
    this.person.address = this.editAddress;
    this._service.save(this.person);
    this.gotoList();
  }

  gotoList() {
    if (this.person) {
      this._router.navigate([&apos;Search&apos;, { term: this.person.name }]);
    } else {
      this._router.navigate([&apos;Search&apos;]);
    }
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Modify &lt;code&gt;SearchService&lt;/code&gt; to contain functions for finding a person by their id, and saving them. While you&apos;re in there, modify
                    the &lt;code&gt;search()&lt;/code&gt; method to
                    be aware of updated objects in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(q:string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&amp;gt; {
    let results = [];
    data.map(item =&amp;gt; {
      // check for item in localStorage
      if (localStorage[&apos;person&apos; + item.id]) {
        item = JSON.parse(localStorage[&apos;person&apos; + item.id]);
      }
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}

get(id: number) {
  return this.getAll().map(all =&amp;gt; {
    if (localStorage[&apos;person&apos; + id]) {
      return JSON.parse(localStorage[&apos;person&apos; + id]);
    }
    return all.find(e =&amp;gt; e.id === id);
  });
}

save(person: Person) {
  localStorage[&apos;person&apos; + person.id] = JSON.stringify(person);
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To make the app are of this new component, add an import and route configuration in &lt;code&gt;app.component.ts&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {EditComponent} from &apos;../../search/components/edit.component&apos;;

@RouteConfig([
  { path: &apos;/&apos;,      name: &apos;Home&apos;,  component: HomeComponent  },
  { path: &apos;/about&apos;, name: &apos;About&apos;, component: AboutComponent },
  { path: &apos;/search&apos;, name: &apos;Search&apos;, component: SearchComponent },
  { path: &apos;/edit/:id&apos;, name: &apos;Edit&apos;, component: EditComponent }
])
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then create &lt;code&gt;src/search/components/edit.component.css&lt;/code&gt; to make the form look a bit better.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;:host {
  display: block;
  padding: 0 16px;
}

button {
  margin-top: 10px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;At this point, you should be able to search for a person and update their information.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;edit-form&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1603/25886692692_42abb78ef6_c.jpg&quot; title=&quot;Edit component&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25886692692/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1603/25886692692_42abb78ef6_z.jpg&quot; width=&quot;640&quot; alt=&quot;Edit component&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;The &amp;lt;form&amp;gt; in &lt;code&gt;src/search/components/edit.component.html&lt;/code&gt; calls a &lt;code&gt;save()&lt;/code&gt; function to update a person&apos;s
                    data. You already implemented this above.
                    The function calls a &lt;code&gt;gotoList()&lt;/code&gt; function that appends the person&apos;s name to the URL when sending the user back to the
                    search screen.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;gotoList() {
  if (this.person) {
    this._router.navigate([&apos;Search&apos;, { term: this.person.name }]);
  } else {
    this._router.navigate([&apos;Search&apos;]);
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Since the &lt;code&gt;SearchComponent&lt;/code&gt; doesn&apos;t execute a search automatically when you execute this URL, add the following logic to do
                    so in its constructor.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;constructor(public searchService: SearchService, params: RouteParams) {
  if (params.get(&apos;term&apos;)) {
    this.query = decodeURIComponent(params.get(&apos;term&apos;));
    this.search();
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You&apos;ll need to import &lt;code&gt;RouteParams&lt;/code&gt; in order for everything to compile.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre class=&quot;brush: js&quot;&gt;import {ROUTER_DIRECTIVES, RouteParams} from &apos;angular2/router&apos;;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;After making all these changes, you should be able to search/edit/update a person&apos;s information. If it works - nice job!&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;source_code&quot;&gt;Source code&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;A completed project with this code in it is available on GitHub at &lt;a href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;https://github.com/mraible/angular2-tutorial&lt;/a&gt;.
                  If you have ideas for improvements, please leave a comment or send a pull request. 
            &lt;/p&gt;
            &lt;p&gt;
                I originally wrote this tutorial in &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt; because it has a slick feature where you
                can include the source code from files rather than copying and pasting. Unfortunately, GitHub
                &lt;a href=&quot;https://github.com/github/markup/issues/172&quot;&gt;doesn&apos;t support includes&lt;/a&gt;. You can
                &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fangular2-tutorial%2F%2FREADME.adoc&quot;&gt;use DocGist to view this tutorial&lt;/a&gt;,
                but &lt;a href=&quot;https://github.com/asciidoctor/docgist/issues/11&quot;&gt;includes don&apos;t work&lt;/a&gt; there either.
            &lt;/p&gt;
            &lt;p&gt;If you&apos;d like to see the Asciidoctor-generated version of this tutorial, you can install the gem, checkout the project from GitHub,
                and then run &lt;code&gt;asciidoctor README.adoc&lt;/code&gt; to produce a &lt;code&gt;README.html&lt;/code&gt; file.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on how to get started with Angular 2. In a future tutorial,
                I&apos;ll show you &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;how to write unit tests and integration tests for this application&lt;/a&gt;. I&apos;ve also started looking into creating an ES6 version of this tutorial using So&#243;s G&#225;bor&apos;s &lt;a href=&quot;https://github.com/blacksonic/angular2-es6-starter&quot;&gt;angular2-es6-starter&lt;/a&gt;. If you know of a better starter for Angular 2 and ES6, please let me know.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;I used a number of resources while creating this application. &lt;a href=&quot;https://www.ng-book.com/2/&quot;&gt;ng-book 2&lt;/a&gt; was an invaluable resource
                    and I highly recommend it if you&apos;re learning Angular 2. I found Chariot Solution&apos;s article on
                    &lt;a href=&quot;http://chariotsolutions.com/blog/post/angular2-observables-http-separating-services-components/&quot;&gt;Angular2 Observables, Http, and
                        separating services and components&lt;/a&gt;
                    to be quite helpful. Finally, the &lt;a href=&quot;https://github.com/angular/angular-cli&quot;&gt;angular-cli&lt;/a&gt; project was a big help, especially its
                    &lt;code&gt;ng generate route &amp;lt;object&amp;gt;&lt;/code&gt; feature.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Kudos to all the pioneers in Angular 2 land that&apos;ve been using it and writing about it on blogs and Stack Overflow.
                    Getting started with Angular 2 would&apos;ve been a real pain without your trailblazing.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/how_to_implement_a_smart</id>
        <title type="html">How to Implement a Smart Chunking Bootstrap Carousel with AngularJS</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/how_to_implement_a_smart"/>
        <published>2016-03-15T09:47:30-06:00</published>
        <updated>2016-03-15T15:48:49-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ui-bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="carousel" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css" scheme="http://roller.apache.org/ns/tags/" />
        <category term="responsive" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
    I&apos;ve been helping a client develop a project management application for the last several months. One of the features
    I implemented uses &lt;a href=&quot;https://angular-ui.github.io/bootstrap/#/carousel&quot;&gt;UI Bootstrap&apos;s carousel directive&lt;/a&gt; to display a list of project templates to choose from when creating a new project. Rather than displaying
    one at a time, we wanted to display as many as the user&apos;s screen would allow. That is, if they were on a large monitor,
    we wanted to display five templates, a medium size monitor would display three and so on. This is a story of how I implemented a
    &lt;em&gt;smart chunking carousel&lt;/em&gt;.
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
    I&apos;ve been helping a client develop a project management application for the last several months. One of the features
    I implemented uses &lt;a href=&quot;https://angular-ui.github.io/bootstrap/#/carousel&quot;&gt;UI Bootstrap&apos;s carousel directive&lt;/a&gt; to display a list of project templates to choose from when creating a new project. Rather than displaying
    one at a time, we wanted to display as many as the user&apos;s screen would allow. That is, if they were on a large monitor,
    we wanted to display five templates, a medium size monitor would display three and so on. This is a story of how I implemented a
    &lt;em&gt;smart chunking carousel&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;
    To begin, I made it possible to show groups of items in the carousel using &lt;a href=&quot;http://stackoverflow.com/a/21653981/65681&quot;&gt;array chunking&lt;/a&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
  function chunk(arr, size) {
    var newArr = [];
    var arrayLength = arr.length;
    for (var i = 0; i &lt; arrayLength; i += size) {
      newArr.push(arr.slice(i, i + size));
    }
    return newArr;
  }
&lt;/pre&gt;
&lt;p&gt;Using UI Bootstrap&apos;s &lt;a href=&quot;https://angular-ui.github.io/bootstrap/#/carousel&quot;&gt;example code&lt;/a&gt;, I created &lt;code&gt;$scope.chunkedSlides&lt;/code&gt;
from &lt;code&gt;$scope.slides&lt;/code&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$scope.chunkSize = 5;

// chunk slides so there&apos;s two per chunk by default
$scope.chunkedSlides = chunk($scope.slides, $scope.chunkSize);
&lt;/pre&gt;
&lt;p&gt;Next, I changed the HTML template to read the grouped slides, and show each one.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;uib-carousel active=&quot;active&quot; interval=&quot;0&quot; no-wrap=&quot;true&quot;&amp;gt;
  &amp;lt;uib-slide ng-repeat=&quot;row in chunkedSlides&quot;&amp;gt;
    &amp;lt;div class=&quot;row&quot;&amp;gt;
      &amp;lt;div ng-repeat=&quot;slide in row track by $index&quot; class=&quot;slide&quot;&amp;gt;
        &amp;lt;img ng-src=&quot;{{slide.image}}&quot;&amp;gt;
        &amp;lt;div class=&quot;carousel-caption&quot;&amp;gt;
          &amp;lt;h4&amp;gt;Slide {{slide.id}}&amp;lt;/h4&amp;gt;
          &amp;lt;p&amp;gt;{{slide.text}}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/uib-slide&amp;gt;
&amp;lt;/uib-carousel&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This was enough to get five squares on a large monitor. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;img src=&quot;https://farm2.staticflickr.com/1704/25673191306_39ee95e5a0_z.jpg&quot; width=&quot;640&quot; alt=&quot;Carousel Diagram&quot;&gt;
&lt;/p&gt;
&lt;p&gt;
However, I wanted to go further and reduce the number per group on smaller monitors. I created a &lt;code&gt;SmartChunking&lt;/code&gt;
    service that defined how many per group for each possible width.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
angular.module(&apos;app&apos;).service(&apos;SmartChunking&apos;, function() {
  var large = 1600;
  var medium = 1200;
  var small = 1024;
  var xsmall = 800;

  this.getChunkSize = function(width) {
    var chunkSize;
    if (width &gt;= large) {
      chunkSize = 5;
    } else if (width &gt;= medium) {
      chunkSize = 4;
    } else if (width &gt;= small) {
      chunkSize = 3;
    } else if (width &gt;= xsmall) {
      chunkSize = 2;
    } else {
      chunkSize = 1;
    }
    return chunkSize;
  }
});
&lt;/pre&gt;
&lt;p&gt;
    I wrote a &lt;code&gt;smart-chunking&lt;/code&gt; directive to fire an event with the chunk size.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
angular.module(&apos;app&apos;).directive(&apos;smartChunking&apos;, function($window, SmartChunking) {
  return {
    restrict: &apos;A&apos;,
    link: function($scope) {
      var w = angular.element($window);

      // window.outerWidth works on desktop, screen.height on iPad (width returns 768)
      var width = ($window.outerWidth &gt; 0) ? $window.outerWidth : screen.height;
      var chunkSize = SmartChunking.getChunkSize(width);
      if (chunkSize !== 5) {
        $scope.$emit(&apos;change-chunk-size&apos;, chunkSize);
      }

      $scope.getWidth = function() {
        return ($window.outerWidth &gt; 0) ? $window.outerWidth : screen.width;
      };

      $scope.$watch($scope.getWidth, function(newValue, oldValue) {
        if (newValue !== oldValue) {
          var chunkSize = SmartChunking.getChunkSize(newValue);
          $scope.$emit(&apos;change-chunk-size&apos;, chunkSize);
        }
      });

      w.bind(&apos;resize&apos;, function() {
        $scope.$apply();
      });
    }
  }
});
&lt;/pre&gt;
&lt;p&gt;Then I added a listener for this in the controller that populated the carousel.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$scope.$on(&apos;change-chunk-size&apos;, function(event, data) {
  if (data !== $scope.chunkSize) {
    $scope.chunkedSlides = chunk($scope.slides, data);
    $scope.chunkSize = data;
  }
});
&lt;/pre&gt;
&lt;p&gt;
    The final step was adding the &lt;code&gt;smark-chunking&lt;/code&gt; directive to each slide and dynamically determining its &lt;code&gt;col-sm-*&lt;/code&gt; class.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div ng-repeat=&quot;slide in row track by $index&quot; class=&quot;slide&quot; ng-class=&quot;getSlideClass(chunkSize)&quot; smart-chunking&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The controller contains a map of classes that map to chunk sizes:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
var classMap = {
  5: &apos;col-sm-2&apos;,
  4: &apos;col-sm-3&apos;,
  3: &apos;col-sm-4&apos;,
  2: &apos;col-sm-5&apos;,
};

$scope.getSlideClass = function(chunkSize) {
  if (classMap[chunkSize]) {
    return classMap[chunkSize];
  } else {
    return &apos;col-sm-10&apos;;
  }
}
&lt;/pre&gt;
&lt;p&gt;I did find that adding some CSS made things look quite a bit better.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.carousel-caption {
  padding-bottom: 0;
}

.carousel-control.left,
.carousel-control.right {
  background-image: none;
}

.carousel-indicators {
  display: none;
}

.carousel-inner {
  padding-left: 10%;
  overflow: visible;
}

.carousel-control .glyphicon-chevron-left,
.carousel-control .glyphicon-chevron-right {
  font-size: 100px;
  margin-top: -60px;
  font-style: normal;
  font-weight: 100;
}

.carousel-control .glyphicon-chevron-left {
  margin-left: -100px;
}

.carousel-control .glyphicon-chevron-right {
  margin-right: -40px;
}

/* make slide widths more responsive */
@media only screen and (min-width: 1600px) {
  .col-sm-2 {
    width: 18%;
  }
}

@media only screen and (min-width: 1200px) {
  .col-sm-3 {
    width: 22%;
  }
  .carousel-control .glyphicon-chevron-left {
    margin-left: -70px;
  }
  .carousel-control .glyphicon-chevron-right {
    margin-right: -20px;
  }
}

@media only screen and (max-width: 1200px) {
  .col-sm-4 {
    width: 29%;
  }
  .carousel-control .glyphicon-chevron-left {
    margin-left: -70px;
  }
  .carousel-control .glyphicon-chevron-right {
    margin-right: -20px;
  }
}

@media only screen and (max-width: 800px) {
  .col-sm-10 {
    width: 90%;
  }
}
&lt;/pre&gt;
&lt;p&gt;I hope this tip helps you if you need to implement a similar feature. I&apos;ve published a &lt;a href=&quot;https://plnkr.co/edit/BQkTiIUTbiLuOQ03QV8z?p=preview&quot;&gt;demo on Plunkr&lt;/a&gt; (best experienced in embedded view).&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;https://farm2.staticflickr.com/1504/25663696696_2517b5f33d_b.jpg&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25663696696/in/datetaken-public/&quot; title=&quot;Smark Chunking Carousel&quot; rel=&quot;lightbox[smart-chunking-carousel]&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1504/25663696696_2517b5f33d_z.jpg&quot; width=&quot;640&quot;  alt=&quot;Smark Chunking Carousel&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/2015_a_year_in_review</id>
        <title type="html">2015 - A Year in Review</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/2015_a_year_in_review"/>
        <published>2016-01-12T16:21:15-07:00</published>
        <updated>2016-03-11T22:46:30-07:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="blogging" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <category term="syncro" scheme="http://roller.apache.org/ns/tags/" />
        <category term="2015" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yearinreview" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apacheroller" scheme="http://roller.apache.org/ns/tags/" />
        <category term="porschebus" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
    2015 was the year &lt;a href=&quot;//raibledesigns.com/rd/category/The+Bus&quot;&gt;The Bus&lt;/a&gt; was supposed to be finished. If you
    read my &lt;a
    href=&quot;//raibledesigns.com/rd/entry/2014_a_year_in_review&quot;&gt;year in review from last year&lt;/a&gt;, you&apos;ll see I was
    certain of it. To be fair, I did have estimates from people that had me expecting it to be done in July. The good
    news is the interior was finished in July. Since then, it&apos;s been back at Reincarnation getting the finishing touches
    applied. I believe if it was worked on for a week straight, it could be finished. It&apos;s &lt;em&gt;that&lt;/em&gt; close. So
    close I can taste it. THIS will be the year!
&lt;/p&gt;
&lt;p&gt;For this &lt;a href=&quot;//raibledesigns.com/rd/tags/yearinreview&quot;&gt;Year in Review&lt;/a&gt; post, I&apos;ll same the format I&apos;ve used
    the last few years.
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#professional&quot;&gt;Professional&lt;/a&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#speaking&quot;&gt;Speaking&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#devoxx4kids&quot;&gt;Devoxx4Kids&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#projects&quot;&gt;Projects&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#personal&quot;&gt;Personal&lt;/a&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#theskibus&quot;&gt;The Ski Bus&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#2016&quot;&gt;2016&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;professional&quot;&gt;Professional&lt;/h2&gt;
&lt;p&gt;I had four different clients in 2015. The first was in the healthcare industry, the second in the API hosting space,
    one in the fashion industry and one in computer software. For the first client, I wrote about &lt;a
        href=&quot;//raibledesigns.com/rd/entry/integrating_node_js_ruby_and&quot;&gt;integrating Node.js,
        Ruby and Spring with Okta&apos;s SAML support&lt;/a&gt;. I also &lt;a
        href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;helped them adopt&lt;/a&gt; and
    &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;learn AngularJS&lt;/a&gt;. Learning about
    &lt;a href=&quot;//raibledesigns.com/rd/entry/best_practices_for_using_foundation1&quot;&gt;Foundation and Angular&lt;/a&gt; was a nice
    treat too.
&lt;/p&gt;
&lt;p&gt;In March, I revisited &lt;a href=&quot;//raibledesigns.com/rd/entry/how_to_setup_your_own1&quot;&gt;how to setup your own software
    company&lt;/a&gt;. In that post, I wrote about how I felt when valuing time over money.&lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&gt;
    Earlier this year, I had the opportunity to work 20 hours per week instead of 40. It was one of the greatest
    work-life experiences I&apos;ve had to date. I was still able to pay all my bills, and I had time during
    each-and-every-day to do something fun. When working 40 hours per week, exercising and cooking dinner were somewhat
    of a chore. When I flipped to working less, work became the chore and exercise and cooking became the fun parts of
    my day. I read somewhere recently that if Americans valued &lt;em&gt;health over wealth&lt;/em&gt;, we&apos;d be a lot better off. I
    felt like I did this when working less and that I was &lt;em&gt;rich in time&lt;/em&gt;.
&lt;/blockquote&gt;</summary>
        <content type="html">&lt;p&gt;
    2015 was the year &lt;a href=&quot;//raibledesigns.com/rd/category/The+Bus&quot;&gt;The Bus&lt;/a&gt; was supposed to be finished. If you
    read my &lt;a
    href=&quot;//raibledesigns.com/rd/entry/2014_a_year_in_review&quot;&gt;year in review from last year&lt;/a&gt;, you&apos;ll see I was
    certain of it. To be fair, I did have estimates from people that had me expecting it to be done in July. The good
    news is the interior was finished in July. Since then, it&apos;s been back at Reincarnation getting the finishing touches
    applied. I believe if it was worked on for a week straight, it could be finished. It&apos;s &lt;em&gt;that&lt;/em&gt; close. So
    close I can taste it. THIS will be the year!
&lt;/p&gt;
&lt;p&gt;For this &lt;a href=&quot;//raibledesigns.com/rd/tags/yearinreview&quot;&gt;Year in Review&lt;/a&gt; post, I&apos;ll same the format I&apos;ve used
    the last few years.
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#professional&quot;&gt;Professional&lt;/a&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#speaking&quot;&gt;Speaking&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#devoxx4kids&quot;&gt;Devoxx4Kids&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#projects&quot;&gt;Projects&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#personal&quot;&gt;Personal&lt;/a&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#theskibus&quot;&gt;The Ski Bus&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/2015_a_year_in_review#2016&quot;&gt;2016&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;professional&quot;&gt;Professional&lt;/h2&gt;
&lt;p&gt;I had four different clients in 2015. The first was in the healthcare industry, the second in the API hosting space,
    one in the fashion industry and one in computer software. For the first client, I wrote about &lt;a
        href=&quot;//raibledesigns.com/rd/entry/integrating_node_js_ruby_and&quot;&gt;integrating Node.js,
        Ruby and Spring with Okta&apos;s SAML support&lt;/a&gt;. I also &lt;a
        href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;helped them adopt&lt;/a&gt; and
    &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;learn AngularJS&lt;/a&gt;. Learning about
    &lt;a href=&quot;//raibledesigns.com/rd/entry/best_practices_for_using_foundation1&quot;&gt;Foundation and Angular&lt;/a&gt; was a nice
    treat too.
&lt;/p&gt;
&lt;p&gt;In March, I revisited &lt;a href=&quot;//raibledesigns.com/rd/entry/how_to_setup_your_own1&quot;&gt;how to setup your own software
    company&lt;/a&gt;. In that post, I wrote about how I felt when valuing time over money.&lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&gt;
    Earlier this year, I had the opportunity to work 20 hours per week instead of 40. It was one of the greatest
    work-life experiences I&apos;ve had to date. I was still able to pay all my bills, and I had time during
    each-and-every-day to do something fun. When working 40 hours per week, exercising and cooking dinner were somewhat
    of a chore. When I flipped to working less, work became the chore and exercise and cooking became the fun parts of
    my day. I read somewhere recently that if Americans valued &lt;em&gt;health over wealth&lt;/em&gt;, we&apos;d be a lot better off. I
    felt like I did this when working less and that I was &lt;em&gt;rich in time&lt;/em&gt;.
&lt;/blockquote&gt;
&lt;p&gt;
    The second client asked me to do a research project where I tried a bunch of API platforms (e.g.
    Apigee and 3Scale) and wrote up an analysis about five different stories they provided. It was a fun project where
    I learned a lot.
&lt;/p&gt;
&lt;p&gt;
    In April, I went to work at a fashion industry startup in downtown Denver. I wrote JavaScript, CSS and HTML for them
    for most of the year. The location afforded me the ability to discontinue my office at
    &lt;a href=&quot;http://www.thriveworkplace.com/en&quot;&gt;Thrive&lt;/a&gt;, a co-working space in Denver. While I missed having kombucha
    and
    local craft beer on tap, I loved the standup desk and the office environment with conversations, banter and laughter
    between desks.
&lt;/p&gt;
&lt;p&gt;In November, I picked up a new client that I&apos;m currently working with. They&apos;ve recently adopted Angular and I&apos;ve
    enjoyed the experience of working with a team of UI developers. We&apos;re putting a fresh UI on a legacy product and
    the team has been able to move really quickly. They&apos;re also in the Bay Area so it&apos;s been fun traveling to Silicon
    Valley once a month. My contract with them ends this spring.
&lt;/p&gt;
&lt;h3 id=&quot;speaking&quot;&gt;Speaking&lt;/h3&gt;
&lt;p&gt;
    I spoke about &lt;a href=&quot;//raibledesigns.com/rd/entry/the_art_of_angularjs_in&quot;&gt;Angular at Denver&apos;s Open Source User
    Group&lt;/a&gt; in February
    and about &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_hip_with_jhipster_at&quot;&gt;JHipster at Denver&apos;s Java User
    Group&lt;/a&gt; in April. In the second half of the year, I re-joined the conference circuit, starting with
    &lt;a href=&quot;//raibledesigns.com/rd/entry/uberconf_2015_my_presentations_on&quot;&gt;UberConf&lt;/a&gt; in July. September brought me
    to &lt;a href=&quot;//raibledesigns.com/rd/entry/springone_2gx_2015_my_presentations&quot;&gt;SpringOne 2GX&lt;/a&gt; in Washington, DC
    and the &lt;a href=&quot;//raibledesigns.com/rd/entry/angular_summit_2015&quot;&gt;Angular Summit&lt;/a&gt; in Boston.
&lt;/p&gt;
&lt;p&gt;
    Trish and I giggled with glee as we &lt;a href=&quot;//raibledesigns.com/rd/entry/devoxx_2015_a_java_hipster&quot;&gt;returned to
    the Devoxx Belgium&lt;/a&gt; in November.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a rel=&quot;lightbox[2015yearinreview]&quot; href=&quot;https://farm6.staticflickr.com/5696/22675879269_b89c0d7368_c.jpg&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/22675879269/&quot; title=&quot;First stop: Bier Central!&quot;&gt;&lt;img
        src=&quot;https://farm6.staticflickr.com/5696/22675879269_b89c0d7368_m.jpg&quot; width=&quot;240&quot;
        alt=&quot;First stop: Bier Central!&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a rel=&quot;lightbox[2015yearinreview]&quot; href=&quot;https://farm6.staticflickr.com/5811/22649819637_1381655f70_c.jpg&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/22649819637/&quot; title=&quot;Shopping&quot;&gt;&lt;img
        src=&quot;https://farm6.staticflickr.com/5811/22649819637_1381655f70_m.jpg&quot; width=&quot;240&quot; alt=&quot;Shopping&quot;
        style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    &lt;a href=&quot;https://www.youtube.com/watch?v=baVOGuFIe9M&quot;&gt;My JHipster talk was a hit&lt;/a&gt; and we loved our brief tour of
    Brugge.
&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm6.staticflickr.com/5752/22690142739_24b443fc6a_c.jpg&quot; title=&quot;Stadhuis Brugge Belgium&quot;
       rel=&quot;lightbox[2015yearinreview]&quot; data-href=&quot;https://www.flickr.com/photos/mcginityphoto/22690142739&quot;&gt;&lt;img
        src=&quot;https://farm6.staticflickr.com/5752/22690142739_24b443fc6a.jpg&quot; width=&quot;500&quot; alt=&quot;Stadhuis Brugge Belgium&quot;
        style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
    As always, the people and the memories were the
    best part.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/595/22445469834_21210403fb_c.jpg&quot;
       title=&quot;Java Hipsters!&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/22445469834&quot;&gt;&lt;img
        src=&quot;https://farm1.staticflickr.com/595/22445469834_21210403fb_m.jpg&quot; width=&quot;240&quot; alt=&quot;Java Hipsters!&quot;
        style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;https://farm1.staticflickr.com/579/23042227126_7e05081502_c.jpg&quot;
       title=&quot;More Cowbell!&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/23042227126&quot;&gt;&lt;img
        src=&quot;https://farm1.staticflickr.com/579/23042227126_7e05081502_m.jpg&quot; width=&quot;240&quot; alt=&quot;More Cowbell!&quot;
        style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;
    To end the year, I journeyed to Fort Lauderdale for the Rich Web Experience. After speaking, I worked remotely and
    networked for a few days. The networking lasted until the wee hours of the morning and good fun was had by all.
&lt;/p&gt;


&lt;h3 id=&quot;devoxx4kids&quot;&gt;Devoxx4Kids&lt;/h3&gt;
&lt;p&gt;I didn&apos;t plan any &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver&quot;&gt;Devoxx4Kids&lt;/a&gt; events until later in the
    year. The Denver Chapter watched Arun and Aditya Gupta &lt;a
        href=&quot;//raibledesigns.com/rd/entry/minecraft_modding_at_denver_s&quot;&gt;
        speak about modding Minecraft&lt;/a&gt; in August. I also presented on
    &lt;a href=&quot;//raibledesigns.com/rd/entry/setting_up_a_minecraft_server&quot;&gt;setting up a Minecraft server in the cloud&lt;/a&gt;.
    In October, the talented &lt;a href=&quot;http://dbakevlar.com/&quot;&gt;Kellyn Pot&apos;Vin-Gorman&lt;/a&gt; taught a class about &lt;a
        href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/events/224932151/&quot;&gt;coding
        simple games with the Raspberry Pi&lt;/a&gt;. It&apos;s always fun to see the kids&apos; faces light up when they solve a
    programming problem.
&lt;/p&gt;
&lt;p&gt;
    This year, with the help of &lt;a href=&quot;http://tuliva.com/&quot;&gt;Tuliva&lt;/a&gt;, we hope to host one Devoxx4Kids class per
    quarter. If there&apos;s a particular topic
    or speaker you&apos;d like to see, &lt;a href=&quot;//raibledesigns.com/rd/page/contact&quot;&gt;please let me know&lt;/a&gt;.
&lt;/p&gt;

&lt;h3 id=&quot;projects&quot;&gt;Projects&lt;/h3&gt;
&lt;p&gt;
    &lt;a href=&quot;http://www.infoq.com/minibooks/jhipster-mini-book&quot;&gt;
        &lt;img src=&quot;http://www.infoq.com/resource/minibooks/jhipster-mini-book/en/cover/Cover.jpg&quot;
             alt=&quot;The JHipster Mini-Book&quot; width=&quot;100&quot; class=&quot;picture&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
I released a &lt;a href=&quot;//raibledesigns.com/rd/entry/appfuse_3_5_released&quot;&gt;new version of AppFuse in February&lt;/a&gt; and
    started working on the &lt;a href=&quot;http://jhipster.github.io/&quot;&gt;JHipster&lt;/a&gt; Mini-Book in March. To help
    provide updates about
    the book, I created a blogging app with JHipster and started &lt;a href=&quot;http://www.jhipster-book.com&quot;&gt;www.jhipster-book.com&lt;/a&gt;.
    I wrote the book and created
    &lt;a href=&quot;http://www.21-points.com&quot;&gt;21-Points Health&lt;/a&gt; as its
    example application. I &lt;a href=&quot;http://www.jhipster-book.com/#!/news/entry/jhipster-at-devoxx-belgium&quot;&gt;open-sourced
        21-Points on GitHub&lt;/a&gt; during my Devoxx presentation. Because of my writing and speaking
    about JHipster, I was invited to join the &lt;a href=&quot;https://jhipster.github.io/team.html&quot;&gt;JHipster Team&lt;/a&gt;. I
    happily accepted; it&apos;s been a great group to work with.
&lt;/p&gt;
&lt;p&gt;
    The &lt;a href=&quot;http://www.jhipster-book.com/#!/news/entry/jhipster-mini-book-released&quot;&gt;JHipster Mini-Book 1.0 was
    released on October 30, 2015&lt;/a&gt;. I wrote about &lt;a
    href=&quot;//raibledesigns.com/rd/entry/the_jhipster_mini_book_how&quot;&gt;how I wrote it&lt;/a&gt; a few days later.
&lt;/p&gt;

&lt;h2 id=&quot;personal&quot;&gt;Personal&lt;/h2&gt;
&lt;p&gt;I wrote down my goals at the beginning of 2015: &lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;21-Points Fitness App&lt;/li&gt;
    &lt;li&gt;JHipster Mini Book (InfoQ)&lt;/li&gt;
    &lt;li&gt;Finish Bus&lt;/li&gt;
    &lt;li&gt;New House&lt;/li&gt;
    &lt;li&gt;Good Blood Pressure&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;m proud to say I accomplished three out of five. The Bus didn&apos;t get finished and we failed to sell our house.
    I did create &lt;a href=&quot;http://www.21-points.com/&quot;&gt;21-Points Health&lt;/a&gt;, wrote &lt;a
        href=&quot;http://www.infoq.com/minibooks/jhipster-mini-book&quot;&gt;
        The JHipster Mini-Book&lt;/a&gt; and maintained good health all year.
&lt;/p&gt;

&lt;p&gt;We weren&apos;t planning on selling our house this year; we wanted to find a new house and rent out our current one.
    However, we
    found a house we loved (small house, lots of land) in the heart of DTC and putting our house on the market was the
    best way to try and get it. Our offer failed
    and we endured the house-is-on-the-market dance through July and August. It was pretty sweet living in such a clean
    house, but we got tired of living in a de-personalized abode. We took it off the market in September and plan
    on renting it once we find our next home.
&lt;/p&gt;
&lt;p&gt;The worst part of the year started with a phone call from a friend in the early afternoon on October 7. My good
    friend, Cletus, called to notify me that he&apos;d found our friend, Jason Miller, dead that morning. Jason was a pledge
    class
    brother of Cletus and mine in the Chi Phi Fraternity at DU. You might remember Jason from my
    &lt;a href=&quot;//raibledesigns.com/rd/entry/oktoberfest_best_vacation_ever&quot;&gt;trip to Oktoberfest in 2008&lt;/a&gt;. He was one of my best friends.
&lt;/p&gt;
&lt;p&gt;
    Jason was a heckuva guy and an awesome friend. He always inspired laughter and smiles with his witty humor. He will
    be missed, but not forgotten. Jason had a zest for life and a good time that we&apos;ve resolved to remember annually
    with a
    brother&apos;s trip to an exotic gambling destination. This year, we&apos;ll be traveling to Las Vegas for St. Patty&apos;s Day
    and March Madness.
&lt;/p&gt;
&lt;h3 id=&quot;theskibus&quot;&gt;The Ski Bus&lt;/h3&gt;
&lt;p&gt;
    The Syncro Ski Bus had a pretty good year. We took it on &lt;a
    href=&quot;//raibledesigns.com/rd/entry/farewell_to_the_2014_2015&quot;&gt;many skiing adventures&lt;/a&gt;, with trips to
    Steamboat, Crested Butte, and Telluride.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://c1.staticflickr.com/9/8729/16180350303_c112218136_c.jpg&quot;
       title=&quot;Packed and ready for Crested Butte. by Matt Raible, on Flickr&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/16180350303&quot;&gt;&lt;img
        src=&quot;https://c1.staticflickr.com/9/8729/16180350303_c112218136_q.jpg&quot; width=&quot;150&quot;
        alt=&quot;Packed and ready for Crested Butte.&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm9.staticflickr.com/8613/16799153151_12e8b3d9ea_c.jpg&quot;
       title=&quot;Jack, James, Abbie and myself skiing in heaven! by Matt Raible, on Flickr&quot;
       rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://farm9.staticflickr.com/8613/16799153151_12e8b3d9ea_c.jpg&quot;&gt;&lt;img
        src=&quot;https://farm9.staticflickr.com/8613/16799153151_12e8b3d9ea_q.jpg&quot; width=&quot;150&quot;
        alt=&quot;Jack, James, Abbie and myself skiing in heaven!&quot; style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm9.staticflickr.com/8738/16612686298_1ea3dfc384_c.jpg&quot;
       title=&quot;Yay! Car bombs at Ice bar by Matt Raible, on Flickr&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://farm9.staticflickr.com/8738/16612686298_1ea3dfc384_c.jpg&quot;&gt;&lt;img
        src=&quot;https://farm9.staticflickr.com/8738/16612686298_1ea3dfc384_q.jpg&quot; width=&quot;150&quot;
        alt=&quot;Yay! Car bombs at Ice bar&quot; style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://c4.staticflickr.com/8/7603/16799164872_82944691da_c.jpg&quot;
       title=&quot;We love you Telluride! by Matt Raible, on Flickr&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/16799164872&quot;&gt;&lt;img
        src=&quot;https://c4.staticflickr.com/8/7603/16799164872_82944691da.jpg&quot; width=&quot;500&quot; alt=&quot;We love you Telluride!&quot;
        style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
&lt;p&gt;
    We enjoyed a couple overnights and tailgated a few times at our favorite ski area: Mary Jane.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm9.staticflickr.com/8628/16800248455_de547b4489_c.jpg&quot;
       title=&quot;Ski In, Ski Out at Challenger Lift by Matt Raible, on Flickr&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://farm9.staticflickr.com/8628/16800248455_de547b4489_c.jpg&quot;&gt;&lt;img
        src=&quot;https://farm9.staticflickr.com/8628/16800248455_de547b4489_m.jpg&quot; width=&quot;240&quot;
        alt=&quot;Ski In, Ski Out at Challenger Lift&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm9.staticflickr.com/8628/16180349473_b99397f8f9_c.jpg&quot;
       title=&quot;Tailgating at Mary Jane by Matt Raible, on Flickr&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://farm9.staticflickr.com/8628/16180349473_b99397f8f9_c.jpg&quot;&gt;&lt;img
        src=&quot;https://farm9.staticflickr.com/8628/16180349473_b99397f8f9_m.jpg&quot; width=&quot;240&quot; alt=&quot;Tailgating at Mary Jane&quot;
        style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    I took Jack and we met my parents in Moab in May for &lt;a href=&quot;//raibledesigns.com/rd/entry/syncro_solstice_2015&quot;&gt;Syncro Solstice 2015&lt;/a&gt;. We took it for
    some off-roading, busted a cooling hose a couple times, and almost made it back to Denver.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://c2.staticflickr.com/8/7753/17920503935_5e8c033cb8_c.jpg&quot;
       title=&quot;Syncro Solstice 2015! by Matt Raible, on Flickr&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/17920503935&quot;&gt;&lt;img
        src=&quot;https://c2.staticflickr.com/8/7753/17920503935_5e8c033cb8.jpg&quot; width=&quot;500&quot; alt=&quot;Syncro Solstice 2015!&quot;
        style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    That next weekend, we took it to &lt;a href=&quot;https://www.flickr.com/photos/mcginityphoto/albums/72157654071550305&quot;&gt;VWs
    on the Green&lt;/a&gt; and showed it alongside The (under construction) Bus.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://www.mcginityphoto.com/Portfolio/VWClassic/i-FvTx9Mx/0/L/Raibles%20at%20VW%20on%20the%20Green-L.jpg&quot;
       rel=&quot;lightbox[2015yearinreview]&quot;&gt;
        &lt;img
            src=&quot;http://www.mcginityphoto.com/Portfolio/VWClassic/i-FvTx9Mx/0/M/Raibles%20at%20VW%20on%20the%20Green-M.jpg&quot;
            alt=&quot;Raibles on the VW Green&quot; width=&quot;600&quot; style=&quot;border: 1px solid black&quot;&gt;
    &lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    In July, we journeyed to Montana for the 4th of July. We drove our Westy in the Swan Valley parade while the kids
    rode on top and threw water balloons and candy to the crowd.
    We drove back from Montana in a single day (900 miles). I remember Wyoming well. Sirius XM was broadcasting the last
    Grateful Dead concert from Soldier Field
    and Trish was driving. She sang for hours, driving through the rain and fog while I dozed periodically.
&lt;/p&gt;

&lt;p&gt;Sewfine finished The Bus&apos;s interior in July. I wrote about this and many other things in &lt;a
    href=&quot;//raibledesigns.com/rd/entry/life_update_the_bus_project&quot;&gt;
    Life Update: The Bus Project, New Gigs, New House and More&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/404/19352716089_59ed639749_c.jpg&quot;
       title=&quot;Love the color scheme with chrome accents&quot; rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/19352716089/in/album-72157655642253461/&quot;&gt;&lt;img
        src=&quot;https://farm1.staticflickr.com/404/19352716089_59ed639749_m.jpg&quot; width=&quot;240&quot;
        alt=&quot;Love the color scheme with chrome accents&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm4.staticflickr.com/3786/19543443241_8eeb15dd74_c.jpg&quot; title=&quot;The cockpit&quot;
       rel=&quot;lightbox[2015yearinreview]&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/19543443241/in/album-72157655642253461/&quot;&gt;&lt;img
        src=&quot;https://farm4.staticflickr.com/3786/19543443241_8eeb15dd74_m.jpg&quot; width=&quot;240&quot; alt=&quot;The cockpit&quot;
        style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    A week later, I won a runner-up trophy for the Syncro at &lt;a
    href=&quot;http://www.bandimere.com/tickets-schedule/event-details/2015/07/12/default-calendar/colorado-bug-in-pres-by-nuvintage&quot;&gt;
    The Colorado Bug-In at Bandimere Speedway&lt;/a&gt;. On my birthday,
    July 16, we drove it to Dinosaur National Monument for a week-long raft trip, starting at Gates of Lodore. I hit my
    first deer
    that night, driving at 3am and doing minimal damage to our German-engineered RV. The deer broke the headlight, but
    not the bulb.
&lt;/p&gt;
&lt;p&gt;
    We had some more camping/rafting adventures with it in the fall, but eventually had to take it to the shop for a
    transmission rebuild in November. While it was getting repairs, we upgraded a few things. It now has a South African
    grill, modern headlights, a new fridge and a Propex heater. It&apos;s finally become the ultimate camping vehicle, for
    summer or winter.
&lt;/p&gt;
&lt;h2 id=&quot;2016&quot;&gt;2016&lt;/h2&gt;
&lt;p&gt;Professionally, I plan to continue speaking at conferences this year. I&apos;ve submitted a few proposals to &lt;a
    href=&quot;http://www.devoxx.fr/&quot;&gt;Devoxx France&lt;/a&gt;,
    &lt;a href=&quot;http://www.devoxx.co.uk/&quot;&gt;Devoxx UK&lt;/a&gt; and I&apos;ve agreed to speak at &lt;a href=&quot;http://2016.geekout.ee/&quot;&gt;Geek
        Out&lt;/a&gt;. And did you hear that &lt;a href=&quot;http://springone.com/&quot;&gt;SpringOne 2016&lt;/a&gt; is in Las Vegas?!
&lt;/p&gt;
&lt;p&gt;
    I suspect this will be the year of &lt;a href=&quot;https://angular.io/&quot;&gt;Angular 2&lt;/a&gt; and microservices. Watch &lt;a
    href=&quot;https://github.com/jhipster/jhipster-experimental-microservices&quot;&gt;
    this project&lt;/a&gt; to see how JHipster implements microservices. I plan to pump out a release of AppFuse in the near
    future, implementing #NoXML and moving to Java 8.
&lt;/p&gt;
&lt;p&gt;
    Trish and I hope to move into a new house this year, but won&apos;t be disappointed if we don&apos;t find the right one. We
    have
    another year before Abbie starts high school at 7am.
&lt;/p&gt;&lt;p&gt;
    I have no real big projects planned, except for driving The Bus. We do have a few fun vacations planned.
    In fact,
    I&apos;m writing this while riding in the back of our Westy. My parents and I are road-tripping from Denver to Jekyll
    Island, Georgia.
    We&apos;re taking the scenic route to see some places we&apos;ve never been and staying at RV Parks with the top popped. We
    arrive in
    New Orleans this afternoon and will be staying two nights to enjoy the culture and cuisine.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a  rel=&quot;lightbox[2015yearinreview]&quot; href=&quot;https://farm2.staticflickr.com/1682/24235662332_ef9e21ff8e_c.jpg&quot; data-href=&quot;https://www.flickr.com/photos/mraible/24235662332/&quot;
       title=&quot;The back seat&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1682/24235662332_ef9e21ff8e_m.jpg&quot; width=&quot;240&quot; style=&quot;border: 1px solid black&quot; alt=&quot;The back seat&quot;&gt;&lt;/a&gt;
    &lt;a  rel=&quot;lightbox[2015yearinreview]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/24317694286/&quot; href=&quot;https://farm2.staticflickr.com/1612/24317694286_e05ec2fd4a_c.jpg&quot;
       title=&quot;Somewhere in Arkansas...&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1612/24317694286_e05ec2fd4a_m.jpg&quot;
                                             width=&quot;240&quot; alt=&quot;Somewhere in Arkansas...&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt; My Dad turns 70 this weekend and we planned a family vacation to celebrate. His brother and sister are joining us
    and Trish and the kids are flying in this weekend. We figured a road trip across the country was appropriate since
    we&apos;ve done so many together and I happen to own the ultimate vehicle for car camping. Below is a &lt;a href=&quot;https://goo.gl/maps/9hE5aNkF7k22&quot;&gt;map of our
    route&lt;/a&gt;.&lt;/p&gt;
&lt;div style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;https://www.google.com/maps/embed?pb=!1m58!1m12!1m3!1d13460084.342798809!2d-102.15251344423332!3d34.54967111566971!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!4m43!3e0!4m5!1s0x876b80aa231f17cf%3A0x118ef4f8278a36d6!2sDenver%2C+CO!3m2!1d39.739235799999996!2d-104.990251!4m5!1s0x87cf62f745c8983f%3A0x6bfd6cb31e690da0!2sSpringfield%2C+MO!3m2!1d37.2089572!2d-93.29229889999999!4m5!1s0x87cd2a9388325047%3A0xbaa8bef944021e0d!2sHot+Springs%2C+AR!3m2!1d34.5037004!2d-93.0551795!4m5!1s0x8620a454b2118265%3A0xdb065be85e22d3b4!2sNew+Orleans%2C+LA!3m2!1d29.9510658!2d-90.0715323!4m5!1s0x889381562ac66341%3A0xbf585ed52c4701f3!2sPanama+City%2C+FL!3m2!1d30.158812899999997!2d-85.6602058!4m5!1s0x88e4dbf62542c839%3A0x10d22d63ea360435!2sJekyll+Island%2C+Glynn+County%2C+GA!3m2!1d31.0685662!2d-81.4134297!4m5!1s0x876b80aa231f17cf%3A0x118ef4f8278a36d6!2sDenver%2C+CO!3m2!1d39.739235799999996!2d-104.990251!5e0!3m2!1sen!2sus!4v1452694275660&quot; width=&quot;600&quot; height=&quot;450&quot; frameborder=&quot;0&quot; style=&quot;border:0&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;In June, we&apos;ll be celebrating Trish&apos;s parents 50th wedding anniversary in Kauai. With Trish&apos;s brother, sister and
    children joining in, everyone is sure to have a fantastic vacation. I hope to continue the fun by working remotely
    from Montana in July.
&lt;/p&gt;
&lt;p&gt;When I drive the finished bus in the Swan Valley parade on the 4th of July, I&apos;ll be smiling from ear-to-ear. 2016
    &lt;em&gt;is&lt;/em&gt; the year! The long wait will be over soon...
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/angular_summit_2015</id>
        <title type="html">Angular Summit 2015</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/angular_summit_2015"/>
        <published>2015-10-01T10:29:31-06:00</published>
        <updated>2015-10-08T21:32:22-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="aurelia" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jhipster" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring-boot" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="meteor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="es6" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;
  I was in Boston this week, speaking and attending the very first &lt;a href=&quot;http://angularsummit.com&quot;&gt;Angular Summit&lt;/a&gt;. I had the privilege of delivering the opening keynote on Monday. I spoke about the Art of Angular
  and used a slide deck similar to &lt;a href=&quot;//raibledesigns.com/rd/entry/the_art_of_angularjs_in&quot;&gt;last time&lt;/a&gt;. I did
  update the presentation to show the astronomical growth of AngularJS in terms of candidate skills (on LinkedIn) and job opportunities (on Dice.com)&lt;sup&gt;1&lt;/sup&gt;.
&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;https://farm1.staticflickr.com/691/21198424124_e9b9b37afb_c.jpg&quot; title=&quot;LinkedIn Skills Growth for JavaScript MVC Frameworks&quot; rel=&quot;lightbox[angularsummit2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/21198424124/in/dateposted-public/&quot;&gt;&lt;img src=&quot;https://farm1.staticflickr.com/691/21198424124_e9b9b37afb_c.jpg&quot; width=&quot;300&quot; alt=&quot;LinkedIn Skills Growth for JavaScript MVC Frameworks&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://farm6.staticflickr.com/5808/21633143850_9aef93d361_c.jpg&quot; title=&quot;Dice.com Job Growth for JavaScript MVC Frameworks&quot; rel=&quot;lightbox[angularsummit2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/21633143850/in/dateposted-public/&quot;&gt;&lt;img src=&quot;https://farm6.staticflickr.com/5808/21633143850_9aef93d361_c.jpg&quot; width=&quot;300&quot; alt=&quot;Dice.com Job Growth for JavaScript MVC Frameworks&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I mentioned the recently announced &lt;a href=&quot;http://angularjs.blogspot.com/2015/08/angular-1-and-angular-2-coexistence.html&quot;&gt;good news for Angular 2&lt;/a&gt;:
  &lt;/p&gt;&lt;ul&gt;
  &lt;li&gt;We&apos;re enabling mixing of Angular 1 and Angular 2 in the same application.&lt;/li&gt;
  &lt;li&gt;You can mix Angular 1 and Angular 2 components in the same view.&lt;/li&gt;
  &lt;li&gt;Angular 1 and Angular 2 can inject services across frameworks.&lt;/li&gt;
  &lt;li&gt;Data binding works across frameworks.&lt;/li&gt;
  &lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In related news, &lt;a href=&quot;https://twitter.com/cdoremus&quot;&gt;Craig Doremus&lt;/a&gt; recently posted a &lt;a href=&quot;https://github.com/cdoremus/state-geo-angular&quot;&gt;state-geo-angular&lt;/a&gt; project
  that shows how you can develop an Angular 1.x application that will be easy to upgrade to Angular 2.x.
  Thanks Craig!
&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/vaTKWLA8oVDr8z&quot; width=&quot;600&quot; height=&quot;377&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen=&quot;&quot;&gt; &lt;/iframe&gt;
&lt;/p&gt;

&lt;div style=&quot;text-align: right; max-width: 600px; margin: -20px auto 10px auto&quot;&gt;
    &lt;a href=&quot;//raibledesigns.com/rd/page/publications&quot;&gt;Download&lt;/a&gt; | &lt;a href=&quot;//www.slideshare.net/mraible/the-art-of-angularjs-in-2015-angular-summit-2015&quot;&gt;SlideShare&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;After my keynote, I attended &lt;a href=&quot;https://twitter.com/prpatel&quot;&gt;Pratik Patel&lt;/a&gt;&apos;s session on &lt;a href=&quot;https://angularsummit.com/conference/boston/2015/09/session?id=34208&quot;&gt;High Performance JavaScript Web Apps&lt;/a&gt;.
  Pratik pointed out &lt;a href=&quot;http://mobitest.akamai.com&quot;&gt;mobitest.akamai.com&lt;/a&gt; for testing an app&apos;s performance and seeing its blocking resources. He also mentioned
  &lt;a href=&quot;http://speedgun.io/&quot;&gt;speedgun.io&lt;/a&gt; (currently unavailable) for capturing performance numbers as part of a continuous integration process. Finally,
  he recommended &lt;a href=&quot;http://addyosmani.com/blog/video-javascript-memory-management-masterclass/&quot;&gt;Addy Somani&apos;s JavaScript Memory Management Masterclass&lt;/a&gt;.
&lt;p&gt;
My second presentation was about &lt;a href=&quot;http://jhipster.github.io/&quot;&gt;JHipster&lt;/a&gt;. Near the end of the presentation,
I mentioned that I hope to finish the &lt;a href=&quot;http://www.jhipster-book.com/&quot;&gt;JHipster Book&lt;/a&gt; this month. Writing presentations for
&lt;a href=&quot;//raibledesigns.com/rd/entry/springone_2gx_2015_my_presentations&quot;&gt;SpringOne 2GX&lt;/a&gt; and the Angular Summit occupied a lot of my free time in September. Now that it&apos;s October, I&apos;ll be dedicating my free time to finishing the book. In fact, I think I can finish the rough draft this week!
&lt;/p&gt;&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/769Ne9avDiEeWl&quot; width=&quot;600&quot; height=&quot;377&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen=&quot;&quot;&gt; &lt;/iframe&gt;
&lt;/p&gt;

&lt;div style=&quot;text-align: right; max-width: 600px; margin: -20px auto 10px auto&quot;&gt;
&lt;a href=&quot;//raibledesigns.com/rd/page/publications&quot;&gt;Download&lt;/a&gt; | &lt;a href=&quot;//www.slideshare.net/mraible/get-hip-with-jhipster-spring-boot-angularjs-bootstrap-angular-summit-2015&quot;&gt;SlideShare&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;
  For the last session of the day, I attended &lt;a href=&quot;https://twitter.com/johnlindquist&quot;&gt;John Lindquist&apos;s&lt;/a&gt; session on &lt;a href=&quot;http://angularsummit.com/conference/boston/2015/09/session?id=34230&quot;&gt;Angular 2 Components&lt;/a&gt;. John showed us
  how &lt;em&gt;everything is a component in Angular 2&lt;/em&gt;. He also said &quot;now is the time to learn ES6&quot; and built an
  &lt;a href=&quot;https://github.com/johnlindquist/angular-2-quickstart&quot;&gt;Angular 2 ToDo App&lt;/a&gt; using ES6 and a bit of TypeScript. You might recognize John&apos;s name; he&apos;s the founder of &lt;a href=&quot;http://egghead.io/&quot;&gt;egghead.io&lt;/a&gt;, an excellent
  site for &lt;a href=&quot;https://egghead.io/playlists/new-to-angular-start-here&quot;&gt;learning Angular&lt;/a&gt; with bite-sized videos.
&lt;/p&gt;
&lt;p&gt;Tuesday morning started with a &lt;a href=&quot;http://angularsummit.com/conference/boston/2015/09/session?id=34187&quot;&gt;
  Angular 2.0 keynote&lt;/a&gt; from &lt;a href=&quot;https://twitter.com/ppavlovich&quot;&gt;Peter Pavlovich&lt;/a&gt;. I really enjoyed
  this session and received lots of good tips about getting ready for Angular 2. The tweet below from
  Ksenia Dmitrieva shows his advice.
&lt;/p&gt;
&lt;div style=&quot;margin: 0 auto; max-width: 500px;&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Best Practices for &lt;a href=&quot;https://twitter.com/hashtag/angularjs?src=hash&quot;&gt;#angularjs&lt;/a&gt; 1.X if you plan to switch to 2.0 by &lt;a href=&quot;https://twitter.com/ppavlovich&quot;&gt;@ppavlovich&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/AngularSummit?src=hash&quot;&gt;#AngularSummit&lt;/a&gt; &lt;a href=&quot;http://t.co/9nobqDc9G9&quot;&gt;pic.twitter.com/9nobqDc9G9&lt;/a&gt;&lt;/p&gt;&amp;mdash; Ksenia Dmitrieva (@KseniaDmitrieva) &lt;a href=&quot;https://twitter.com/KseniaDmitrieva/status/648865784152915968&quot;&gt;September 29, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;p&gt;My biggest takeaway was to start following &lt;a href=&quot;https://github.com/johnpapa/angular-styleguide&quot;&gt;John Papa&apos;s Angular Style Guide&lt;/a&gt; &lt;abbr title=&quot;As Soon As Possible&quot;&gt;ASAP&lt;/abbr&gt;.
&lt;p&gt;The first session I attended on Tuesday was &lt;a href=&quot;https://twitter.com/JuddFlamm&quot;&gt;Judd Flamm&lt;/a&gt;&apos;s &lt;a href=&quot;https://angularsummit.com/conference/boston/2015/09/session?id=34298&quot;&gt;Google Material Design &amp;amp; Angular&lt;/a&gt;.
  I&apos;m using &lt;a href=&quot;https://fezvrasta.github.io/bootstrap-material-design/&quot;&gt;Material Design for Bootstrap&lt;/a&gt; on a side project, so I was interested in learning more about its inspiration.
  We learned that &lt;a href=&quot;https://design.google.com/&quot;&gt;Google Design&lt;/a&gt; has everything you need to know about why Material Design exists. We also
  learned about &lt;a href=&quot;https://material.angularjs.org&quot;&gt;Angular Material&lt;/a&gt; and spent most of the session looking at its components. Judd
  recommended &lt;a href=&quot;https://github.com/angular/material-start&quot;&gt;Angular Material-Start&lt;/a&gt; for those looking to get started quickly with both frameworks.
  Judd was a very entertaining speaker; I highly recommend you attend one of his talks if you get the opportunity.
&lt;/p&gt;
&lt;p&gt;After being dazzled by Peter&apos;s knowledge of Angular 2 in Tuesday&apos;s keynote, I attended two more of his talks: one on &lt;a href=&quot;https://www.meteor.com/&quot;&gt;Meteor&lt;/a&gt; and
  another on &lt;a href=&quot;http://aurelia.io/&quot;&gt;Aurelia&lt;/a&gt;. I&apos;ve known about Meteor for a while, but have become more intrigued by it lately with its
  &lt;a href=&quot;http://www.infoq.com/news/2015/09/meteor-12-ecmascript&quot;&gt;1.2 release&lt;/a&gt; and &lt;a href=&quot;http://info.meteor.com/blog/official-angular-support-with-angular-meteor-1.0.0&quot;&gt;Angular support&lt;/a&gt;. Meteor&apos;s
  command line tools that auto-inject CSS and JS demoed very well, as did it&apos;s installable features like a LESS support and Facebook authentication.
&lt;/p&gt;
&lt;p&gt;After hearing all the good things about Angular 2 from Peter, it was interesting to hear him downplay it in his Aurelia talk later that day. When he started showing code,
  it was pretty obvious that Aurelia is doing a great job of simplifying JavaScript MVC syntax for developers. You can develop components with almost half the
    code that Angular 2 requires, and it uses ES6, &lt;a href=&quot;http://jspm.io/&quot;&gt;jspm&lt;/a&gt; and &lt;a href=&quot;https://github.com/systemjs/systemjs&quot;&gt;SystemJS&lt;/a&gt;.
    If you&apos;re developing JavaScript, learning these tools will help prepare you for the future. It&apos;s cool that Aurelia encourages learning things you should learn anyway.
  &lt;/p&gt;
  &lt;p&gt;Aurelia and Angular 2 are both still in Alpha, so I&apos;m not sure it makes sense to use them on a project this year. However, I do think it&apos;s important to track
    them both. I especially think it&apos;s interesting that the founder of Aurelia, &lt;a href=&quot;http://twitter.com/EisenbergEffect&quot;&gt;Rob Eisenberg&lt;/a&gt;,
    &lt;a href=&quot;http://eisenbergeffect.bluespire.com/leaving-angular/&quot;&gt;left the Angular Team&lt;/a&gt; in November 2014 and &lt;a href=&quot;http://blog.durandal.io/2015/01/26/introducing-aurelia/&quot;&gt;
      announced Aurelia&lt;/a&gt; in January 2015 (&lt;a href=&quot;https://news.ycombinator.com/item?id=8948665&quot;&gt;Hacker News thread&lt;/a&gt;). Peter mentioned several times that Aurelia wants to help developers write apps,
      while AngularJS is more tied to helping Google write apps.
  &lt;/p&gt;
&lt;p&gt;
  There were around 400 people at Angular Summit, which I think is pretty good for a first-run conference. As with most No Fluff Just Stuff shows, it ran smoothly, had
    plenty of time between sessions and was filled with knowledgeable, entertaining speakers. It was fun doing my first keynote and I look forward to speaking again in November
    (at &lt;a href=&quot;http://www.devoxx.be/&quot;&gt;Devoxx&lt;/a&gt;) and December (at
    &lt;a href=&quot;http://www.therichwebexperience.com/conference/fort_lauderdale/2015/12/home&quot;&gt;The Rich Web Experience&lt;/a&gt;).
  &lt;/head&gt;
&lt;/p&gt;
&lt;p style=&quot;font-size: .9em&quot;&gt;1. I know Dice.com is probably not a great site, but it makes sense to use it since I&apos;ve
been tracking JavaScript MVC framework job stats on it since February 2014.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/how_do_i_become_a</id>
        <title type="html">How do I become a programmer?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/how_do_i_become_a"/>
        <published>2015-08-13T08:32:43-06:00</published>
        <updated>2015-08-13T14:37:48-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="learning" scheme="http://roller.apache.org/ns/tags/" />
        <category term="computerscience" scheme="http://roller.apache.org/ns/tags/" />
        <category term="job" scheme="http://roller.apache.org/ns/tags/" />
        <category term="programmer" scheme="http://roller.apache.org/ns/tags/" />
        <category term="computers" scheme="http://roller.apache.org/ns/tags/" />
        <category term="career" scheme="http://roller.apache.org/ns/tags/" />
        <category term="developer" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;Yesterday, I received a message from a friend, asking about how to become a programmer. It&apos;s not the first time I&apos;ve been asked this. In fact, this summer I&apos;ve been asked by several friends how to get into the field. It seems that as people grow older, they see the lifestyle of working remotely and enjoying their job as an attractive thing to do. In yesterday&apos;s case, this friend is a mom that now has her days free because all her kids are in school. Here&apos;s what she wrote:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Now that my girls are both in school full day, I&apos;ve been thinking about taking some programming classes. It&apos;s something I started to do while I was working at [ABC Company], but obviously didn&apos;t pursue once I quit to have kids. I&apos;m thinking of getting my MIS in web development or specializing in designing apps if that&apos;s even a thing? Anyway, what languages would you recommend I concentrate on? JavaScript, Python? Lastly, is there a particular school you would recommend? I can&apos;t afford DU on my stay-at-home-mom salary, or even Regis which is where I started when I was getting tuition reimbursement. I was hoping I could do most of my education online while the kids are in school? Any advice or words of wisdom would be greatly appreciated! &lt;/p&gt;
&lt;p&gt;Since this is a common question I see, I figured I&apos;d publish my answers here, and get some advice from y&apos;all too. Here&apos;s my response:&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;
Python would definitely be good, as would JavaScript. JavaScript can be done on the client and server these days, so you could do that and be able to do front-end and backend development. 
&lt;/p&gt;
&lt;p&gt;
For programming specifically, I&apos;ve heard these guys have a good JavaScript course: &lt;a href=&quot;https://www.codecademy.com/&quot;&gt;https://www.codecademy.com&lt;/a&gt;. Here&apos;s how to get started with Python in eight weeks: &lt;a href=&quot;http://lifehacker.com/how-i-taught-myself-to-code-in-eight-weeks-511615189&quot;&gt;http://lifehacker.com/how-i-taught-myself-to-code-in-eight-weeks-511615189&lt;/a&gt;. And one of my favorites: &lt;a href=&quot;http://programming-motherfucker.com/become.html&quot;&gt;http://programming-motherfucker.com/become.html&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I&apos;ve taken a Scala course from Coursera, it was hard and intense, but I learned a lot. They have lots of courses and give you certifications you can put on your LinkedIn profile: &lt;a href=&quot;https://www.coursera.org/&quot;&gt;https://www.coursera.org&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I&apos;ve also recommended &lt;a href=&quot;https://teamtreehouse.com&quot;&gt;https://teamtreehouse.com&lt;/a&gt; to folks and &lt;a href=&quot;https://www.khanacademy.org/&quot;&gt;https://www.khanacademy.org&lt;/a&gt; has always been good, even for kids.&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;
Ultimately, the best way to learn to code is by doing. It&apos;s definitely good to study, learn and practice, but it&apos;ll probably won&apos;t sink in and become real knowledge until you&apos;re getting paid to do it. With the plethora of high-priced programmers out there, you can likely find a junior position, show a willingness to learn and come up to speed quickly. If you can couple that with a remote position, I think you&apos;ll really enjoy yourself.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Her response was interesting, as she thought she might need a &lt;abbr title=&quot;Computer Science&quot;&gt;CS&lt;/abbr&gt; degree to even get a programming job.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Coincidentally I looked over many of these coding sites yesterday but wasn&apos;t sure if I needed an accredited diploma. It sounds like it&apos;s more important that I just get some experience.&lt;/p&gt;
&lt;p&gt;From my experience, a college degree matters, but not a CS degree. I told her people skills make programmers stand out and she&apos;s a witty person that certainly has those. What&apos;s your advice as a programmer? What would you tell people to do if they want to break into the field?&lt;/p&gt;
&lt;p&gt;More importantly, if you&apos;re on the hiring side, what would it take for you to hire a 40-something person with no programming background? If they&apos;ve been studying for six months and have really good people skills, would you hire them for a junior position?&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/life_update_the_bus_project</id>
        <title type="html">Life Update: The Bus Project, New Gigs, New House and More</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/life_update_the_bus_project"/>
        <published>2015-07-08T22:14:56-06:00</published>
        <updated>2016-01-30T19:25:38-07:00</updated> 
        <category term="/The Bus" label="The Bus" />
        <category term="roadtrip" scheme="http://roller.apache.org/ns/tags/" />
        <category term="life" scheme="http://roller.apache.org/ns/tags/" />
        <category term="update" scheme="http://roller.apache.org/ns/tags/" />
        <category term="busproject" scheme="http://roller.apache.org/ns/tags/" />
        <category term="montana" scheme="http://roller.apache.org/ns/tags/" />
        <category term="vwbus" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rafting" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
    I&apos;ve &lt;a href=&quot;//raibledesigns.com/rd/entry/life_update&quot;&gt;written&lt;/a&gt; &lt;a href=&quot;//raibledesigns.com/rd/entry/life_update_new_treehouse_new&quot;&gt;a&lt;/a&gt; &lt;a href=&quot;//raibledesigns.com/rd/entry/life_update1&quot;&gt;few&lt;/a&gt; Life Update blog posts in the past and it seems
    appropriate to write another one today. A lot has
    happened since I wrote about our trip to &lt;a href=&quot;//raibledesigns.com/rd/entry/syncro_solstice_2015&quot;&gt;Syncro
    Solstice 2015 in Moab&lt;/a&gt;. First of all, let&apos;s talk about the most exciting one: The Bus Project.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bus Project&lt;/strong&gt;&lt;br&gt;
    The last time I wrote about The Bus, it&apos;d just &lt;a href=&quot;//raibledesigns.com/rd/entry/the_bus_arrives_at_sewfine&quot;&gt;arrived
        at Sewfine&lt;/a&gt; to have the interior installed. From the get-go, I knew this was going to be a good experience.
    I&apos;ve been talking with the owners (Carol and Mike) for years about the project. Seeing the knowledge they had about
    VWs and knowing it was in good hands brought a sense of calmness over me. They estimated it&apos;d take 4-8 weeks to
    finish and it ended up taking 12. I&apos;m proud to say it left Sewfine yesterday with a completed interior.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/404/19352716089_59ed639749_c.jpg&quot; title=&quot;Love the color scheme with chrome accents&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/19352716089/in/album-72157655642253461/&quot;&gt;&lt;img src=&quot;https://farm1.staticflickr.com/404/19352716089_59ed639749_m.jpg&quot; width=&quot;240&quot; alt=&quot;Love the color scheme with chrome accents&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm4.staticflickr.com/3786/19543443241_8eeb15dd74_c.jpg&quot; title=&quot;The cockpit&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/19543443241/in/album-72157655642253461/&quot;&gt;&lt;img src=&quot;https://farm4.staticflickr.com/3786/19543443241_8eeb15dd74_m.jpg&quot; width=&quot;240&quot; alt=&quot;The cockpit&quot; style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; text-align: right; margin-top: -10px; max-width: 500px&quot;&gt;
    &lt;a href=&quot;https://www.flickr.com/photos/mraible/sets/72157655642253461&quot;&gt;The Bus Interior on Flickr &amp;rarr;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;In mid-May,
    we took The Bus to its first show: &lt;a href=&quot;http://vwgreenshow.com/&quot;&gt;VWs on the Green&lt;/a&gt; in Littleton. Sewfine
    had completed
    the driver&apos;s seat and ragtop. I got license plates and insurance and was planning on driving it to the show.
    However,
    Mike pointed out that the engine compartment wasn&apos;t sealed and the engine might get really hot on the 10-mile drive
    (because it&apos;s an air-cooled engine). I agreed to trailer it instead and rented a car hauler from U-Haul.
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
    I&apos;ve &lt;a href=&quot;//raibledesigns.com/rd/entry/life_update&quot;&gt;written&lt;/a&gt; &lt;a href=&quot;//raibledesigns.com/rd/entry/life_update_new_treehouse_new&quot;&gt;a&lt;/a&gt; &lt;a href=&quot;//raibledesigns.com/rd/entry/life_update1&quot;&gt;few&lt;/a&gt; Life Update blog posts in the past and it seems
    appropriate to write another one today. A lot has
    happened since I wrote about our trip to &lt;a href=&quot;//raibledesigns.com/rd/entry/syncro_solstice_2015&quot;&gt;Syncro
    Solstice 2015 in Moab&lt;/a&gt;. First of all, let&apos;s talk about the most exciting one: The Bus Project.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bus Project&lt;/strong&gt;&lt;br&gt;
    The last time I wrote about The Bus, it&apos;d just &lt;a href=&quot;//raibledesigns.com/rd/entry/the_bus_arrives_at_sewfine&quot;&gt;arrived
        at Sewfine&lt;/a&gt; to have the interior installed. From the get-go, I knew this was going to be a good experience.
    I&apos;ve been talking with the owners (Carol and Mike) for years about the project. Seeing the knowledge they had about
    VWs and knowing it was in good hands brought a sense of calmness over me. They estimated it&apos;d take 4-8 weeks to
    finish and it ended up taking 12. I&apos;m proud to say it left Sewfine yesterday with a completed interior.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/404/19352716089_59ed639749_c.jpg&quot; title=&quot;Love the color scheme with chrome accents&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/19352716089/in/album-72157655642253461/&quot;&gt;&lt;img src=&quot;https://farm1.staticflickr.com/404/19352716089_59ed639749_m.jpg&quot; width=&quot;240&quot; alt=&quot;Love the color scheme with chrome accents&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm4.staticflickr.com/3786/19543443241_8eeb15dd74_c.jpg&quot; title=&quot;The cockpit&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/19543443241/in/album-72157655642253461/&quot;&gt;&lt;img src=&quot;https://farm4.staticflickr.com/3786/19543443241_8eeb15dd74_m.jpg&quot; width=&quot;240&quot; alt=&quot;The cockpit&quot; style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; text-align: right; margin-top: -10px; max-width: 500px&quot;&gt;
    &lt;a href=&quot;https://www.flickr.com/photos/mraible/sets/72157655642253461&quot;&gt;The Bus Interior on Flickr &amp;rarr;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;In mid-May,
    we took The Bus to its first show: &lt;a href=&quot;http://vwgreenshow.com/&quot;&gt;VWs on the Green&lt;/a&gt; in Littleton. Sewfine
    had completed
    the driver&apos;s seat and ragtop. I got license plates and insurance and was planning on driving it to the show.
    However,
    Mike pointed out that the engine compartment wasn&apos;t sealed and the engine might get really hot on the 10-mile drive
    (because it&apos;s an air-cooled engine). I agreed to trailer it instead and rented a car hauler from U-Haul.
&lt;/p&gt;
&lt;p&gt;
    While driving it onto the trailer, and onto the grass at the show, I discovered several issues that needed fixed
    before I&apos;d consider it road-worthy. First of all, the shifting was very sloppy and it was hard to find first gear.
    Third gear was easy to find and I ended up using that a few times, much to the chagrin of the clutch. I also found
    the air-bag suspension to extremely stiff. So stiff it seemed dangerous to drive over 20mph. Then there was the oil
    leak that left a trail behind us.
&lt;/p&gt;
&lt;p&gt;The good news is &lt;a href=&quot;//raibledesigns.com/rd/entry/the_bus_is_painted_holy&quot;&gt;the guys who painted it&lt;/a&gt; have agreed to try and fix these issues and it&apos;s back in their good hands
    today. I don&apos;t know when it&apos;ll be done, but I&apos;ve given up on trying to establish deadlines; especially since the
    largest VW show in Colorado is this weekend and it won&apos;t be done by then.&lt;/p&gt;
&lt;p&gt;
    Below are some photos we took at the VWs on the Green Show.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/515/17856445423_a9e6c2e9a2_c.jpg&quot; title=&quot;The Dream Team&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mcginityphoto/17856445423/in/album-72157654071550305/&quot;&gt;&lt;img src=&quot;https://farm1.staticflickr.com/515/17856445423_a9e6c2e9a2.jpg&quot; width=&quot;500&quot; alt=&quot;The Dream Team&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/521/18450736286_1b47070b36_c.jpg&quot; title=&quot;Happy Daze&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mcginityphoto/18450736286/in/album-72157654071550305/&quot;&gt;&lt;img src=&quot;https://farm1.staticflickr.com/521/18450736286_1b47070b36.jpg&quot; width=&quot;500&quot; alt=&quot;Happy Daze&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/327/18290996479_db85b1a989_c.jpg&quot; title=&quot;Thing 1&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mcginityphoto/18290996479/in/album-72157654071550305/&quot;&gt;&lt;img src=&quot;https://farm1.staticflickr.com/327/18290996479_db85b1a989_m.jpg&quot; width=&quot;240&quot; alt=&quot;Thing 1&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/399/17856493663_1c3c728666_c.jpg&quot; title=&quot;Thing 2&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mcginityphoto/17856493663/in/album-72157654071550305/&quot;&gt;&lt;img src=&quot;https://farm1.staticflickr.com/399/17856493663_1c3c728666_m.jpg&quot; width=&quot;240&quot; alt=&quot;Thing 2&quot; style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm8.staticflickr.com/7741/18198846995_9dbf8b2fee_c.jpg&quot; title=&quot;Empty Interior&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/18198846995/in/album-72157651268032504/&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7741/18198846995_9dbf8b2fee_m.jpg&quot; width=&quot;240&quot; alt=&quot;Empty Interior&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm8.staticflickr.com/7757/18012627889_b999254673_c.jpg&quot; title=&quot;Under Construction&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/18012627889/in/album-72157651268032504/&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7757/18012627889_b999254673_m.jpg&quot; width=&quot;240&quot; alt=&quot;Under Construction&quot; style=&quot;border: 1px solid black; margin-left: 15px;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm1.staticflickr.com/399/18288802680_4c48d1211f_c.jpg&quot; title=&quot;66 VW Bus Front view on the Green&quot; rel=&quot;lightbox[lifeupdate4]&quot; data-href=&quot;https://www.flickr.com/photos/mcginityphoto/18288802680/in/album-72157654071550305/&quot;&gt;&lt;img src=&quot;https://farm1.staticflickr.com/399/18288802680_4c48d1211f_c.jpg&quot; width=&quot;530&quot; alt=&quot;66 VW Bus Front view on the Green&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;For more pictures, see Trish&apos;s &lt;a href=&quot;https://www.flickr.com/photos/mcginityphoto/sets/72157654071550305/&quot;&gt;VWs on
    the Green 2015&lt;/a&gt; album on Flickr. I also &lt;a href=&quot;https://www.flickr.com/photos/mraible/sets/72157651268032504&quot;&gt;published mine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New Gigs&lt;/strong&gt;&lt;br&gt;
    I started a new gig in March, slinging code for a fashion-industry startup in downtown Denver. The team is comprised
    of guys I&apos;ve known for years and I&apos;ve had a really great time. The project is led by &lt;a href=&quot;https://www.linkedin.com/in/jgoodwill&quot;&gt;James Goodwill&lt;/a&gt;, who I worked
    with at Virtuas, Overstock and Time Warner cable with. It&apos;s good to be working with him again. My good friend, The
    &apos;Fesser, just joined our team last week too! I&apos;m writing JavaScript, HTML5 and CSS and we&apos;re using AngularJS and Bootstrap as our UI frameworks. There&apos;s also  opportunities to do
    Python and iOS development. James and another friend/co-worker, &lt;a href=&quot;https://www.linkedin.com/pub/wesley-matlock/0/67b/116&quot;&gt;Wes Matlock&lt;/a&gt;, recently published &lt;a href=&quot;http://www.amazon.com/Beginning-Swift-Games-Development-iOS/dp/1484204018&quot;&gt;Beginning Swift Games
        Development for iOS&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;Trish started a new gig with &lt;a href=&quot;http://www.tuliva.com/&quot;&gt;Tuliva&lt;/a&gt; this week and now has
    an office a block from mine (at &lt;a href=&quot;http://ballpark.thriveworkplace.com/en&quot;&gt;Thrive Ballpark&lt;/a&gt;). Thrive&apos;s new
    space is awesome and I love that they have coffee, beer and kambucha on tap! I&apos;ve been riding my bike to work (6
    miles each way) most of the year. My &lt;a href=&quot;http://www.styleselect.com/&quot;&gt;client&lt;/a&gt; has an office five blocks from
    mine, so I tend to spend half my time there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The JHipster Mini-Book&lt;/strong&gt;&lt;br&gt;
    I wrote about my &lt;a href=&quot;http://www.jhipster-book.com/#!/news/entry/book-progress-update&quot;&gt;progress on the JHipster
        book&lt;/a&gt; at the end of June. Now that it&apos;s July, I&apos;m working on &lt;a href=&quot;http://uberconf.com/conference/denver/2015/07/session?id=33782&quot;&gt;Apache Camel&lt;/a&gt; and
    &lt;a href=&quot;http://uberconf.com/conference/denver/2015/07/session?id=33746&quot;&gt;Java Web Security&lt;/a&gt; presentations
    for &lt;a href=&quot;http://uberconf.com/conference/denver/2015/07/home&quot;&gt;UberConf&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Selling our House&lt;/strong&gt;&lt;br&gt;
    Last month, we found a house we loved near our kids&apos; schools. It was on an acre of land in a prime part of DTC (near
    Cherry Creek High School). To make an offer, we had to put our house &lt;a href=&quot;http://www.mlsfinder.com/co_metrolist/cherrycreek/index.cfm?action=listing_detail&amp;amp;property_id=7849000&amp;amp;searchkey=1cca13cd-f465-a525-830b-a7daeafff022&quot;&gt;on the market&lt;/a&gt;. Unfortunately, our offer wasn&apos;t
    accepted, but now we&apos;re on the market and hope to move south soon. It&apos;s been an interesting experience polishing up our
    house and keeping it clean, especially in the midst of the torrential rain we&apos;ve had lately.
&lt;/p&gt;
&lt;p id=&quot;summer-vacations&quot;&gt;&lt;strong&gt;Summer Vacations&lt;/strong&gt;&lt;br&gt;
    Last week, we drove our Syncro to my &lt;a href=&quot;//raibledesigns.com/rd/entry/the_house&quot;&gt;parent&apos;s house&lt;/a&gt; in Montana.
    Trish took a gorgeous photo of the sunset in Wyoming on the way up.
&lt;/p&gt;
&lt;div style=&quot;text-align: center&quot;&gt;
    &lt;div class=&quot;fb-post&quot; data-href=&quot;https://www.facebook.com/photo.php?fbid=10152865676856712&amp;amp;set=a.10150292995926712.331409.571296711&amp;amp;type=1&quot; data-width=&quot;500&quot; style=&quot;text-align: center&quot;&gt;
        &lt;div class=&quot;fb-xfbml-parse-ignore&quot;&gt;
            &lt;blockquote cite=&quot;https://www.facebook.com/photo.php?fbid=10152865676856712&amp;amp;set=a.10150292995926712.331409.571296711&amp;amp;type=1&quot;&gt;
                &lt;p&gt;A beautiful Wyoming sunset on our way to Montana in our Syncro Westy. Photo by the amazing Trish
                    McGinity.&lt;/p&gt;Posted by &lt;a href=&quot;#&quot; role=&quot;button&quot;&gt;Matt Raible&lt;/a&gt; on&amp;nbsp;&lt;a href=&quot;https://www.facebook.com/photo.php?fbid=10152865676856712&amp;amp;set=a.10150292995926712.331409.571296711&amp;amp;type=1&quot;&gt;Thursday,
                July 2, 2015&lt;/a&gt;&lt;/blockquote&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
    We had an amazing week with my parents, my sister and her wife, Mya, and many old friends. My uncle was in town for
    the first few days,
    so we played golf, hiked in Glacier Park, and spent quality time together. The kids had a great time throwing water
    balloons from the Syncro in the Swan Valley Parade and we admired the beauty of the
    &lt;a href=&quot;http://www.swanvalleyhistoricalsociety.org/contact-us.html&quot;&gt;Swan Valley Museum&lt;/a&gt;. We fished, swam,
    boated, picked huckleberries and &lt;a href=&quot;https://instagram.com/p/4pkWq8FPJx/&quot;&gt;played guitar on the front porch&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;We drove back to Denver on the 5th of July in 16 hours. We stopped for an hour in Billings, and had good enough
    timing to watch the USA Women&apos;s soccer team win it all!
    Driving through the dark rain and fog in Wyoming wasn&apos;t fun, but we arrived safe and sound at 3am. We were very
    impressed with the Syncro&apos;s performance.
&lt;/p&gt;
&lt;p&gt;We have a raft trip scheduled next week, with the same friends &lt;a href=&quot;//raibledesigns.com/rd/entry/rafting_the_yampa_through_dinosaur&quot;&gt;from last year&lt;/a&gt;. This time, we&apos;re rafting
    the Green
    instead of the Yampa, starting at the &lt;a href=&quot;http://www.nps.gov/dino/planyourvisit/gatesoflodore.htm&quot;&gt;Gates of
        Lodore&lt;/a&gt;. I love rafting and it&apos;s really fun to take my family on multi-day trips with epic people. I feel very fortunate. When
    the bus is done, I just might melt into a puddle of glee. &lt;img src=&quot;https://raibledesigns.com/images/smileys/smile.gif&quot; class=&quot;smiley&quot; alt=&quot;:)&quot; title=&quot;:)&quot; /&gt;&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/best_practices_for_using_foundation1</id>
        <title type="html">Best Practices for using Foundation with AngularJS Revisited</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/best_practices_for_using_foundation1"/>
        <published>2015-02-19T09:38:42-07:00</published>
        <updated>2015-02-19T15:49:23-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="foundation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="foundationforapps" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;
&lt;a href=&quot;http://foundation.zurb.com&quot;&gt;&lt;img src=&quot;//raibledesigns.com/repository/images/angular-foundation.png&quot; width=&quot;200&quot; class=&quot;picture&quot; alt=&quot;Angular Foundation&quot; style=&quot;margin-top: -12px&quot;&gt;&lt;/a&gt;
A couple weeks ago I wrote about &lt;a href=&quot;//raibledesigns.com/rd/entry/best_practices_for_using_foundation&quot;&gt;using Foundation with AngularJS&lt;/a&gt;. Based on research I&apos;d done, I concluded that it was best to use &lt;a href=&quot;http://foundation.zurb.com/apps/&quot;&gt;Foundation for Apps&lt;/a&gt; for any webapps my client created and &lt;a href=&quot;http://foundation.zurb.com/&quot;&gt;Foundation for Sites&lt;/a&gt; for any websites (e.g. a WordPress-based intranet).&lt;/p&gt;
&lt;p&gt;After doing my initial research, I did some prototyping with Foundation for Apps (F4A). What I discovered is that F4A does &lt;em&gt;not&lt;/em&gt; include all the same components as Foundation for Sites (F5). For example, the top-bar and dropdown functionality are missing. I &lt;a href=&quot;http://foundation.zurb.com/forum/posts/22587-dropdowns-in-foundation-for-apps&quot;&gt;posted my issues to the Foundation Forums&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The response I received:&lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&gt;
It should work. You would need to copy over all the Scss and global mixins that you used in top-bar or at least all the output CSS from it. Otherwise there is no reason the components won&apos;t fit into the grid.&lt;/blockquote&gt;
&lt;p&gt;I was able to import Foundation for Sites into my project by adding it to bower.json:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;  &quot;dependencies&quot;: {
    &quot;foundation-apps&quot;: &quot;~1.0.2&quot;,
    &quot;foundation&quot;: &quot;~5.5.1&quot;
  }
&lt;/pre&gt;
&lt;p&gt;After doing this, I added the new path to Gulpfile.js:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;var sassPaths = [
  &apos;client/assets/scss&apos;,
  &apos;bower_components/foundation/scss&apos;,
  &apos;bower_components/foundation-apps/scss&apos;
];
&lt;/pre&gt;
&lt;p&gt;
After making this change, the top-bar rendered and my dropdowns worked. Since there was no jQuery in the page, I thought this might be a viable option. However, &lt;a href=&quot;http://foundation.zurb.com/forum/12220-jason-demitri&quot;&gt;Jason Demitri&lt;/a&gt; quickly pointed out it probably wouldn&apos;t work with mobile. He was right.&lt;/p&gt;
&lt;p&gt;While using F4A, I noticed that its components, and much of its look-n-feel, was different than F5. If you look at its &lt;a href=&quot;http://foundation.zurb.com/apps/app-templates/email/#!/&quot;&gt;Email App template&lt;/a&gt;, you&apos;ll see it looks kinda like a mobile app, even in a desktop browser. After trying F4A myself, I decided that F4A wasn&apos;t for us.  First of all, it doesn&apos;t seem to provide a consistent look and feel with a website that&apos;s written using F5. Furthermore, F4A only supports IE10+. In the healthcare industry, there&apos;s a lot of older browsers out there, so my client needs to support IE9 as a minimum.&lt;/p&gt;
&lt;p&gt;For these reasons, I decided to try &lt;a href=&quot;http://pineconellc.github.io/angular-foundation/&quot;&gt;Angular directives for Foundation&lt;/a&gt;. I took a prototype I&apos;d written with F5, removed its JavaScript, added Angular Foundation + Foundation dependencies to bower.json, added references to the respective scripts in index.html and added &apos;mm.foundation&apos; as a dependency in app.js. The experiment worked beautifully and I was quite happy with the results. I shared my findings with the team and we decided &lt;strong&gt;Angular Foundation is the best way to integrate Foundation and AngularJS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;F4A is pretty new and I imagine it&apos;ll add more of F5&apos;s features as it evolves. However, I don&apos;t know if the two will ever be so similar that they can live side-by-side and allow a seamless experience for users. If you&apos;re interested in mixing F4A and F5, you might want to watch Jason Demitri&apos;s &lt;a href=&quot;https://github.com/RelutionDev/foundationUltra&quot;&gt;foundationUltra&lt;/a&gt;. This project combines Angular Foundation, Foundation for Sites, Foundation for Apps and Font Awesome. You can see a demo at &lt;a href=&quot;http://relutiondev.github.io/foundationUltra/&quot;&gt;http://relutiondev.github.io/foundationUltra/&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/how_to_reactjs_and_tooling</id>
        <title type="html">How to ReactJS and Tooling is Awesome at HTML5 Denver</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/how_to_reactjs_and_tooling"/>
        <published>2015-02-17T10:16:08-07:00</published>
        <updated>2015-02-17T17:05:37-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="webpack" scheme="http://roller.apache.org/ns/tags/" />
        <category term="denver" scheme="http://roller.apache.org/ns/tags/" />
        <category term="thingmonk" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="tooling" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5denver" scheme="http://roller.apache.org/ns/tags/" />
        <category term="willklein" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="react" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last night, I had the pleasure of attending the &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/events/220053261/&quot;&gt;HTML5 Denver Meetup&lt;/a&gt; with two talks by &lt;a href=&quot;https://twitter.com/willslab&quot;&gt;Will Klein&lt;/a&gt;. I was motivated to attend because &lt;a href=&quot;http://facebook.github.io/react/&quot;&gt;React&lt;/a&gt; has been on my radar for a while and Will&apos;s first talk was titled &lt;strong&gt;How to ReactJS&lt;/strong&gt;. Will&apos;s &lt;a href=&quot;http://slides.com/willklein/how-to-reactjs#/&quot;&gt;presentation&lt;/a&gt; doesn&apos;t show the real meat of this talk, which contained lots of live coding. Will started with a static webapp, then converted it to use React bit-by-bit. His live coding was greatly helped by the fact that he had 3-4 co-workers in the room, so there was a sense of pair programming when things didn&apos;t work. During the presentation, he mentioned the &lt;a href=&quot;http://devchat.tv/js-jabber/073-jsj-react-with-pete-hunt-and-jordan-walke&quot;&gt;JavaScript Jabber Podcast on React&lt;/a&gt;. I listened to it this morning, and I recommend it if you want to learn about the history of React.&lt;/p&gt;
&lt;p&gt;Will&apos;s second talk was titled &lt;strong&gt;Tooling is Awesome&lt;/strong&gt;. In this presentation, he showed us how to use &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npm&lt;/a&gt; and &lt;a href=&quot;http://webpack.github.io/&quot;&gt;webpack&lt;/a&gt;. Again, the &lt;a href=&quot;http://slides.com/willklein/tooling-is-awesome#/&quot;&gt;presentation&lt;/a&gt; doesn&apos;t capture the vast amount of knowledge demonstrated during the &lt;em&gt;live-cli&lt;/em&gt; session. I hadn&apos;t heard of webpack before, so I was pumped to learn about it. If you need to complete/translate to JavaScript or CSS from another language, chances are that webpack will work well for you. During this demo, Will converted the previously developed React code to require/export modules, as well to do transpilation using webpack&apos;s &lt;a href=&quot;https://github.com/petehunt/jsx-loader&quot;&gt;jsx-loader&lt;/a&gt;. He also mentioned Keith Cirkel&apos;s &lt;a href=&quot;http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/&quot;&gt;How to Use npm as a Build Tool&lt;/a&gt;. If you&apos;re just getting started with JavaScript development and don&apos;t want to learn tools like Grunt or Gulp, this article will help you use npm as your only build tool.&lt;/p&gt;
&lt;p&gt;Even though you can&apos;t experience the live-coding that happened last night, the code &lt;a href=&quot;https://github.com/willklein/how-to-react&quot;&gt;has been posted to GitHub&lt;/a&gt;. If you&apos;re looking to have talks about developing with React, I&apos;d suggest contacting Will. He delivered great talks on subjects I&apos;ve been keen to learn more about. Thanks Will!&lt;/p&gt;
&lt;p&gt;In other Denver-related tech news, &lt;a href=&quot;http://thingmonk.com/&quot;&gt;ThingMonk&lt;/a&gt; is coming March 3-4 and HTML5 Denver has &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/events/220053733/&quot;&gt;lightning talks&lt;/a&gt; on March 23rd. ThingMonk is &quot;a meeting of the tribes for people building the Internet of Things&quot; and is sure to be a great conference. The &lt;a href=&quot;http://redmonk.com/&quot;&gt;Redmonk&lt;/a&gt; crew is always fun to hang out with and knows how to create a conference. Did I mention it&apos;s at a distillery?! The lightning talks in March are always a great time too. You can really learn a lot in a short period of time and it&apos;s a great way to share knowledge about cool technology you&apos;ve recently used. Heck, you could attend ThingMonk, then create a lightning talk about what you learned for HTML5 Denver!</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/converting_an_application_to_jhipster</id>
        <title type="html">Converting an Application to JHipster</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/converting_an_application_to_jhipster"/>
        <published>2015-02-12T09:28:59-07:00</published>
        <updated>2015-02-12T15:29:50-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="groovy" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jhipster" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jpa" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springboot" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="dosug" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;&lt;a href=&quot;http://jhipster.github.io/&quot;&gt;&lt;img src=&quot;http://jhipster.github.io/images/logo-jhipster.png&quot; class=&quot;picture&quot; width=&quot;94&quot;&gt;&lt;/a&gt;
I&apos;ve been intrigued by &lt;a href=&quot;http://jhipster.github.io/&quot;&gt;JHipster&lt;/a&gt; ever since I first tried it &lt;a href=&quot;http://raibledesigns.com/rd/entry/getting_started_with_jhipster_on&quot;&gt;last September&lt;/a&gt;. I&apos;d worked with AngularJS and Spring Boot quite a bit, and I liked the idea that someone had combined them, adding some nifty features along the way. When I &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_art_of_angularjs_in&quot;&gt;spoke about AngularJS&lt;/a&gt; earlier this month, I included &lt;a href=&quot;http://www.slideshare.net/mraible/the-art-of-angularjs-in-2015/67&quot;&gt;a few slides on JHipster&lt;/a&gt; near the end of the presentation.&lt;/p&gt;
&lt;p&gt;This week, I received an email from someone who attended that presentation. &lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;Hey Matt,&lt;br&gt;
We met a few weeks back when you presented at DOSUG. You were talking about JHipster which I had been eyeing for a few months and wanted your quick .02 cents.&lt;/p&gt;
&lt;p&gt;
I have built a pretty heavy application over the last 6 months that is using mostly the same tech as JHipster. 
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;li&gt;Spring&lt;/li&gt;
&lt;li&gt;JPA&lt;/li&gt;
&lt;li&gt;AngularJS&lt;/li&gt;
&lt;li&gt;Compass&lt;/li&gt;
&lt;li&gt;Grunt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
It&apos;s ridiculously close for most of the tech stack. So, I was debating rolling it over into a JHipster app to make it a more familiar stack for folks. My concern is that it I will spend months trying to shoehorn it in for not much ROI. Any thoughts on going down this path?
What are the biggest issues you&apos;ve seen in using JHipster?
It seems pretty straightforward except for the entity generators. I&apos;m concerned they are totally different than what I am using. 
&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;
The main difference in what I&apos;m doing compared to JHipster is my almost complete use of groovy instead of old school Java in the app. I would have to be forced into going back to regular java beans...
Thoughts?&lt;/p&gt; 
&lt;/blockquote&gt;
&lt;p&gt;I replied with the following advice:&lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;
JHipster is great for starting a project, but I don&apos;t know that it buys you much value after the first few months. I would stick with your current setup and consider JHipster for your next project. I&apos;ve only prototyped with it, I haven&apos;t created any client apps or put anything in production. I have with Spring Boot and AngularJS though, so I like that JHipster combines them for me.
&lt;/p&gt;
&lt;p&gt;
JHipster doesn&apos;t generate Scala or Groovy code, but you could still use them in a project as long as you had Maven/Gradle configured properly. 
&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;
You might try generating a new app with JHipster and examine how they&apos;re doing this. At the very least, it can be a good learning tool, even if you&apos;re not using it directly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Java Hipsters: Do you agree with this advice? Have you tried migrating an existing app to JHipster? Are any of you using Scala or Groovy in your JHipster projects?&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/appfuse_reduced</id>
        <title type="html">AppFuse, Reduced</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/appfuse_reduced"/>
        <published>2014-12-16T06:03:31-07:00</published>
        <updated>2014-12-17T16:39:14-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maven" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maintenance" scheme="http://roller.apache.org/ns/tags/" />
        <category term="lessxml" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
In November, I had some time off between clients. To occupy my time, I exercised my body and brain a bit. I spent a couple hours a day exercising and a few hours a day working on
&lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;. AppFuse isn&apos;t used to start projects nearly as much as it once was. This makes sense since there&apos;s been a ton of innovation on the JVM and there&apos;s lots of
&lt;em&gt;get-started-quickly&lt;/em&gt; frameworks now. Among my favorites are Spring Boot, JHipster, Grails and Play.
&lt;p&gt;
    You can see that AppFuse&apos;s community activity has decreased quite a bit over the years by looking at its mailing list
    traffic.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://appfuse.markmail.org/&quot;
       title=&quot;AppFuse Mailing List Traffic, December 2014 by Matt Raible, on Flickr&quot;&gt;&lt;img
        src=&quot;https://farm8.staticflickr.com/7525/15825430580_0531875e59.jpg&quot; width=&quot;500&quot; 
        alt=&quot;AppFuse Mailing List Traffic, December 2014&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    Even though there&apos;s not a lot of users talking on the mailing list, it still seems to get quite a few downloads from
    Maven Central.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://www.flickr.com/photos/mraible/16011987392&quot;
       title=&quot;AppFuse Maven Central Stats, November 2014 by Matt Raible, on Flickr&quot;&gt;&lt;img
        src=&quot;https://farm8.staticflickr.com/7534/16011987392_442236433b.jpg&quot; width=&quot;500&quot; 
        alt=&quot;AppFuse Maven Central Stats, November 2014&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    I think the biggest value that AppFuse provides now is a learning tool for those who work on it. Also, it&apos;s a good place to
    show other developers how they can evolve with open source frameworks (e.g. Spring, Hibernate, JSF, Tapestry, Struts) over several years. Showing how
    we migrated to Spring MVC Test, for example, might be useful. The upcoming move to Spring Data instead of our
    Generic DAO solution might be interesting as well. 
&lt;/p&gt;
&lt;p&gt;
    Regardless of whether AppFuse is used a lot or not, it should be easy to maintain. Over the several weeks, I made some
    opinionated changes and achieved some pretty good progress on simplifying things and making the project easier to
    maintain. The previous structure has a lot of duplicate versions, properties and plugin configurations between
    different projects. I was able to leverage Maven&apos;s inheritance model to make a number of improvements:
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
In November, I had some time off between clients. To occupy my time, I exercised my body and brain a bit. I spent a couple hours a day exercising and a few hours a day working on
&lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;. AppFuse isn&apos;t used to start projects nearly as much as it once was. This makes sense since there&apos;s been a ton of innovation on the JVM and there&apos;s lots of
&lt;em&gt;get-started-quickly&lt;/em&gt; frameworks now. Among my favorites are Spring Boot, JHipster, Grails and Play.
&lt;p&gt;
    You can see that AppFuse&apos;s community activity has decreased quite a bit over the years by looking at its mailing list
    traffic.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://appfuse.markmail.org/&quot;
       title=&quot;AppFuse Mailing List Traffic, December 2014 by Matt Raible, on Flickr&quot;&gt;&lt;img
        src=&quot;https://farm8.staticflickr.com/7525/15825430580_0531875e59.jpg&quot; width=&quot;500&quot; 
        alt=&quot;AppFuse Mailing List Traffic, December 2014&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    Even though there&apos;s not a lot of users talking on the mailing list, it still seems to get quite a few downloads from
    Maven Central.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://www.flickr.com/photos/mraible/16011987392&quot;
       title=&quot;AppFuse Maven Central Stats, November 2014 by Matt Raible, on Flickr&quot;&gt;&lt;img
        src=&quot;https://farm8.staticflickr.com/7534/16011987392_442236433b.jpg&quot; width=&quot;500&quot; 
        alt=&quot;AppFuse Maven Central Stats, November 2014&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    I think the biggest value that AppFuse provides now is a learning tool for those who work on it. Also, it&apos;s a good place to
    show other developers how they can evolve with open source frameworks (e.g. Spring, Hibernate, JSF, Tapestry, Struts) over several years. Showing how
    we migrated to Spring MVC Test, for example, might be useful. The upcoming move to Spring Data instead of our
    Generic DAO solution might be interesting as well. 
&lt;/p&gt;
&lt;p&gt;
    Regardless of whether AppFuse is used a lot or not, it should be easy to maintain. Over the several weeks, I made some
    opinionated changes and achieved some pretty good progress on simplifying things and making the project easier to
    maintain. The previous structure has a lot of duplicate versions, properties and plugin configurations between
    different projects. I was able to leverage Maven&apos;s inheritance model to make a number of improvements:
&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Changed AppFuse&apos;s parent to be based on the &lt;a
        href=&quot;http://www.infoq.com/news/2014/07/springio-platform&quot;&gt;Spring IO Platform&lt;/a&gt;. This project
        is a dependency manager that defines version numbers for open source projects that work well with Spring.
    &lt;/li&gt;
    &lt;li&gt;Defined plugins, their versions and configurations in &lt;code&gt;&amp;lt;pluginManagement&gt;&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Defined dependencies, their versions and exclusions in &lt;code&gt;&amp;lt;dependencyManagement&gt;&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Simplified archetypes so new projects have minimal dependencies. For example, here&apos;s a basic project&apos;s &lt;code&gt;pom.xml&lt;/code&gt;:
    &lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&quot;&amp;gt;

    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
    &amp;lt;groupId&amp;gt;com.company&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;springmvc-project&amp;lt;/artifactId&amp;gt;
    &amp;lt;packaging&amp;gt;war&amp;lt;/packaging&amp;gt;
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;name&amp;gt;AppFuse Spring MVC Application&amp;lt;/name&amp;gt;

    &amp;lt;parent&amp;gt;
        &amp;lt;groupId&amp;gt;org.appfuse&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;appfuse-web&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;3.5.0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;/parent&amp;gt;

    &amp;lt;build&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;de.juplo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;hibernate4-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;dbunit-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;build-helper-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/build&amp;gt;

    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.appfuse&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;appfuse-${web.framework}&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${appfuse.version}&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;

    &amp;lt;properties&amp;gt;
        &amp;lt;amp.genericCore&amp;gt;true&amp;lt;/amp.genericCore&amp;gt;
        &amp;lt;amp.fullSource&amp;gt;false&amp;lt;/amp.fullSource&amp;gt;
        &amp;lt;dao.framework&amp;gt;hibernate&amp;lt;/dao.framework&amp;gt;
        &amp;lt;db.name&amp;gt;mydatabase&amp;lt;/db.name&amp;gt;
        &amp;lt;web.framework&amp;gt;spring&amp;lt;/web.framework&amp;gt;

        &amp;lt;!-- Framework/Plugin versions --&amp;gt;
        &amp;lt;appfuse.version&amp;gt;3.5.0-SNAPSHOT&amp;lt;/appfuse.version&amp;gt;
        &amp;lt;java.version&amp;gt;1.7&amp;lt;/java.version&amp;gt;
    &amp;lt;/properties&amp;gt;

    &amp;lt;profiles&amp;gt;
        &amp;lt;profile&amp;gt;
            &amp;lt;id&amp;gt;itest&amp;lt;/id&amp;gt;
            &amp;lt;build&amp;gt;
                &amp;lt;plugins&amp;gt;
                    &amp;lt;plugin&amp;gt;
                        &amp;lt;groupId&amp;gt;org.codehaus.cargo&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;cargo-maven2-plugin&amp;lt;/artifactId&amp;gt;
                    &amp;lt;/plugin&amp;gt;
                    &amp;lt;plugin&amp;gt;
                        &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;webtest-maven-plugin&amp;lt;/artifactId&amp;gt;
                    &amp;lt;/plugin&amp;gt;
                &amp;lt;/plugins&amp;gt;
            &amp;lt;/build&amp;gt;
        &amp;lt;/profile&amp;gt;
    &amp;lt;/profiles&amp;gt;

    &amp;lt;reporting&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;webtest-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/reporting&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;
    span.diffstat {
        white-space: nowrap;
        text-align: right;
        font-family: Helvetica, arial, freesans, clean, sans-serif, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;;
        color: #666;
        font-weight: bold;
        font-size: 12px;
        cursor: default;
    }

    span.diffstat .lines-added, span.diffstat .lines-deleted {
        display: inline-block;
        margin-left: 3px;
        font-weight: bold;
    }
&lt;/style&gt;
&lt;p&gt;
    The pull request for these changes says it all:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/appfuse/appfuse/pull/20&quot;&gt;AppFuse&lt;/a&gt; &lt;span class=&quot;diffstat&quot;&gt;
          &lt;span class=&quot;lines-added&quot; style=&quot;color: #55a532&quot;&gt;
            &lt;span class=&quot;diffstat-icon&quot;&gt;+&lt;/span&gt;4,822
          &lt;/span&gt;
          &lt;span class=&quot;lines-deleted&quot; style=&quot;color: #bd2c00&quot;&gt;
            &lt;span class=&quot;diffstat-icon&quot;&gt;-&lt;/span&gt;14,369
          &lt;/span&gt;
        &lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/appfuse/appfuse-light/pull/1&quot;&gt;AppFuse Light&lt;/a&gt; &lt;span class=&quot;diffstat&quot;&gt;
              &lt;span class=&quot;lines-added&quot; style=&quot;color: #55a532&quot;&gt;
                &lt;span class=&quot;diffstat-icon&quot;&gt;+&lt;/span&gt;776
              &lt;/span&gt;
              &lt;span class=&quot;lines-deleted&quot; style=&quot;color: #bd2c00&quot;&gt;
                &lt;span class=&quot;diffstat-icon&quot;&gt;-&lt;/span&gt;4,687
              &lt;/span&gt;

            &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s right, I was able to eliminate a good chunk of code without affecting any of AppFuse&apos;s functionality&lt;sup&gt;&lt;a
    href=&quot;http://raibledesigns.com/rd/entry/appfuse_reduced#footnote1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.
    I think we can all agree that less code == easier maintenance. This theme will continue as we work on future
    releases.
&lt;/p&gt;

&lt;p&gt;Other improvements include migrating all tests to use JUnit4, integrating Spring MVC Test, and configuring the
    surefire plugin to run tests in parallel. I also The &lt;a href=&quot;http://mojo.codehaus.org/build-helper-maven-plugin/&quot;&gt;build-helper-maven-plugin&lt;/a&gt;
    is now used to find open ports for Cargo to run and a lot of testing was done to ensure you can build/test multiple
    AppFuse-derived projects at the same time. Finally, I migrated to the &lt;a href=&quot;http://juplo.de/hibernate4-maven-plugin/&quot;&gt;hibernate4-maven-plugin&lt;/a&gt; and upgraded to Tapestry 5.4.&lt;/p&gt;

&lt;p&gt;In the next version of AppFuse, I plan to remove as
    much XML as possible - moving all of the configuration to Spring&apos;s JavaConfig. We&apos;ll also be moving to Java 8 as a
    minimum. I&apos;m even considering getting rid of all the pom.xml files in favor of another build language that requires
    less code. &lt;/p&gt;
&lt;p&gt;In other words, the upcoming 3.5 release will be the last release that supports Java 7 and uses Spring&apos;s XML for configuration. AppFuse 4.0 will strive for #NoXML.
   &lt;a href=&quot;http://appfuse.org/display/APF/Roadmap&quot;&gt;The project&apos;s roadmap&lt;/a&gt; has more details on additional
    hopes and dreams.&lt;/p&gt;

&lt;p&gt;We&apos;d love to hear your feedback on these change. Do you like the simplification theme? Are you OK with having AppFuse
    as a parent in your projects?
&lt;/p&gt;
&lt;p class=&quot;footnotes&quot; style=&quot;border-top: 1px dotted silver; padding-top: 5px; font-size: .9em&quot;&gt;
    &lt;a name=&quot;footnote1&quot;&gt;&lt;/a&gt;1. For project
    and code stats, see &lt;a href=&quot;https://www.openhub.net/p/appfuse/analyses/latest/languages_summary&quot;&gt;AppFuse on Open
    Hub&lt;/a&gt;.
&lt;/p&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/why_i_prefer_intellij_idea</id>
        <title type="html">Why I prefer IntelliJ IDEA over Eclipse</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/why_i_prefer_intellij_idea"/>
        <published>2014-07-21T13:33:55-06:00</published>
        <updated>2014-11-06T20:03:50-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webdevelopment" scheme="http://roller.apache.org/ns/tags/" />
        <category term="intellij" scheme="http://roller.apache.org/ns/tags/" />
        <category term="idea" scheme="http://roller.apache.org/ns/tags/" />
        <category term="eclipse" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;Over the last couple months, I&apos;ve received a few emails asking why I prefer &lt;a href=&quot;http://www.jetbrains.com/idea/&quot;&gt;IntelliJ IDEA&lt;/a&gt; over &lt;a href=&quot;https://www.eclipse.org/&quot;&gt;Eclipse&lt;/a&gt;. They usually go something like this:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
I keep seeing you recommending IntelliJ. I keep trying it intermittently with using Eclipse, but I feel like I&apos;m missing something obvious that makes so many people think it&apos;s better. 
Granted having the usual plugins incorporated is nice, but other things like the build process and debugger sometimes seems a step back from Eclipse. Could you please blog a &apos;10 reasons why I love IntelliJ&apos; or point me to something that would clue me in?
&lt;/p&gt;
&lt;p&gt;
I grew to love IntelliJ for a few reasons. It all started in 2006 when I decided to migrate &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; from Ant to Maven. Before that, I was a huge Eclipse fan (2002 - 2006). Before Eclipse, I used HomeSite, an HTML Editor to write all my Java code (1999-2002). Eclipse was the first IDE that didn&apos;t hog all my system&apos;s memory and was pleasant to work with.
&lt;/p&gt;
&lt;p&gt;
The reason I started using IntelliJ in 2006 was because of it&apos;s multi-module Maven support. Eclipse&apos;s Maven support was terrible, and m2e hasn&apos;t gotten a whole lot better in recent years AFAIK. 
&lt;/p&gt;
&lt;p&gt;
Back then, I used to think everything should be built and run from the command line. A couple years later, I realized it was better to run tests and debug from an IDE. Now I&apos;m more concerned with the ability to run tests and debug in an IDE than I am from the build system.
&lt;/p&gt;
&lt;p&gt;
In 2009, I started doing a lot more front-end work: writing HTML, CSS and JavaScript. I also started digging into alternate languages for these: Jade, GWT, CoffeeScript, LESS, SASS - even Scala. I found IntelliJ&apos;s support, and plugins, to be outstanding for these languages and really enjoyed how it would tell me I had invalid JavaScript, HTML and CSS. 
&lt;/p&gt;
&lt;p&gt;
My original passion in software was HTML and JavaScript and I found that hasn&apos;t changed in the last 15 years. AFAIK, Eclipse still has terrible web tools support; it excels at Java (and possibly C++ support). Even today, I write most of my HTML code (&lt;a href=&quot;http://www.infoq.com/author/Matt-Raible&quot;&gt;for InfoQ&lt;/a&gt; and this blog) in IntelliJ.
&lt;/p&gt;
&lt;p&gt;
In reality, it probably doesn&apos;t matter which IDE you use, as long as you&apos;re productive with it. Once you learn one IDE well, the way others do things will likely seem backwards. I&apos;m so familiar with debugging in IntelliJ, that when I tried to use Eclipse&apos;s debugger a few weeks ago, it seemed backwards to me. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In a nutshell: the technologies I&apos;ve worked with have been better embraced by IntelliJ. Has this happened to you? Have certain technologies caused you to use one IDE over another?&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/this_site_now_powered_by</id>
        <title type="html">This site now powered by Java 8, Tomcat 7 and Wufoo Forms</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/this_site_now_powered_by"/>
        <published>2014-04-09T07:37:59-06:00</published>
        <updated>2014-05-08T20:09:00-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="tomcat7" scheme="http://roller.apache.org/ns/tags/" />
        <category term="wufoo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java8" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ssl" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apacheroller" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I recently upgraded this site to use the latest version of &lt;a href=&quot;http://roller.apache.org/&quot;&gt;Apache Roller&lt;/a&gt;. It was a minor release (5.0.3), but I figured I&apos;d document the steps in case &quot;early onset&quot; comes soon. First of all, to download raibledesigns.com and get it running locally, I perform the following steps:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Backup everything using ~/bin/backup.sh on raibledesigns.com&lt;/li&gt;
&lt;li&gt;scp backup file to local hard drive and expand&lt;/li&gt;
&lt;li&gt;Copy ROOT, skins and repository directories to local webapps&lt;/li&gt;
&lt;li&gt;Make sure activation, mail, mysql and jta JARs are in $CATALINA_HOME/lib&lt;/li&gt;
&lt;li&gt;Copy roller-custom.properties from raibledesigns.com&apos;s $CATALINA_HOME/lib&lt;/li&gt;
&lt;li&gt;Copy context files from hosted $CATALINA_HOME/conf/Catalina to local directory&lt;/li&gt;
&lt;li&gt;Import database and change roller-custom.properties to match local credentials&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, to upgrade to the latest Roller release, I do the following:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download latest Roller release and expand&lt;/li&gt;
&lt;li&gt;Copy JARs (from WEB-INF/lib) to existing install (to upgrade dependencies)&lt;/li&gt;
&lt;li&gt;Delete any lower-versioned JARS from WEB-INF/lib directory&lt;/li&gt;
&lt;li&gt;Copy JSPs (from WEB-INF/jsps) to existing install&lt;/li&gt;
&lt;li&gt;Run database migration scripts from WEB-INF/classes/dbscripts&lt;/li&gt;
&lt;li&gt;Use a diff tool (like SmartSynchronize) to compare new vs. existing&lt;/li&gt;
&lt;li&gt;Test and troubleshoot (if there&apos;s startup errors)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This process has worked well for the last 10 years, and it&apos;s been in my head the whole time. It&apos;s bound to escape someday.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contact Form Enhancements&lt;/strong&gt;&lt;br/&gt;
In addition to upgrading Roller, I also upgraded Tomcat 6 to Tomcat 7.0.52. In doing so, I found &lt;a href=&quot;http://tomcat.markmail.org/thread/3jbtasxiaekzgi62&quot;&gt;Jakarta&apos;s Mailer Taglib doesn&apos;t work with Tomcat 7&lt;/a&gt;. As you can tell from the aforementioned thread, I&apos;ve known this for several years. That&apos;s the only thing that&apos;s stopped me from upgrading Tomcat the past couple years. 
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;I recently upgraded this site to use the latest version of &lt;a href=&quot;http://roller.apache.org/&quot;&gt;Apache Roller&lt;/a&gt;. It was a minor release (5.0.3), but I figured I&apos;d document the steps in case &quot;early onset&quot; comes soon. First of all, to download raibledesigns.com and get it running locally, I perform the following steps:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Backup everything using ~/bin/backup.sh on raibledesigns.com&lt;/li&gt;
&lt;li&gt;scp backup file to local hard drive and expand&lt;/li&gt;
&lt;li&gt;Copy ROOT, skins and repository directories to local webapps&lt;/li&gt;
&lt;li&gt;Make sure activation, mail, mysql and jta JARs are in $CATALINA_HOME/lib&lt;/li&gt;
&lt;li&gt;Copy roller-custom.properties from raibledesigns.com&apos;s $CATALINA_HOME/lib&lt;/li&gt;
&lt;li&gt;Copy context files from hosted $CATALINA_HOME/conf/Catalina to local directory&lt;/li&gt;
&lt;li&gt;Import database and change roller-custom.properties to match local credentials&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, to upgrade to the latest Roller release, I do the following:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download latest Roller release and expand&lt;/li&gt;
&lt;li&gt;Copy JARs (from WEB-INF/lib) to existing install (to upgrade dependencies)&lt;/li&gt;
&lt;li&gt;Delete any lower-versioned JARS from WEB-INF/lib directory&lt;/li&gt;
&lt;li&gt;Copy JSPs (from WEB-INF/jsps) to existing install&lt;/li&gt;
&lt;li&gt;Run database migration scripts from WEB-INF/classes/dbscripts&lt;/li&gt;
&lt;li&gt;Use a diff tool (like SmartSynchronize) to compare new vs. existing&lt;/li&gt;
&lt;li&gt;Test and troubleshoot (if there&apos;s startup errors)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This process has worked well for the last 10 years, and it&apos;s been in my head the whole time. It&apos;s bound to escape someday.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contact Form Enhancements&lt;/strong&gt;&lt;br/&gt;
In addition to upgrading Roller, I also upgraded Tomcat 6 to Tomcat 7.0.52. In doing so, I found &lt;a href=&quot;http://tomcat.markmail.org/thread/3jbtasxiaekzgi62&quot;&gt;Jakarta&apos;s Mailer Taglib doesn&apos;t work with Tomcat 7&lt;/a&gt;. As you can tell from the aforementioned thread, I&apos;ve known this for several years. That&apos;s the only thing that&apos;s stopped me from upgrading Tomcat the past couple years. 
&lt;/p&gt;
Rather than using my own code to send email, I figured I&apos;d &lt;a href=&quot;https://twitter.com/mraible/status/448227865889210368&quot;&gt;explore my options&lt;/a&gt;. I looked into &lt;a href=&quot;http://kontactr.com/&quot;&gt;Kontactr&lt;/a&gt;, &lt;a href=&quot;http://www.emailmeform.com/&quot;&gt;EmailMeForm&lt;/a&gt; and &lt;a href=&quot;http://www.wufoo.com/&quot;&gt;Wufoo&lt;/a&gt;. The first two allowed me to customize their colors, but Wufoo was the only one that had a transparent background feature. Because Wufoo form&apos;s are transparent, and I&apos;d &lt;a href=&quot;http://issues.appfuse.org/browse/APF-309&quot;&gt;copied their CSS for AppFuse&apos;s forms&lt;/a&gt; in the past, I decided to use it. To make it work with both of this site&apos;s themes (light and dark), I did need to create two forms on Wufoo and do some local JavaScript modifications. You can see the results on &lt;a href=&quot;http://raibledesigns.com/rd/page/contact&quot;&gt;my contact page&lt;/a&gt; (view-source to see the JavaScript). If you switch the theme (by clicking the little rectangles in the top-right corner), you can see how the form is redrawn with the proper color scheme.
&lt;/p&gt;
&lt;p&gt;If I&apos;d found it sooner, I likely would&apos;ve used &lt;a href=&quot;http://forms.brace.io/&quot;&gt;Brace Forms&lt;/a&gt; for my contact form.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Theme Improvements&lt;/strong&gt;&lt;br/&gt;
Ever since I &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;switched to a new look and feel&lt;/a&gt;, there&apos;s been improvements I wanted to make. &lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&gt;
The stylesheet switching doesn&apos;t happen as fast as I&apos;d hoped (there&apos;s a flash even if using cookies), so I&apos;ll likely be converting some theme-setting logic to the server-side. The HTML5 version of the FaceBook Like Button requires you to specify the &quot;data-colorscheme&quot; in markup so this further supports moving to the server.
&lt;/blockquote&gt;
&lt;p&gt;To fix this issue, I tried &lt;a href=&quot;http://markmail.org/message/jdhfplj4kd27ub7e&quot;&gt;creating a cookie-reader plugin&lt;/a&gt; for Roller. This worked, but not as well as I&apos;d hoped. The theme cookie was read the first time the page loaded, but then cached for subsequent requests. If a user changed their theme, the new value would not be read by the cached page. My solution now is putting the JavaScript &lt;em&gt;set-default-theme&lt;/em&gt; logic right after the stylesheet to set it before the body loads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Powered by Java 8&lt;/strong&gt;&lt;br/&gt;
To make this site run on Java 8, I had to move to a new server. &lt;a href=&quot;http://kgbinternet.com&quot;&gt;Keith&lt;/a&gt; did a bunch of work on my old server to get Java 8 working, but found Linux JDK 7 code has dependancies on the 2.6 kernel. If you&apos;re reading this post, the DNS changes have propagated. This means I have my first Java 8 app in production! &lt;img src=&quot;//raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secured by SSL&lt;/strong&gt;&lt;br/&gt;
The last change I made was to start using my SSL certificate on this site. You can read about the challenges I encountered &lt;a href=&quot;http://markmail.org/message/uorad3gme6hj3syy&quot;&gt;on the Roller mailing list&lt;/a&gt;. The final solution involved changing my Absolute URL to use https and then changing my theme to use the following for its base.&lt;/p&gt;
&lt;pre&gt;&amp;lt;base href=&quot;$absBaseURL.replace(&apos;https:&apos;, &apos;&apos;)&quot; /&gt;&lt;/pre&gt;
&lt;p&gt;I also had to change a number of iframes to use src=&quot;//...&quot; instead of src=&quot;//...&quot;. To force HTTPS for the admin pages, Keith configured Apache to do the redirect. Using Roller to redirect resulted in infinite loop errors. 
&lt;/p&gt;
&lt;p&gt;This means you can now access this site using &lt;a href=&quot;https://raibledesigns.com&quot;&gt;https://raibledesigns.com&lt;/a&gt; or &lt;a href=&quot;http://raibledesigns.com&quot;&gt;http://raibledesigns.com&lt;/a&gt;, whichever you prefer. Speaking of SSL, you might wonder if this new server is affected by &lt;a href=&quot;http://heartbleed.com/&quot;&gt;The Heartbleed Bug&lt;/a&gt;. According to Keith, it is not.
&lt;blockquote class=&quot;quote&quot;&gt;
This server is still running openSSL 1.0.0 which was shipped with Centos 6.1, and the Heartbleed vulnerability appeared in OpenSSL 1.0.1 which shipped Centos 6.5.&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
This site has undergone quite a few updates this month: Roller 5.0.3, Tomcat 7.0.52, &lt;a href=&quot;http://raible.kgbinternet.com/rd/page/contact&quot;&gt;a Wufoo contact form (+ recent tweets on the same page)&lt;/a&gt;, Java 8 and optional SSL. If you see any issues, please let me know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;May 8, 2014 Update&lt;/strong&gt;: I decided to revert to &quot;http&quot; for the absolute URL instead of &quot;https&quot;. However, I&apos;ve updated all &quot;src&quot; attributes on &amp;lt;iframe&gt; and &amp;lt;img&gt; tags to use schema-less URLs (// instead of http://). This means that both secure and non-secure URLs should work.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_an_ios_native_app</id>
        <title type="html">Developing an iOS Native App with Ionic</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_an_ios_native_app"/>
        <published>2014-03-27T16:38:55-06:00</published>
        <updated>2014-05-08T19:47:26-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="ionic" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ios" scheme="http://roller.apache.org/ns/tags/" />
        <category term="mobile" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ionicframework" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
    In my current project, I&apos;ve been helping a client develop a native iOS app for their customers. It&apos;s
    written mostly in Objective-C and talks to a REST API. I talked about &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/documenting_your_spring_api_with&quot;&gt;how we documented our REST API&lt;/a&gt;
    a couple days ago. We developed a prototype for this application back in December, using
    &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; and &lt;a href=&quot;http://getbootstrap.com/&quot;&gt;Bootstrap&lt;/a&gt;. Rather than
    using &lt;a href=&quot;http://phonegap.com/&quot;&gt;PhoneGap&lt;/a&gt;, we loaded our app in a UIWebView.
&lt;/p&gt;

&lt;p&gt;
    It all seemed to work well until we needed to read an activation code with the device&apos;s camera. Since
    we didn&apos;t know how to do &lt;a href=&quot;http://en.wikipedia.org/wiki/Optical_character_recognition&quot;&gt;OCR&lt;/a&gt; in JavaScript,
    we figured a mostly-native app was the way to go. We hired an outside company to do iOS development in January and
    they&apos;ve been developing the app since the beginning of February. In the last couple weeks, we encountered some
    screens that seemed fitting for HTML5, so we turned back to our AngularJS prototype.
&lt;/p&gt;

&lt;p&gt;The prototype used Bootstrap heavily, but we quickly learned it didn&apos;t look like an iOS 7 app, which is what our
    UX Designer requested. A co-worker pointed out &lt;a href=&quot;http://ionicframework.com/&quot;&gt;Ionic&lt;/a&gt;, developed by
    &lt;a href=&quot;http://drifty.com/&quot;&gt;Drifty&lt;/a&gt;. It&apos;s basically &lt;em&gt;Bootstrap for Native&lt;/em&gt;, so the apps you develop
    look and behave like a mobile application.
&lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
    &lt;strong&gt;What is Ionic?&lt;/strong&gt;&lt;br/&gt;
    Free and open source, Ionic offers a library of mobile-optimized HTML, CSS and JS components for building highly
    interactive apps. Built with Sass and optimized for AngularJS.
&lt;/blockquote&gt;

&lt;p&gt;
    I started developing with Ionic a few weeks ago. Using its CSS classes and AngularJS directives,
    I was able to create several new screens in a matter of days. Most of the time, I was learning new things:
    how to &lt;a
        href=&quot;http://forum.ionicframework.com/t/is-it-possible-to-override-the-back-button-behavior-on-certain-screens/1867/2&quot;&gt;
    override its back button&lt;/a&gt; behavior (to launch back into the native app), how to
    &lt;a href=&quot;http://ionicframework.com/docs/angularjs/controllers/view-state/&quot;&gt;configure routes&lt;/a&gt; with
    &lt;a href=&quot;https://github.com/angular-ui/ui-router&quot;&gt;ui-router&lt;/a&gt;, and how to make the
    &lt;a href=&quot;http://ionicframework.com/docs/angularjs/views/loading/&quot;&gt;$ionicLoading service&lt;/a&gt; look native. Now that
    I know a lot of the basics, I feel like I can really crank out some code.
&lt;/p&gt;

&lt;p style=&quot;border: 1px solid #91c89c;
color: #333;
padding: 10px 10px 10px 10px;
background: #f3f9f4;&quot;&gt;
    &lt;strong&gt;Tip:&lt;/strong&gt;
    I learned how subviews work with ui-router thanks to a YouTube video of &lt;a
        href=&quot;http://www.youtube.com/watch?v=dqJRoh8MnBo&quot;&gt;Tim Kindberg on Angular UI-Router&lt;/a&gt;. However, subviews
    never fully made sense until I saw
    &lt;a href=&quot;http://forum.ionicframework.com/t/using-state-go-to-navigate-between-views-doesnt-work-location-path-does/1846/11&quot;&gt;
        Jared Bell&apos;s diagram&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
    To demonstrate how easy it is to use Ionic, I whipped up a quick example application. You can get the source
    on GitHub at &lt;a href=&quot;https://github.com/mraible/boot-ionic&quot;&gt;https://github.com/mraible/boot-ionic&lt;/a&gt;. The app is a
    refactored version of Josh Long&apos;s &lt;a href=&quot;https://github.com/mraible/boot-examples/tree/master/x-auth-security&quot;&gt;x-auth-security&lt;/a&gt; that uses Ionic instead
    of raw AngularJS and Bootstrap. To keep things simple, I did not develop the native app that wraps the HTML.
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
    In my current project, I&apos;ve been helping a client develop a native iOS app for their customers. It&apos;s
    written mostly in Objective-C and talks to a REST API. I talked about &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/documenting_your_spring_api_with&quot;&gt;how we documented our REST API&lt;/a&gt;
    a couple days ago. We developed a prototype for this application back in December, using
    &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; and &lt;a href=&quot;http://getbootstrap.com/&quot;&gt;Bootstrap&lt;/a&gt;. Rather than
    using &lt;a href=&quot;http://phonegap.com/&quot;&gt;PhoneGap&lt;/a&gt;, we loaded our app in a UIWebView.
&lt;/p&gt;

&lt;p&gt;
    It all seemed to work well until we needed to read an activation code with the device&apos;s camera. Since
    we didn&apos;t know how to do &lt;a href=&quot;http://en.wikipedia.org/wiki/Optical_character_recognition&quot;&gt;OCR&lt;/a&gt; in JavaScript,
    we figured a mostly-native app was the way to go. We hired an outside company to do iOS development in January and
    they&apos;ve been developing the app since the beginning of February. In the last couple weeks, we encountered some
    screens that seemed fitting for HTML5, so we turned back to our AngularJS prototype.
&lt;/p&gt;

&lt;p&gt;The prototype used Bootstrap heavily, but we quickly learned it didn&apos;t look like an iOS 7 app, which is what our
    UX Designer requested. A co-worker pointed out &lt;a href=&quot;http://ionicframework.com/&quot;&gt;Ionic&lt;/a&gt;, developed by
    &lt;a href=&quot;http://drifty.com/&quot;&gt;Drifty&lt;/a&gt;. It&apos;s basically &lt;em&gt;Bootstrap for Native&lt;/em&gt;, so the apps you develop
    look and behave like a mobile application.
&lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
    &lt;strong&gt;What is Ionic?&lt;/strong&gt;&lt;br/&gt;
    Free and open source, Ionic offers a library of mobile-optimized HTML, CSS and JS components for building highly
    interactive apps. Built with Sass and optimized for AngularJS.
&lt;/blockquote&gt;

&lt;p&gt;
    I started developing with Ionic a few weeks ago. Using its CSS classes and AngularJS directives,
    I was able to create several new screens in a matter of days. Most of the time, I was learning new things:
    how to &lt;a
        href=&quot;http://forum.ionicframework.com/t/is-it-possible-to-override-the-back-button-behavior-on-certain-screens/1867/2&quot;&gt;
    override its back button&lt;/a&gt; behavior (to launch back into the native app), how to
    &lt;a href=&quot;http://ionicframework.com/docs/angularjs/controllers/view-state/&quot;&gt;configure routes&lt;/a&gt; with
    &lt;a href=&quot;https://github.com/angular-ui/ui-router&quot;&gt;ui-router&lt;/a&gt;, and how to make the
    &lt;a href=&quot;http://ionicframework.com/docs/angularjs/views/loading/&quot;&gt;$ionicLoading service&lt;/a&gt; look native. Now that
    I know a lot of the basics, I feel like I can really crank out some code.
&lt;/p&gt;

&lt;p style=&quot;border: 1px solid #91c89c;
color: #333;
padding: 10px 10px 10px 10px;
background: #f3f9f4;&quot;&gt;
    &lt;strong&gt;Tip:&lt;/strong&gt;
    I learned how subviews work with ui-router thanks to a YouTube video of &lt;a
        href=&quot;http://www.youtube.com/watch?v=dqJRoh8MnBo&quot;&gt;Tim Kindberg on Angular UI-Router&lt;/a&gt;. However, subviews
    never fully made sense until I saw
    &lt;a href=&quot;http://forum.ionicframework.com/t/using-state-go-to-navigate-between-views-doesnt-work-location-path-does/1846/11&quot;&gt;
        Jared Bell&apos;s diagram&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
    To demonstrate how easy it is to use Ionic, I whipped up a quick example application. You can get the source
    on GitHub at &lt;a href=&quot;https://github.com/mraible/boot-ionic&quot;&gt;https://github.com/mraible/boot-ionic&lt;/a&gt;. The app is a
    refactored version of Josh Long&apos;s &lt;a href=&quot;https://github.com/mraible/boot-examples/tree/master/x-auth-security&quot;&gt;x-auth-security&lt;/a&gt; that uses Ionic instead
    of raw AngularJS and Bootstrap. To keep things simple, I did not develop the native app that wraps the HTML.
&lt;/p&gt;

&lt;p&gt;Below are the steps I used to convert from AngularJS + Bootstrap to Ionic. If you want to convert a simple AngularJS
    app to use Ionic, hopefully this will help.&lt;/p&gt;

&lt;p&gt;
    &lt;strong&gt;1. Download Ionic and add it to your project.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;Ionic 1.0 Beta was released earlier this week. You can download it from &lt;a
        href=&quot;http://code.ionicframework.com/1.0.0-beta.1/ionic-v1.0.0-beta.1.zip&quot;&gt;here&lt;/a&gt;. Add its files
    to your project. In this example, I &lt;a
            href=&quot;https://github.com/mraible/boot-ionic/commit/d564bb8ecb26c8519f88d6797db53f6ae327d9ad&quot;&gt;added them&lt;/a&gt;
    to &lt;em&gt;src/main/resources/public&lt;/em&gt;. In my index.html, I removed Bootstrap&apos;s CSS and replaced it with Ionic&apos;s.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-    &amp;lt;link href=&quot;webjars/bootstrap/3.1.1/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot;&amp;gt;
+    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;css/ionic.css&quot;/&amp;gt;
  &amp;lt;/head&amp;gt;
-&amp;lt;body style=&quot;padding-top: 60px&quot;&amp;gt;
+&amp;lt;body&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I replaced Angular, Bootstrap and jQuery&apos;s JavaScript references.&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-    &amp;lt;script src=&quot;webjars/jquery/2.0.3/jquery.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
-    &amp;lt;script src=&quot;webjars/bootstrap/3.1.1/js/bootstrap.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
-    &amp;lt;script src=&quot;webjars/angularjs/1.2.13/angular.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
+    &amp;lt;script src=&quot;js/ionic.bundle.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
     &amp;lt;script src=&quot;webjars/angularjs/1.2.13/angular-resource.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
-    &amp;lt;script src=&quot;webjars/angularjs/1.2.13/angular-route.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
     &amp;lt;script src=&quot;webjars/angularjs/1.2.13/angular-cookies.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;blockquote class=&quot;quote&quot; style=&quot;margin-left: 0; margin-right: 0&quot;&gt;
    &lt;strong&gt;What about WebJars?&lt;/strong&gt;&lt;br/&gt;
    You might ask - why not use &lt;a href=&quot;http://www.webjars.org/&quot;&gt;WebJars&lt;/a&gt;? You can, once
    &lt;a href=&quot;https://github.com/webjars/ionic/pull/2&quot;&gt;this pull request&lt;/a&gt; is accepted and an updated version is
deployed to Maven central. &lt;a href=&quot;https://github.com/mraible/boot-ionic/commit/1bffd792683b2428fc16a73f3e7e3e6d43f327c2&quot;&gt;Here&apos;s how&lt;/a&gt;
the application would change.&lt;/blockquote&gt;
&lt;p&gt;
    &lt;strong&gt;2. Change from Angular&apos;s Router to ui-router.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;Ionic uses ui-router for matching URLs and loading particular pages.
    The raw Angular routing looks pretty similar to how it does with ui-router, except it uses a
    &lt;code&gt;$stateProvider&lt;/code&gt;
    service instead of &lt;code&gt;$routeProvider&lt;/code&gt;. You&apos;ll notice I also added &apos;ionic&apos; as a dependency.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-angular.module(&apos;exampleApp&apos;, [&apos;ngRoute&apos;, &apos;ngCookies&apos;, &apos;exampleApp.services&apos;])
+angular.module(&apos;exampleApp&apos;, [&apos;ionic&apos;, &apos;ngCookies&apos;, &apos;exampleApp.services&apos;])
 	.config(
-		[ &apos;$routeProvider&apos;, &apos;$locationProvider&apos;, &apos;$httpProvider&apos;, function($routeProvider, $locationProvider, $httpProvider) {
+		[ &apos;$stateProvider&apos;, &apos;$urlRouterProvider&apos;, &apos;$httpProvider&apos;, function($stateProvider, $urlRouterProvider, $httpProvider) {

-	    $routeProvider.when(&apos;/create&apos;, { templateUrl: &apos;partials/create.html&apos;, controller: CreateController});
+           $stateProvider.state(&apos;create&apos;, {url: &apos;/create&apos;, templateUrl: &apos;partials/create.html&apos;, controller: CreateController})
+               .state(&apos;edit&apos;, {url: &apos;/edit/:id&apos;, templateUrl: &apos;partials/edit.html&apos;, controller: EditController})
+               .state(&apos;login&apos;, {url: &apos;/login&apos;, templateUrl: &apos;partials/login.html&apos;, controller: LoginController})
+               .state(&apos;index&apos;, {url: &apos;/index&apos;, templateUrl: &apos;partials/index.html&apos;, controller: IndexController});

-	    $routeProvider.when(&apos;/edit/:id&apos;, { templateUrl: &apos;partials/edit.html&apos;, controller: EditController});
-	    $routeProvider.when(&apos;/login&apos;, { templateUrl: &apos;partials/login.html&apos;, controller: LoginController});
-	    $routeProvider.otherwise({templateUrl: &apos;partials/index.html&apos;, controller: IndexController});
-
-	    $locationProvider.hashPrefix(&apos;!&apos;);
+	    $urlRouterProvider.otherwise(&apos;/index&apos;);
&lt;/pre&gt;
&lt;p&gt;
    &lt;strong&gt;3. Add Ionic elements to your index.html.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
    In contrast to Bootstrap&apos;s navbar, Ionic has header and footer elements. Rather than using a &lt;a
        href=&quot;http://docs.angularjs.org/api/ngRoute/directive/ngView&quot;&gt;ng-view&lt;/a&gt;
    directive, you use an &amp;lt;ion-nav-view&amp;gt;. It&apos;s a pretty slick setup once you understand it, especially since they
    allow you to easily override &lt;a href=&quot;http://ionicframework.com/docs/api/directive/ionNavBackButton/&quot;&gt;back-button
    behavior&lt;/a&gt; and
    &lt;a href=&quot;http://ionicframework.com/docs/api/directive/ionNavButtons/&quot;&gt;nav buttons&lt;/a&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-    &amp;lt;nav class=&quot;navbar navbar-fixed-top navbar-default&quot; role=&quot;navigation&quot;&amp;gt;
-        &amp;lt;!-- lots of HTML here --&gt;
-    &amp;lt;/nav&amp;gt;
-
-    &amp;lt;div class=&quot;container&quot;&amp;gt;
-        &amp;lt;div class=&quot;alert alert-danger&quot; ng-show=&quot;error&quot;&amp;gt;{{error}}&amp;lt;/div&amp;gt;
-        &amp;lt;div ng-view&amp;gt;&amp;lt;/div&amp;gt;
-    &amp;lt;/div&amp;gt;
+    &amp;lt;ion-nav-bar class=&quot;bar-positive nav-title-slide-ios7&quot;&amp;gt;&amp;lt;/ion-nav-bar&amp;gt;
+    &amp;lt;ion-nav-view animation=&quot;slide-left-right&quot;&amp;gt;
+        &amp;lt;div class=&quot;alert alert-danger&quot; ng-show=&quot;error&quot;&amp;gt;{{error}}&amp;lt;/div&amp;gt;
+    &amp;lt;/ion-nav-view&amp;gt;
+    &amp;lt;ion-footer-bar class=&quot;bar-dark&quot; ng-show=&quot;user&quot;&amp;gt;
+        &amp;lt;button class=&quot;button button-assertive&quot; ng-click=&quot;logout()&quot;&amp;gt;
+            Logout
+        &amp;lt;/button&amp;gt;
+    &amp;lt;/ion-footer-bar&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
    &lt;strong&gt;4. Change your templates to use &amp;lt;ion-view&gt; and &amp;lt;ion-content&gt;.&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;After routes are migrated and basic navigation is working, you&apos;ll need to modify your templates to use &amp;lt;ion-view&amp;gt;
    and &amp;lt;ion-content&amp;gt;. Here&apos;s a diff from the most complicated page in the app.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-&amp;lt;div style=&quot;float: right&quot;&amp;gt;
-	&amp;lt;a href=&quot;#!/create&quot; class=&quot;btn btn-default&quot; ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;Create&amp;lt;/a&amp;gt;
-&amp;lt;/div&amp;gt;
-&amp;lt;div class=&quot;page-header&quot;&amp;gt;
-	&amp;lt;h3&amp;gt;News&amp;lt;/h3&amp;gt;
-&amp;lt;/div&amp;gt;
+&amp;lt;ion-view title=&quot;News&quot;&amp;gt;
+    &amp;lt;ion-content&amp;gt;
+        &amp;lt;ion-nav-buttons side=&quot;left&quot;&amp;gt;
+            &amp;lt;div class=&quot;buttons&quot; ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;
+                &amp;lt;button class=&quot;button button-icon icon ion-ios7-minus-outline&quot;
+                        ng-click=&quot;data.showDelete = !data.showDelete&quot;&amp;gt;&amp;lt;/button&amp;gt;
+            &amp;lt;/div&amp;gt;
+        &amp;lt;/ion-nav-buttons&amp;gt;
+        &amp;lt;ion-nav-buttons side=&quot;right&quot;&amp;gt;
+            &amp;lt;a href=&quot;#/create&quot; class=&quot;button button-icon icon ion-ios7-plus-outline&quot;
+               ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;&amp;lt;/a&amp;gt;
+        &amp;lt;/ion-nav-buttons&amp;gt;

-&amp;lt;div ng-repeat=&quot;newsEntry in newsEntries&quot;&amp;gt;
-	&amp;lt;hr /&amp;gt;
-	&amp;lt;div class=&quot;pull-right&quot;&amp;gt;
-		&amp;lt;a ng-click=&quot;deleteEntry(newsEntry)&quot; class=&quot;btn btn-xs btn-default&quot; ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;Remove&amp;lt;/a&amp;gt;
-		&amp;lt;a href=&quot;#!/edit/{{newsEntry.id}}&quot; class=&quot;btn btn-xs btn-default&quot; ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;Edit&amp;lt;/a&amp;gt;
-	&amp;lt;/div&amp;gt;
-	&amp;lt;h4&amp;gt;{{newsEntry.date | date}}&amp;lt;/h4&amp;gt;
-	&amp;lt;p&amp;gt;{{newsEntry.content}}&amp;lt;/p&amp;gt;
-&amp;lt;/div&amp;gt;
-&amp;lt;hr /&amp;gt;
+        &amp;lt;ion-list show-delete=&quot;data.showDelete&quot; on-delete=&quot;deleteEntry(item)&quot;
+                  option-buttons=&quot;itemButtons&quot; can-swipe=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;
+            &amp;lt;ion-item ng-repeat=&quot;newsEntry in newsEntries&quot; item=&quot;newsEntry&quot;&amp;gt;
+                &amp;lt;h4&amp;gt;{{newsEntry.date | date}}&amp;lt;/h4&amp;gt;
+                &amp;lt;p&amp;gt;{{newsEntry.content}}&amp;lt;/p&amp;gt;
+            &amp;lt;/ion-item&amp;gt;
+        &amp;lt;/ion-list&amp;gt;
+    &amp;lt;/ion-content&amp;gt;
+&amp;lt;/ion-view&amp;gt;
&lt;/pre&gt;
&lt;p&gt;

    I did migrate
    to use an &lt;a href=&quot;http://codepen.io/ionic/pen/JsHjf&quot;&gt;&amp;lt;ion-list&amp;gt; with delete/options buttons&lt;/a&gt;, so
some additional JavaScript changes were needed.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
-function IndexController($scope, NewsService) {
+function IndexController($scope, $state, NewsService) {

    $scope.newsEntries = NewsService.query();

+     $scope.data = {
+         showDelete: false
+     };
+
    $scope.deleteEntry = function(newsEntry) {
         newsEntry.$remove(function() {
              $scope.newsEntries = NewsService.query();
         });
    };
+
+     $scope.itemButtons = [{
+         text: &apos;Edit&apos;,
+         type: &apos;button-assertive&apos;,
+         onTap: function (item) {
+              $state.go(&apos;edit&apos;, {id: item.id});
+         }
+     }];
}
&lt;/pre&gt;
&lt;h3&gt;Screenshots&lt;/h3&gt;
&lt;p&gt;After making all these changes, the app looks pretty good in Chrome.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;https://farm4.staticflickr.com/3733/13454844134_3bec9e6d75_b.jpg&quot;
   data-href=&quot;https://www.flickr.com/photos/mraible/13454844134&quot; title=&quot;Ionic Login&quot; rel=&quot;lightbox[ionic]&quot;&gt;
    &lt;img src=&quot;//farm4.staticflickr.com/3733/13454844134_3bec9e6d75_n.jpg&quot; width=&quot;244&quot; height=&quot;320&quot; alt=&quot;Ionic Login&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://farm6.staticflickr.com/5545/13454844054_fec4b44a35_b.jpg&quot;
   data-href=&quot;https://www.flickr.com/photos/mraible/13454844054&quot; title=&quot;Ionic News&quot; rel=&quot;lightbox[ionic]&quot;&gt;
    &lt;img src=&quot;//farm6.staticflickr.com/5545/13454844054_fec4b44a35_n.jpg&quot; width=&quot;244&quot; height=&quot;320&quot; alt=&quot;Ionic News&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm4.staticflickr.com/3808/13454594373_3092b81b25_b.jpg&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/13454594373&quot; title=&quot;Ionic Swipe&quot; rel=&quot;lightbox[ionic]&quot;&gt;
        &lt;img src=&quot;//farm4.staticflickr.com/3808/13454594373_3092b81b25_n.jpg&quot; width=&quot;244&quot; height=&quot;320&quot; alt=&quot;Ionic Swipe&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm4.staticflickr.com/3769/13454594503_c5609d4000_b.jpg&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/13454594503&quot; title=&quot;Ionic Edit&quot; rel=&quot;lightbox[ionic]&quot;&gt;
        &lt;img src=&quot;//farm4.staticflickr.com/3769/13454594503_c5609d4000_n.jpg&quot; width=&quot;244&quot; height=&quot;320&quot; alt=&quot;Ionic Edit&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h3&gt;Tips and Tricks&lt;/h3&gt;
&lt;p&gt;
    In additional to figuring out how to use Ionic, I discovered a few other tidbits along the way. First of all,
    we had a different default color for the header. Since Ionic uses generic color names (e.g. light, stable, positive, calm),
    I found it easy to change the default value for &quot;positive&quot; and then continue to use their class names.
&lt;/p&gt;
&lt;p&gt;
    &lt;strong&gt;Modifying CSS variable colors&lt;/strong&gt;&lt;br/&gt;
To modify the base color for &quot;positive&quot;, I &lt;a href=&quot;https://github.com/driftyco/ionic&quot;&gt;cloned the source&lt;/a&gt;, and
modified &lt;em&gt;scss/_variables.scss&lt;/em&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
$light: #fff !default;
$stable: #f8f8f8 !default;
-$positive: #4a87ee !default;
+$positive: #589199 !default;
$calm: #43cee6 !default;
$balanced: #66cc33 !default;
$energized: #f0b840 !default;
&lt;/pre&gt;
&lt;p&gt;After making this change, I ran &quot;grunt&quot; and copied &lt;em&gt;dist/css/ionic.css&lt;/em&gt; into our project.&lt;/p&gt; 
&lt;p&gt;
    &lt;strong&gt;iOS Native Integration&lt;/strong&gt;&lt;br/&gt;
    Our app uses a similar token-based authentication mechanism as x-auth-security, except its backed by Crowd.
    However, since users won&apos;t be logging directly into the Ionic app, we added
    the &quot;else&quot; clause in &lt;em&gt;app.js&lt;/em&gt; to allow a token to be passed in via URL. We also allowed the backend API
    path to be overridden.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
/* Try getting valid user from cookie or go to login page */
var originalPath = $location.path();
$location.path(&quot;/login&quot;);
var user = $cookieStore.get(&apos;user&apos;);

if (user !== undefined) {
    $rootScope.user = user;
    $http.defaults.headers.common[xAuthTokenHeaderName] = user.token;
    $location.path(originalPath);
} else {
    // token passed in from native app
    var authToken = $location.search().token;
    if (authToken) {
        $http.defaults.headers.common[&apos;X-Auth-Token&apos;] = authToken;
    }
}

// allow overriding the base API path
$rootScope.apiPath = &apos;/api/v1.0&apos;;
if ($location.search().apiPath) {
    $rootScope.apiPath = $location.search().apiPath;
}
&lt;/pre&gt;
&lt;p&gt;By adding this logic, the iOS app can pull up any particular page in a webview and let the Ionic app talk to the API. Here&apos;s
    what the Objective-C code looks like:
    &lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
NSString *versionNumber = @&quot;v1.0&quot;;
NSString *apiPath = @&quot;https://server.com/api/&quot;;
NSString *authToken = [TemporaryDataStore sharedInstance].authToken;
// webapp is a symbolic link to the Ionic app, created with Angular Seed
NSString *htmlFilePath = [[NSBundle mainBundle] pathForResource:@&quot;index&quot; ofType:@&quot;html&quot; inDirectory:@&quot;webapp/app&quot;];

// Note: We need to do it this way because &apos;fileURLWithPath:&apos; would encode the &apos;#&apos; to &apos;%23&quot; which breaks the html page
NSURL *htmlFileURL = [NSURL fileURLWithPath:htmlFilePath];

NSString *webappURLPath = [NSString stringWithFormat:@&quot;%@#/news?apiPath=%@%@&amp;token=%@&quot;,
                           htmlFileURL.absoluteString, apiPath, versionNumber, authToken];

// Now convert the string to a URL (doesn&apos;t seem to encode the &apos;#&apos; this way)
NSURL *webappURL = [NSURL URLWithString:webappURLPath];
[super updateWithURL:webappURL];
&lt;/pre&gt;
&lt;p&gt;We also had to write some logic to navigate back to the native app. We used a &lt;a href=&quot;http://www.idev101.com/code/Objective-C/custom_url_schemes.html&quot;&gt;
    custom URL scheme&lt;/a&gt; to do this, and the Ionic app simply called it. To override the default back button, I added
    an &quot;ng-controller&quot; attribute to &amp;lt;ion-nav-bar&amp;gt; and added a custom back button.

&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;ion-nav-bar class=&quot;bar-positive nav-title-slide-ios7&quot; ng-controller=&quot;NavController&quot;&amp;gt;
    &amp;lt;ion-nav-back-button class=&quot;button-icon&quot; ng-click=&quot;goBack()&quot;&amp;gt;
        &amp;lt;i class=&quot;ion-arrow-left-c&quot;&amp;gt;&amp;lt;/i&amp;gt;
    &amp;lt;/ion-nav-back-button&amp;gt;
&amp;lt;/ion-nav-bar&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To detect if the app was loaded by iOS (vs. a browser, which we tested in), we used the following logic:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
// set native app indicator
if (document.location.toString().indexOf(&apos;appName.app&apos;) &gt; -1) {
    $rootScope.isNative = true;
}
&lt;/pre&gt;
&lt;p&gt;Our Ionic app has three entry points, defined by &quot;stateName1&quot;, &quot;stateName2&quot; and &quot;stateName3&quot; in this example. The code for our &lt;code&gt;NavController&lt;/code&gt; handles navigating back normally (when in a browser) or back to the native app. The &quot;appName&quot; reference below is a 3-letter acronym we used for our app.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.controller(&apos;NavController&apos;, function($scope, $ionicNavBarDelegate, $state) {
    $scope.goBack = function() {
        if ($scope.isNative &amp;amp;&amp;amp; backToNative($state)) {
            location.href=&apos;appName-ios://back&apos;;
        } else {
            $ionicNavBarDelegate.back();
        }
    };

    function backToNative($state) {
        var entryPoints = &amp;#91;&apos;stateName1&apos;, &apos;stateName2&apos;, &apos;stateName3&apos;&amp;#93;;
        return entryPoints.some(function (entry) {
            return $state.current === $state.get(entry);
        });
    }
})
&lt;/pre&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;

    I&apos;ve enjoyed working with Ionic over the last month. The biggest change I&apos;ve had to make to our AngularJS app has been
    to integrate &lt;a href=&quot;https://github.com/angular-ui/ui-router&quot;&gt;ui-router&lt;/a&gt;. Apart from this, the JavaScript didn&apos;t
    change much. However, the HTML had to change quite a bit. As far as CSS is concerned, I found myself tweaking things
    to fit our designs, but less so than I did with Bootstrap.     When I&apos;ve run into issues with Ionic, the community has been very helpful on their
    &lt;a href=&quot;http://forum.ionicframework.com/&quot;&gt;forum&lt;/a&gt;. It&apos;s the first forum I&apos;ve used that&apos;s powered by
    &lt;a href=&quot;http://www.discourse.org/&quot;&gt;Discourse&lt;/a&gt;, and I dig it.&lt;/p&gt;
&lt;p&gt;You can find the source from this article in my &lt;a href=&quot;https://github.com/mraible/boot-ionic&quot;&gt;boot-ionic project&lt;/a&gt;. Clone it and run &quot;mvn spring-boot:run&quot;, then open &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;If you&apos;re looking to create a native app using HTML5 technologies, I highly recommend you take a look at Ionic.
    We&apos;re glad we did.
    &lt;a href=&quot;http://www.infoq.com/news/2014/03/angular-2-0&quot;&gt;Angular 2.0 will target mobile apps&lt;/a&gt; and Ionic is already
    making them look pretty damn good.
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/2013_a_year_in_review</id>
        <title type="html">2013 - A Year in Review</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/2013_a_year_in_review"/>
        <published>2014-01-31T08:53:10-07:00</published>
        <updated>2014-07-26T21:11:02-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yearinreview" scheme="http://roller.apache.org/ns/tags/" />
        <category term="2013" scheme="http://roller.apache.org/ns/tags/" />
        <category term="blogging" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;2013 was an amazing year:
    Trish and I got married, celebrated on a &apos;round-the-world honeymoon and invested in a new 4x4 VW Bus. I finally
    achieved my goal of vacationing 25% and I got to spend more than two months in the presence of my wonderful parents.
&lt;/p&gt;
&lt;p&gt;
    For this Year in Review post, I&apos;ll use the same format as I did &lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review&quot;&gt;last
        year&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#professional&quot;&gt;Professional&lt;/a&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#speaking&quot;&gt;Speaking&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#projects&quot;&gt;Projects&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#personal&quot;&gt;Personal&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#2014&quot;&gt;2014&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;professional&quot;&gt;Professional&lt;/h2&gt;
&lt;p&gt;
    For the last few years, I&apos;ve generally had one client per year. That changed this year when my contract with Oracle
    ended in May. Fortunately, I had the opportunity to develop a cool dashboard application before I finished. I
    wrote about it in a four-part series.&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;2013 was an amazing year:
    Trish and I got married, celebrated on a &apos;round-the-world honeymoon and invested in a new 4x4 VW Bus. I finally
    achieved my goal of vacationing 25% and I got to spend more than two months in the presence of my wonderful parents.
&lt;/p&gt;
&lt;p&gt;
    For this &lt;a href=&quot;http://raibledesigns.com/rd/tags/yearinreview&quot;&gt;Year in Review&lt;/a&gt; post, I&apos;ll use the same format as I did &lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review&quot;&gt;last
        year&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#professional&quot;&gt;Professional&lt;/a&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#speaking&quot;&gt;Speaking&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#projects&quot;&gt;Projects&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#personal&quot;&gt;Personal&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2013_a_year_in_review#2014&quot;&gt;2014&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;professional&quot;&gt;Professional&lt;/h2&gt;
&lt;p&gt;
    For the last few years, I&apos;ve generally had one client per year. That changed this year when my contract with Oracle
    ended in May. Fortunately, I had the opportunity to develop a cool dashboard application before I finished. I
    wrote about it in a four-part series.&lt;/p&gt;
&lt;ul&gt;

    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;Developing with AngularJS - Part I:
        The Basics&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii&quot;&gt;Developing with AngularJS - Part
        II: Dialogs and Data&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii&quot;&gt;Developing with AngularJS - Part
        III: Services&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv&quot;&gt; Developing with AngularJS - Part
        IV: Making it Pop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
    I engaged in a month-long contract with &lt;a href=&quot;http://www.travelport.com/&quot;&gt;Travelport&lt;/a&gt;
    to keep me busy in June. They hired me to develop a portal consolidation prototype, which I did with AngularJS,
    Grails and LDAP. I showcased &lt;a href=&quot;http://vimeo.com/80314102&quot;&gt;that application&lt;/a&gt; at Devoxx in November.
&lt;/p&gt;
&lt;p&gt;In September, I started a new gig with &lt;a href=&quot;http://www.johnmuirhealth.com/&quot;&gt;John Muir Health&lt;/a&gt;. I was hired
    to help with their mobile architecture, and spent my first couple months doing front-end optimizing and helping get
    their MyJMH product released. Once the release was complete, I proposed a mobile architecture and started assisting
    with the development of their mobile application.
&lt;/p&gt;
&lt;p&gt;
    We&apos;re developing the Mobile API with &lt;a href=&quot;http://projects.spring.io/spring-boot/&quot;&gt;Spring Boot&lt;/a&gt;. I wrote about
    my initial experience in &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_webapp_makeover_with_spring&quot;&gt;A Webapp Makeover
    with Spring 4 and Spring Boot&lt;/a&gt;. My contract with John Muir Health is through the end of March and I hope to
    start something new shortly after.
&lt;/p&gt;
&lt;p&gt;In October, I started &lt;a href=&quot;http://raibledesigns.com/rd/entry/writing_for_infoq&quot;&gt;Writing for InfoQ&lt;/a&gt; and I&apos;ve
    really enjoyed it so far. You can see the articles I&apos;ve written on &lt;a
            href=&quot;http://www.infoq.com/author/Matt-Raible&quot;&gt;my author page&lt;/a&gt; or by clicking the links below:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;http://www.infoq.com/articles/javaone2013-roundup&quot;&gt;JavaOne 2013 Roundup: Java 8 is Revolutionary, Java
        is back&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://www.infoq.com/news/2013/11/netty4-twitter&quot;&gt;Netty 4 Reduces GC Overhead by 5x at Twitter&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://www.infoq.com/news/2013/12/spring4&quot;&gt;Spring 4 Enhances Support for Java 8, Java EE 7, REST and
        HTML5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;speaking&quot;&gt;Speaking&lt;/h3&gt;
&lt;p&gt;I spoke at five events in 2013:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Denver JUG on &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_modern_java_web_developer&quot;&gt;The Modern Java Web
        Developer and Java Web Security&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Devoxx France on &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_france_a_great_conference&quot;&gt;Comparing JVM Web
        Frameworks and Play vs. Grails&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;HTML5 Denver on &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_bootstrap_presentation_from_html5&quot;&gt;Bootstrap&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;JavaOne: I &lt;a href=&quot;http://raibledesigns.com/rd/entry/javaone_2013 JavaOne 2013&quot;&gt;spoke for the first time&lt;/a&gt;
        and &lt;a href=&quot;http://raibledesigns.com/rd/entry/javaone_2013_videos_of_presentations&quot;&gt;received a Rock Star
            Award&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;Devoxx and a Nordic Countries Speaking Tour on &lt;a
            href=&quot;http://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries&quot;&gt;The Modern Java Web Developer&lt;/a&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It seems that folks liked my presentations since they were in &lt;a href=&quot;http://www.slideshare.net/yearinreview/mraible/ycEAAA&quot;&gt;the top 1% of most viewed on SlideShare in 2013&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
    Trish took many &lt;a href=&quot;http://raibledesigns.com/rd/entry/paris_and_iceland_a_photographers&quot;&gt;beautiful
    photos&lt;/a&gt; as part of our trip to Devoxx France.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8615035109/&quot;
       href=&quot;http://farm9.staticflickr.com/8108/8615035109_cb5d88a4ed_c.jpg&quot;
       title=&quot;Our Lady Liberty and Eiffel Tower by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8108/8615035109_cb5d88a4ed_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Our Lady Liberty and Eiffel Tower&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8615047387/&quot;
       href=&quot;http://farm9.staticflickr.com/8384/8615047387_93155b7fcc_c.jpg&quot;
       title=&quot;R&#233;cipon Quadrigas France by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8384/8615047387_93155b7fcc_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;R&#233;cipon Quadrigas France&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;


    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8616147874/&quot;
       href=&quot;http://farm9.staticflickr.com/8543/8616147874_8e0de9d7aa_c.jpg&quot;
       title=&quot;Boats Seine River Eiffel Tower by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8543/8616147874_8e0de9d7aa.jpg&quot; width=&quot;500&quot;
            alt=&quot;Boats Seine River Eiffel Tower&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8616151686/&quot;
       href=&quot;http://farm9.staticflickr.com/8266/8616151686_89d7bfdf75_c.jpg&quot;
       title=&quot;Statue Of LaFayette Cours La Reine Paris Frances by McGinityPhoto, on Flickr&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8266/8616151686_89d7bfdf75.jpg&quot;
                                             width=&quot;500&quot;
                                             alt=&quot;Statue Of LaFayette Cours La Reine Paris Frances&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
    We stopped in Iceland on the way home to see the &lt;a
        href=&quot;http://www.flickr.com/photos/mcginityphoto/sets/72157633161145490/&quot;&gt;Northern
    Lights&lt;/a&gt;.

&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8617531184/&quot;
       href=&quot;http://farm9.staticflickr.com/8262/8617531184_a688cb0f88_c.jpg&quot;
       title=&quot;Northern Lights 19 by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8262/8617531184_a688cb0f88.jpg&quot; width=&quot;500&quot; alt=&quot;Northern Lights 19&quot;
            style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id=&quot;projects&quot;&gt;Projects&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;AppFuse:&lt;/strong&gt; I released &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_light_2_2_1&quot;&gt;AppFuse Light
    2.2.1&lt;/a&gt; in January and
    switched AppFuse from JSF&apos;s &lt;a href=&quot;http://raibledesigns.com/rd/entry/switching_appfuse_from_myfaces_to&quot;&gt;MyFaces to
        PrimeFaces&lt;/a&gt; in February. I blogged about &lt;a
            href=&quot;http://raibledesigns.com/rd/entry/integrating_gwt_into_appfuse&quot;&gt;AppFuse&apos;s GWT integration&lt;/a&gt; in March
    and
    celebrated the project&apos;s &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_10_year_appfuse&quot;&gt;10 year anniversary in
        April&lt;/a&gt;. AppFuse 3.0 was released &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_3_0_released&quot;&gt;just before
        Christmas&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
    &lt;strong&gt;Roller:&lt;/strong&gt; I didn&apos;t contribute any code to the Roller project in 2013, but I did &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/responsive_design_with_css_media&quot;&gt;make this site responsive with CSS
    media queries&lt;/a&gt;.
&lt;/p&gt;
&lt;h2 id=&quot;personal&quot;&gt;Personal&lt;/h2&gt;
&lt;p&gt;
    Trish and I started our world travels in March with a trip to &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/magnificent_mexico&quot;&gt;Magnificent Mexico&lt;/a&gt;. We had a wonderful time
    playing with old friends and renting a house on the beach.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a data-url=&quot;http://www.flickr.com/photos/mraible/8491989219/&quot;
       href=&quot;https://farm9.staticflickr.com/8096/8491989219_5150a05565_c.jpg&quot; title=&quot;The Pool by mraible, on Flickr&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8096/8491989219_5150a05565_q.jpg&quot;
                                             width=&quot;150&quot; alt=&quot;The Pool&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a data-url=&quot;http://www.flickr.com/photos/mraible/8491993847/&quot;
       href=&quot;https://farm9.staticflickr.com/8366/8491993847_a77a00081d_c.jpg&quot; title=&quot;My Love and I by mraible, on Flickr&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8366/8491993847_a77a00081d_q.jpg&quot;
                                             width=&quot;150&quot; alt=&quot;My Love and I&quot;
                                             style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
    &lt;a data-url=&quot;http://www.flickr.com/photos/mraible/8491997899/&quot;
       href=&quot;https://farm9.staticflickr.com/8388/8491997899_190ec7bfd0_c.jpg&quot;
       title=&quot;Julie at Sunset by mraible, on Flickr&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8388/8491997899_190ec7bfd0_q.jpg&quot;
                                             width=&quot;150&quot; alt=&quot;Julie at Sunset&quot;
                                             style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8525669404/&quot;
       href=&quot;http://farm9.staticflickr.com/8097/8525669404_4fc25f6bb8_c.jpg&quot;
       title=&quot;Chacala Sunset by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8097/8525669404_4fc25f6bb8.jpg&quot; width=&quot;500&quot;
            alt=&quot;Chacala Sunset&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8524458091/&quot;
       href=&quot;http://farm9.staticflickr.com/8368/8524458091_721ed22fe7_c.jpg&quot;
       title=&quot;What a great posse! by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8368/8524458091_721ed22fe7_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;What a great posse!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8525571064/&quot;
       href=&quot;http://farm9.staticflickr.com/8243/8525571064_da036d44a7_c.jpg&quot;
       title=&quot;The Chacala gang! by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8243/8525571064_da036d44a7_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;The Chacala gang!&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

    &lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8525569828/&quot;
       href=&quot;http://farm9.staticflickr.com/8521/8525569828_1eb8850118_c.jpg&quot;
       title=&quot;YAY Chacalas! by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8521/8525569828_1eb8850118_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;YAY Chacalas!&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;

&lt;p&gt;Our trip to &lt;a href=&quot;http://raibledesigns.com/rd/entry/paris_and_iceland_a_photographers&quot;&gt;Paris and Iceland&lt;/a&gt; was a
    dream come true for Trish. Watching the Aurora Borealis dance in the sky is something I&apos;ll never forget.&lt;/p&gt;
&lt;p&gt;
    To end the ski season in April, we &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_trifecta_2013&quot;&gt;completed the
    trifecta&lt;/a&gt; (3 ski resorts in 3 days) with
    a fun family weekend.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm9.staticflickr.com/8246/8662516874_f2400ba9c6_c.jpg&quot; title=&quot;13&amp;quot; Powder Day at Copper!&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8246/8662516874_f2400ba9c6.jpg&quot;
                                             width=&quot;500&quot; alt=&quot;13&amp;quot; Powder Day at Copper!&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
    The last week of the ski season was epic, as I described in a &lt;a href=&quot;http://raibledesigns.com/rd/entry/life_update1&quot;&gt;life
    update&lt;/a&gt;.
&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;
    The last week in Winter Park was the best skiing of the year. The first (Tuesday) afternoon, I started skiing around
    1pm and it snowed all afternoon - resulting in several inches by the end of the day. The next day was smooth and
    empty, followed by a day of deep powder and knee-deep runs down Eagle Wind. I had one of the best runs of my life
    that day.
&lt;/p&gt;

&lt;p&gt;
    I wrote about the &apos;66 Bus Project and how I removed it from Motorworks Restorations after six years.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm9.staticflickr.com/8522/8615924358_c863705496_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/8615924358/&quot; title=&quot;Sweet Stance by mraible, on Flickr&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8522/8615924358_c863705496_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Sweet Stance&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;http://farm9.staticflickr.com/8263/8615925190_80b016767b_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/8615925190/&quot; title=&quot;Love That Boy by mraible, on Flickr&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8263/8615925190_80b016767b_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Love That Boy&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7418/9032950835_57cf83c68f_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/9032950835/&quot; title=&quot;Leaving Motorworks by mraible, on Flickr&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7418/9032950835_57cf83c68f_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Leaving Motorworks&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;http://farm4.staticflickr.com/3679/9035175304_773922f83b_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/9035175304/&quot;
       title=&quot;Time to get this thing done! by mraible, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3679/9035175304_773922f83b_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Time to get this thing done!&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    We spent the first weekends in June &lt;a href=&quot;http://raibledesigns.com/rd/entry/father_s_day_weekend_on&quot;&gt;on the
    Colorado River&lt;/a&gt;.
    One of the biggest highlights for me was catching a 16&quot; Trout, right after Trish saw it jump and told me exactly
    where to cast my line. The fact that I was able to cast it to the precise spot was cool enough, but getting the fish
    on my line moments later was exhilarating. Shortly after, a majestic Bald Eagle flew over us and we all dropped our
    jaws in amazement.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a data-href=&quot;http://www.flickr.com/photos/mraible/9071726423/&quot;
       href=&quot;http://farm8.staticflickr.com/7332/9071726423_3d1faf669c_c.jpg&quot; title=&quot;My Crew&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7332/9071726423_3d1faf669c_m.jpg&quot;
                                             width=&quot;240&quot; alt=&quot;My Crew&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a data-href=&quot;http://www.flickr.com/photos/mraible/9071727141/&quot;
       href=&quot;http://farm4.staticflickr.com/3668/9071727141_b3ce9e3286_c.jpg&quot; title=&quot;Roasting Hot Dogs&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3668/9071727141_b3ce9e3286_m.jpg&quot;
                                             width=&quot;240&quot; alt=&quot;Roasting Hot Dogs&quot;
                                             style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a data-href=&quot;http://www.flickr.com/photos/mraible/9071648965/&quot;
       href=&quot;http://farm4.staticflickr.com/3725/9071648965_b142ae7f05_c.jpg&quot; title=&quot;16 Incher&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3725/9071648965_b142ae7f05.jpg&quot;
                                             width=&quot;500&quot; alt=&quot;16 Incher&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    Trish and I started a two month sabbatical in July to &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/matrimony_in_montana&quot;&gt;get married in proper
    fashion&lt;/a&gt; in the town I grew up in. She looked absolutely stunning.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7431/9624418537_21f624efcc_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9624418537/&quot; title=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7431/9624418537_21f624efcc.jpg&quot;
                                             width=&quot;500&quot; alt=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;During the ceremony, I surprised her with a 162-page
    book I built with my words and her pictures.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm4.staticflickr.com/3775/9624416155_92cb67d722_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9624416155/&quot; title=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3775/9624416155_92cb67d722_m.jpg&quot;
                                             width=&quot;240&quot; alt=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm4.staticflickr.com/3756/9627642486_c1c3cca8b2_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9627642486/&quot; title=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3756/9627642486_c1c3cca8b2_m.jpg&quot;
                                             width=&quot;240&quot; alt=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
                                             style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    When asked if I&apos;d take her as my lawfully-wedded wife, I shouted as &quot;YES!&quot; at the top of my lungs. Trish agreed enthusiastically and we were pronounced husband and wife.
&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7348/9624417367_a672878836_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9624417367/&quot; title=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7348/9624417367_a672878836_m.jpg&quot;
                                             width=&quot;240&quot; alt=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm8.staticflickr.com/7337/9624416939_a67bb07e70_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9624416939/&quot; title=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7337/9624416939_a67bb07e70_m.jpg&quot;
                                             width=&quot;240&quot; alt=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
                                             style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

    &lt;a href=&quot;http://farm8.staticflickr.com/7442/9627648458_38de03b06a_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9627648458/&quot; title=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7442/9627648458_38de03b06a.jpg&quot;
                                             width=&quot;500&quot; alt=&quot;Raible &amp;amp; McGinity Wedding Photos&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Our wedding week was an unbelievably fun experience with many, many friends and family. We look forward to
    celebrating it over and over again as long as we live.&lt;/p&gt;
&lt;p&gt;
    We had a day of rest following the wedding, then drove back to Denver to begin our &apos;round-the-world honeymoon
    without phones. We spent a month traveling to &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/around_the_world_honeymoon_1st&quot;&gt;Ireland&lt;/a&gt;,
    &lt;a href=&quot;http://raibledesigns.com/rd/entry/around_the_world_honeymoon_2nd&quot;&gt;Italy&lt;/a&gt;,
    &lt;a href=&quot;http://raibledesigns.com/rd/entry/around_the_world_honeymoon_3rd&quot;&gt;Thailand&lt;/a&gt; and
    &lt;a href=&quot;http://raibledesigns.com/rd/entry/around_the_world_honeymoon_last&quot;&gt;Fiji&lt;/a&gt;. It was a wonderful journey
    and we greatly enjoyed all the people, places and experiences.

&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;

    &lt;a href=&quot;http://farm8.staticflickr.com/7342/10322700916_88449ec9ea_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/10322700916/&quot;
       title=&quot;Ballynahinch Castle Ireland by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7342/10322700916_88449ec9ea.jpg&quot; width=&quot;500&quot;
            alt=&quot;Ballynahinch Castle Ireland&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm8.staticflickr.com/7362/9902197834_90ef009fd8_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9902197834/&quot; rel=&quot;lightbox[2013yearinreview]&quot; title=&quot;View from hills
	just outside of La Morra Italy! by McGinityPhoto, on Flickr&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7362/9902197834_90ef009fd8.jpg&quot; width=&quot;500&quot;
            alt=&quot;View from hills just outside of La Morra Italy!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a data-href=&quot;http://www.flickr.com/photos/mcginityphoto/10076904953/&quot;
       href=&quot;http://farm3.staticflickr.com/2871/10076904953_34cb763f5c_c.jpg&quot;
       title=&quot;View of Ang Thong National Marine Park by McGinityPhoto, on Flickr&quot;
       rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2871/10076904953_34cb763f5c.jpg&quot;
                                             width=&quot;500&quot; alt=&quot;View of Ang Thong National Marine Park&quot;
                                             style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;http://farm6.staticflickr.com/5343/10177404544_1358494510_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/10177404544/&quot;
       title=&quot;Chillin&apos; in Qamea Fiji by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm6.staticflickr.com/5343/10177404544_1358494510.jpg&quot; width=&quot;500&quot; alt=&quot;Chillin&apos; in Qamea Fiji&quot;
            style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;We arrived back in the US at the end of August, just in time to celebrate &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/happy_birthday_jack5&quot;&gt;Jack&apos;s 9th birthday&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
    In September, we got loud at
    the Broncos Home Opener, and experienced beautiful Colorado fall weather with trips to Estes Park and Aspen. In
    October, we
    traveled to Minnesota for a best friend&apos;s wedding. We also visited Pennsylvania to spend some time with Trish&apos;s parents.
&lt;/p&gt;
&lt;p&gt;
    &lt;a href=&quot;http://farm4.staticflickr.com/3731/10424172085_fbf8ebb173_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/10424172085&quot;
       title=&quot;Right 3/4&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3731/10424172085_fbf8ebb173_t.jpg&quot; width=&quot;100&quot; alt=&quot;Right 3/4&quot;
            style=&quot;border: 1px solid black&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://raibledesigns.com/rd/entry/we_bought_a_ski_bus&quot;&gt;We Bought a Ski Bus&lt;/a&gt; in October and celebrated &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/happy_birthday_abbie7&quot;&gt;Abbie&apos;s birthday&lt;/a&gt; in November. Shortly after,
    we
    departed on a whirlwind &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries&quot;&gt;six-country
    speaking tour&lt;/a&gt; in Europe. We visited 14 countries in 2013. 
    &lt;!-- Mexico, France, Iceland, Ireland, Britain, Italy, Thailand, Fiji, Holland, Belgium, Sweden, Norway, Finland, Copenhagen --&gt;
&lt;/p&gt;
&lt;p&gt;
    In early December, we flew to
    Idaho to &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_syncro_rescue_road_trip&quot;&gt;pickup our new Syncro&lt;/a&gt;. We
    anticipated a few VW Adventures when we bought the bus and haven&apos;t been disappointed.


&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

    &lt;a href=&quot;http://farm4.staticflickr.com/3771/11318813023_0a33a869ef_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/11318813023/&quot;
       title=&quot;Let the VW adventures begin! by mraible, on Flickr&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3771/11318813023_0a33a869ef_n.jpg&quot; width=&quot;320&quot;
            alt=&quot;Let the VW adventures begin!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;

    The Syncro drove
    back to Denver just fine, but started experiencing cooling issues driving around town. We got it fixed and drove
    to Montana for Christmas. We broke down on Christmas Eve in Bozeman, tried to fix it, but eventually gave up and
    rented a car. Straightaway Motors replaced the thermostat over the next week and we were able to drive it back to
    Denver with no issues. It started overheating again a couple days later. We&apos;ve only recently got it running smoothly, with help from &lt;a
        href=&quot;http://www.rockymountainwesty.com/&quot;&gt;Rocky Mountain Westy&lt;/a&gt;. You can read the full story on &lt;a
        href=&quot;http://www.thesamba.com/vw/forum/viewtopic.php?t=580347&quot;&gt;
    thesamba.com&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
    Christmas Vacation was spent at The Raible Homestead, enjoying my parents new retirement cabin and a
    &lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/sets/72157639816640594/&quot;&gt;sweet sledding hill&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm3.staticflickr.com/2845/11973497666_90c0414f3d_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11973497666/&quot;
       title=&quot;Yee Haw!&quot; rel=&quot;lightbox[2013yearinreview]&quot;&gt;&lt;img
            src=&quot;//farm3.staticflickr.com/2845/11973497666_90c0414f3d.jpg&quot; width=&quot;500&quot;
            alt=&quot;Yee Haw!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;We finished the year skiing at &lt;a href=&quot;http://skiwhitefish.com/&quot;&gt;Big Mountain&lt;/a&gt;, where I learned how to downhill
    ski in grade school.&lt;/p&gt;

&lt;h2 id=&quot;2014&quot;&gt;2014&lt;/h2&gt;

&lt;p&gt;My goal for 2014 is singular: &lt;em&gt;finish The Bus&lt;/em&gt;. I finally found the restoration shop I wish I would&apos;ve found years ago.
    They&apos;re
    very quick and efficient, and send me daily (picture) updates of their progress. With any luck, it&apos;ll be done in a
    couple months (sound
    &lt;a href=&quot;http://raibledesigns.com/rd/entry/when_is_the_bus_gonna&quot;&gt;familiar&lt;/a&gt;?).
&lt;/p&gt;
&lt;p&gt;Last year, I wanted to &lt;em&gt;slow down&lt;/em&gt; and I feel like I did that on our honeymoon. My wife scoffs at that notion.
    &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot; /&gt; This year, I
    hope to simply take more time to do things, instead of trying to cram many things into tight timelines.&lt;/p&gt;
&lt;p&gt;
    I plan to spend less time traveling and speaking at conferences and more time at Volkswagen shows. With our 4x4 VW
    Syncro, we hope to camp, raft and spend quite a bit of time in the Rocky Mountains. We also hope to take the busses
    to a few art shows to show off Trish&apos;s majestic photos.
&lt;/p&gt;
&lt;p&gt;Professionally, I hope to continue developing HTML5 apps and APIs with JavaScript, CSS, Java and Groovy. I might dive back
    into Scala and I&apos;m intrigued by Node.js and Spring Boot. Since I won&apos;t be doing as many conferences, the
    technologies I
    use will likely be driven by client engagements. I expect 2014 to be a big year for HTTP/2.
&lt;/p&gt;
&lt;p&gt;Last year, and the year before, we watched the Broncos fail miserably in the NFL playoffs. This year, they&apos;re in the
    Super Bowl.
    With a kick-ass Ski Bus, a Porsche Bus on the way, awesome kids and a great wife - 2014 is destined to be
    spectacular. I hope the Broncos are too! &lt;img src=&quot;https://raibledesigns.com/images/smileys/smile.gif&quot; class=&quot;smiley&quot; alt=&quot;:)&quot; title=&quot;:)&quot; /&gt;
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/you_shouldn_t_have_to</id>
        <title type="html">You shouldn&apos;t have to worry about front end optimization</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/you_shouldn_t_have_to"/>
        <published>2014-01-16T13:49:03-07:00</published>
        <updated>2014-01-16T20:02:51-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="pagespeed" scheme="http://roller.apache.org/ns/tags/" />
        <category term="optimization" scheme="http://roller.apache.org/ns/tags/" />
        <category term="http" scheme="http://roller.apache.org/ns/tags/" />
        <category term="concatenation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="http2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="minification" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;After writing yesterday&apos;s article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for&quot;&gt;optimizing AngularJS apps with Grunt&lt;/a&gt; I received an &lt;a href=&quot;https://twitter.com/markj9/statuses/423540086873927682&quot;&gt;interesting reply from @markj9&lt;/a&gt; on Twitter.
&lt;div style=&quot;margin-left: 30px&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/mraible&quot;&gt;@mraible&lt;/a&gt; that might be a bad thing! you should go listen to Igor..&amp;#10;&lt;a href=&quot;http://t.co/HL2mho7R86&quot;&gt;http://t.co/HL2mho7R86&lt;/a&gt;&lt;/p&gt;&amp;mdash; Mark (@markj9) &lt;a href=&quot;https://twitter.com/markj9/statuses/423540086873927682&quot;&gt;January 15, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;
I clicked on the &lt;a href=&quot;http://rubyrogues.com/135-rr-http-2-0-with-ilya-grigorik/&quot;&gt;provided link&lt;/a&gt;, listened to the podcast (RR HTTP 2.0 with &lt;a href=&quot;https://twitter.com/igrigorik&quot;&gt;Ilya Grigorik&lt;/a&gt;) and discovered some juicy bits at around 27:00. The text below is from the podcast&apos;s transcript at the bottom of the page. &lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;After writing yesterday&apos;s article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for&quot;&gt;optimizing AngularJS apps with Grunt&lt;/a&gt; I received an &lt;a href=&quot;https://twitter.com/markj9/statuses/423540086873927682&quot;&gt;interesting reply from @markj9&lt;/a&gt; on Twitter.
&lt;div style=&quot;margin-left: 30px&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/mraible&quot;&gt;@mraible&lt;/a&gt; that might be a bad thing! you should go listen to Igor..&amp;#10;&lt;a href=&quot;http://t.co/HL2mho7R86&quot;&gt;http://t.co/HL2mho7R86&lt;/a&gt;&lt;/p&gt;&amp;mdash; Mark (@markj9) &lt;a href=&quot;https://twitter.com/markj9/statuses/423540086873927682&quot;&gt;January 15, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;
I clicked on the &lt;a href=&quot;http://rubyrogues.com/135-rr-http-2-0-with-ilya-grigorik/&quot;&gt;provided link&lt;/a&gt;, listened to the podcast (RR HTTP 2.0 with &lt;a href=&quot;https://twitter.com/igrigorik&quot;&gt;Ilya Grigorik&lt;/a&gt;) and discovered some juicy bits around 27:00. The text below is from the podcast&apos;s transcript at the bottom of the page. &lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;&lt;b&gt;AVDI:&amp;nbsp; &lt;/b&gt;Yeah. If I pushed back a little, it&#8217;s only because I just wonder, are the improvements coming solely from the perspective of a Google or Facebook, or are they also coming from the perspective of the hundreds of thousands of people developing smaller applications and websites?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;ILYA:&amp;nbsp; &lt;/b&gt;Yeah. So, I think it&#8217;s the latter, which is to say the primary objective here is actually to make the browsers faster. So, if you open a webpage over HTTP 2.0, it should load faster. And that&#8217;s one kind of population that will benefit from it. And the second one is actually developers. We want to make developing web applications easier. You shouldn&#8217;t have to worry about things like spriting and concatenating files and doing all this stuff, domain sharding and all this other mess, which are just completely unnecessary and actually makes performance worse in many cases because each one of those has negative repercussions.&lt;/p&gt;
&lt;p&gt;Things like, let&#8217;s say concatenating your style sheets or JavaScript. Why do we do that? Well, we do that because we want to reduce the number of requests because we have this connection limit with HTTP 1.0. But the downside then is let&#8217;s say you&#8217;ve &#8212; actually Rails does this, you concatenate all of your CSS into one giant bundle. Great, we reduced the number of requests. We can download it faster. Awesome. Then you go in and your designer changes one line from whatever, the background color from blue to red. And now, you have to download the entire bundle. You have to invalidate that certain file and you need to download the whole thing.&lt;/p&gt;
&lt;p&gt;Chances are, if you&#8217;re doing sound software development today, you already have things split into modules. Like here is my base.css, here is my other page.css. Here are my JavaScript modules. And there&#8217;s no reason why we need to concatenate those into one giant bundle and invalidate on every request. This is something that we&#8217;ve automated to some degree, but it&#8217;s unnecessary. And it actually slows down the browser, too, in unexpected ways.&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;We recently realized that serving these large JavaScript files actually hurts your performance because we can&#8217;t execute the JavaScript until we get the entire file. So, if you actually split it into a bunch of smaller chunks, it actually allows us to execute them incrementally, one chunk at a time. And that makes the site faster. Same thing for CSS, splitting all that stuff. And this may sound trivial, but in practice, it&#8217;s actually a giant pain for a lot of applications.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The conversation goes on to talk about how this change in thinking is largely caused by the fact that bandwidth is no longer a problem, latency is.&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;&lt;b&gt;JAMES:&amp;nbsp; &lt;/b&gt;That seems really, really weird to me though. Everything has been moving in that direction and you&#8217;re saying our data on that&#8217;s just wrong. It&#8217;s not faster?&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;&lt;b&gt;ILYA:&amp;nbsp;&lt;/b&gt; Yeah. Part of it is the connectivity profiles are also changing. So when we first started advocating for those sorts of changes back in, whatever it was, 2005, 2007, when this stuff started showing up, the connection speeds were different. We were primarily maybe DSL was state of the art and bandwidth was really an issue there. So, you spend more time just downloading resources. Now that bandwidth is much less of an issue, latency is the problem. And because of that, these &#8220;best practices&#8221; are changing. And with HTTP 2.0, you actually don&#8217;t have to do that at all. And in fact, some of those things will actually hurt your performance.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As you can imagine, this news is quite surprising to me. Optimizations like gzipping and expires headers will continue to be important, but concatenating and minifying might become a &quot;worst&quot; practice? That seems crazy, especially when the tools I test with (YSlow and Page Speed browser plugins) give me higher grades for minifying and reducing the number of requests. 
&lt;/p&gt;
&lt;p&gt;
The good news is there&apos;s lots of good things coming in HTTP 2.0, and you can use it today. &lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;&lt;b&gt;ILYA:&amp;nbsp; &lt;/b&gt;... any application that&#8217;s delivered over HTTP 1.0 will work over HTTP 2.0. There&#8217;s nothing changing there. The semantics are all the same. It could be the case that certain optimizations that you&#8217;ve done for HTTP 1.1 will actually hurt in HTTP 2.0. And when I say hurt, in practice at least from what I&#8217;ve seen today, it doesn&#8217;t mean that your site is actually going to be slower. It&#8217;s just that it won&#8217;t be any better than HTTP 1.0.&lt;/p&gt;
&lt;p&gt;Upgrading my servers to support HTTP 2.0 brings up an interesting dilemma. How do I measure that non-minified and non-concatenated assets are actually better? Do I just benchmark page load times or are there better tools for proving things are faster and better?</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for</id>
        <title type="html">Using Grunt with AngularJS for Front End Optimization</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for"/>
        <published>2014-01-15T12:15:52-07:00</published>
        <updated>2014-01-15T22:11:46-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="grunt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="pagespeed" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yslow" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I&apos;m passionate about front end optimization and have been for years. My original inspiration was Steve Souders and his &lt;a href=&quot;http://raibledesigns.com/rd/entry/oscon_2008_even_faster_web&quot;&gt;Even Faster Web Sites talk at OSCON 2008&lt;/a&gt;. Since then, I&apos;ve &lt;a href=&quot;http://raibledesigns.com/rd/entry/javascript_and_css_concatenation&quot;&gt;optimized this blog&lt;/a&gt;, made it even faster &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;with a new design&lt;/a&gt;, doubled the speed of several apps for clients and showed how to &lt;a href=&quot;http://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with&quot;&gt;make AppFuse faster&lt;/a&gt;. As part of my &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries&quot;&gt;Devoxx 2013 presentation&lt;/a&gt;, I showed &lt;a href=&quot;https://vimeo.com/mraible/page-speed-demo&quot;&gt;how to do page speed optimization in a Java webapp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I developed a couple AngularJS apps last year. To concat and minify their stylesheets and scripts, I used mechanisms that already existed in the projects. On one project, it was Ant and its &lt;a href=&quot;https://ant.apache.org/manual/Tasks/concat.html&quot;&gt;concat task&lt;/a&gt;. On the other, it was part of a Grails application, so I used the &lt;a href=&quot;http://grails.org/plugin/resources&quot;&gt;resources&lt;/a&gt; and &lt;a href=&quot;http://grails.org/plugin/yui-minify-resources&quot;&gt;yui-minify-resources&lt;/a&gt; plugins.
&lt;/p&gt;
&lt;p&gt;The Angular project I&apos;m working on now will be published on a web server, as well as bundled in an iOS native app. Therefore, I turned to &lt;a href=&quot;http://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt; to do the optimization this time. I found it to be quite simple, once I figured out &lt;a href=&quot;http://stackoverflow.com/questions/21056767/angular-and-grunt&quot;&gt;how to make it work with Angular&lt;/a&gt;. Based on my findings, I submitted a &lt;a href=&quot;https://github.com/angular/angular-seed/pull/131&quot;&gt;pull request to add Grunt to angular-seed&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;Below are the steps I used to add Grunt to my Angular project.&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;I&apos;m passionate about front end optimization and have been for years. My original inspiration was Steve Souders and his &lt;a href=&quot;http://raibledesigns.com/rd/entry/oscon_2008_even_faster_web&quot;&gt;Even Faster Web Sites talk at OSCON 2008&lt;/a&gt;. Since then, I&apos;ve &lt;a href=&quot;http://raibledesigns.com/rd/entry/javascript_and_css_concatenation&quot;&gt;optimized this blog&lt;/a&gt;, made it even faster &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;with a new design&lt;/a&gt;, doubled the speed of several apps for clients and showed how to &lt;a href=&quot;http://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with&quot;&gt;make AppFuse faster&lt;/a&gt;. As part of my &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries&quot;&gt;Devoxx 2013 presentation&lt;/a&gt;, I showed &lt;a href=&quot;https://vimeo.com/mraible/page-speed-demo&quot;&gt;how to do page speed optimization in a Java webapp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I developed a couple AngularJS apps last year. To concat and minify their stylesheets and scripts, I used mechanisms that already existed in the projects. On one project, it was Ant and its &lt;a href=&quot;https://ant.apache.org/manual/Tasks/concat.html&quot;&gt;concat task&lt;/a&gt;. On the other, it was part of a Grails application, so I used the &lt;a href=&quot;http://grails.org/plugin/resources&quot;&gt;resources&lt;/a&gt; and &lt;a href=&quot;http://grails.org/plugin/yui-minify-resources&quot;&gt;yui-minify-resources&lt;/a&gt; plugins.
&lt;/p&gt;
&lt;p&gt;The Angular project I&apos;m working on now will be published on a web server, as well as bundled in an iOS native app. Therefore, I turned to &lt;a href=&quot;http://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt; to do the optimization this time. I found it to be quite simple, once I figured out &lt;a href=&quot;http://stackoverflow.com/questions/21056767/angular-and-grunt&quot;&gt;how to make it work with Angular&lt;/a&gt;. Based on my findings, I submitted a &lt;a href=&quot;https://github.com/angular/angular-seed/pull/131&quot;&gt;pull request to add Grunt to angular-seed&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;Below are the steps I used to add Grunt to my Angular project.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Grunt&apos;s command line interface with &quot;sudo npm install -g grunt-cli&quot;.&lt;/li&gt;
&lt;li&gt;Edit package.json to include a version number (e.g. &quot;version&quot;: &quot;1.0.0&quot;).&lt;/li&gt;
&lt;li&gt;Add Grunt plugins in package.json to do concat/minify/asset versioning:
&lt;pre class=&quot;brush: js&quot;&gt;
    &quot;grunt&quot;: &quot;~0.4.1&quot;,
    &quot;grunt-contrib-concat&quot;: &quot;~0.3.0&quot;,
    &quot;grunt-contrib-uglify&quot;: &quot;~0.2.7&quot;,
    &quot;grunt-contrib-cssmin&quot;: &quot;~0.7.0&quot;,
    &quot;grunt-usemin&quot;: &quot;~2.0.2&quot;,
    &quot;grunt-contrib-copy&quot;: &quot;~0.5.0&quot;,
    &quot;grunt-rev&quot;: &quot;~0.1.0&quot;,
    &quot;grunt-contrib-clean&quot;: &quot;~0.5.0&quot;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Run &quot;sudo npm install&quot; to install the project&apos;s dependencies.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;Gruntfile.js&lt;/code&gt; that runs all the plugins.
&lt;pre class=&quot;brush: js&quot;&gt;
module.exports = function (grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON(&apos;package.json&apos;),

        clean: [&quot;dist&quot;, &apos;.tmp&apos;],

        copy: {
            main: {
                expand: true,
                cwd: &apos;app/&apos;,
                src: [&apos;**&apos;, &apos;!js/**&apos;, &apos;!lib/**&apos;, &apos;!**/*.css&apos;],
                dest: &apos;dist/&apos;
            },
            shims: {
                expand: true,
                cwd: &apos;app/lib/webshim/shims&apos;,
                src: [&apos;**&apos;],
                dest: &apos;dist/js/shims&apos;
            }
        },

        rev: {
            files: {
                src: [&apos;dist/**/*.{js,css}&apos;, &apos;!dist/js/shims/**&apos;]
            }
        },

        useminPrepare: {
            html: &apos;app/index.html&apos;
        },

        usemin: {
            html: [&apos;dist/index.html&apos;]
        },

        uglify: {
            options: {
                report: &apos;min&apos;,
                mangle: false
            }
        }
    });

    grunt.loadNpmTasks(&apos;grunt-contrib-clean&apos;);
    grunt.loadNpmTasks(&apos;grunt-contrib-copy&apos;);
    grunt.loadNpmTasks(&apos;grunt-contrib-concat&apos;);
    grunt.loadNpmTasks(&apos;grunt-contrib-cssmin&apos;);
    grunt.loadNpmTasks(&apos;grunt-contrib-uglify&apos;);
    grunt.loadNpmTasks(&apos;grunt-rev&apos;);
    grunt.loadNpmTasks(&apos;grunt-usemin&apos;);

    // Tell Grunt what to do when we type &quot;grunt&quot; into the terminal
    grunt.registerTask(&apos;default&apos;, [
        &apos;copy&apos;, &apos;useminPrepare&apos;, &apos;concat&apos;, &apos;uglify&apos;, &apos;cssmin&apos;, &apos;rev&apos;, &apos;usemin&apos;
    ]);
};
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Add comments to app/index.html so usemin knows what files to process. The comments are the important part, your files will likely be different.
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;!-- build:css css/app-name.min.css --&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;lib/bootstrap/bootstrap.min.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;lib/font-awesome/font-awesome.min.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;lib/toaster/toaster.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;css/app.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;css/custom.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;css/responsive.css&quot;/&amp;gt;
&amp;lt;!-- endbuild --&amp;gt;
...

&amp;lt;!-- build:js js/app-name.min.js --&amp;gt;
&amp;lt;script src=&quot;lib/jquery/jquery-1.10.2.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/bootstrap/bootstrap.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular-animate.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular-cookies.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular-resource.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular-route.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/fastclick.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/toaster/toaster.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/webshim/modernizr.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/webshim/polyfiller.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/services.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/controllers.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/filters.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/directives.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- endbuild --&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A couple of things to note: 1) the &lt;em&gt;copy&lt;/em&gt; task copies the &quot;shims&quot; directory from &lt;a href=&quot;http://afarkas.github.io/webshim/demos/&quot;&gt;Webshims lib&lt;/a&gt; because it loads files dynamically and 2) setting &quot;mangle: false&quot; on the &lt;em&gt;uglify&lt;/em&gt; task is necessary for Angular&apos;s dependency injection to work. I tried to use &lt;a href=&quot;https://npmjs.org/package/grunt-ngmin&quot;&gt;grunt-ngmin&lt;/a&gt; with uglify and had no luck.&lt;/p&gt;
&lt;p&gt;After making these changes, I&apos;m able to run &quot;grunt&quot; and get an optimized version of my app in the &quot;dist&quot; folder of my project. For development, I continue to run the app from my &quot;app&quot; folder, so I don&apos;t currently have a need for watching and processing assets on-the-fly. That could change if I start using LESS or CoffeeScript.
&lt;/p&gt;
&lt;p&gt;The results speak for themselves: from 27 requests to 5 on initial load, and only 3 requests for less than 2K after that.&lt;/p&gt;
&lt;table class=&quot;comparison&quot; style=&quot;max-width: 600px&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;YSlow&lt;/th&gt;
&lt;th&gt;Page Speed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No optimization&lt;/td&gt;
&lt;td&gt;75 &lt;div style=&quot;float: right&quot;&gt;27 HTTP requests / 464K&lt;/div&gt;&lt;/td&gt;
&lt;td&gt;55/100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache optimization (gzip and expires headers)&lt;/td&gt;
&lt;td&gt;89
&lt;div style=&quot;float: right&quot;&gt;
initial load: 26 requests / 166K&lt;br/&gt;
primed cache: 4 requests / 40K 
&lt;/div&gt;
&lt;/td&gt;
&lt;td&gt;88/100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache + concat/minified/versioned files&lt;/td&gt;
&lt;td&gt;98
&lt;div style=&quot;float: right&quot;&gt;
initial load: 5 requests / 136K&lt;br/&gt;
primed cache: 3 requests / 1.4K
&lt;/div&gt;
&lt;/td&gt;
&lt;td&gt;93/100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Andreas Andreou has a &lt;a href=&quot;https://twitter.com/andyhot/status/423571136538877952&quot;&gt;nice tip&lt;/a&gt; on how to reduce the LOC in this example.
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add &quot;matchdep&quot; as a dependency in package.json (or run &quot;sudo npm install matchdep --save-dev&quot;).
&lt;pre class=&quot;brush: js; gutter: false&quot;&gt;
&quot;matchdep&quot;: &quot;~0.3.0&quot;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Replace all the &lt;code&gt;grunt.loadNpmTasks(...)&lt;/code&gt; calls with the following:
&lt;pre class=&quot;brush: js; gutter: false&quot;&gt;
require(&apos;matchdep&apos;).filterDev(&apos;grunt-*&apos;).forEach(grunt.loadNpmTasks);
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thanks Andreas!&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries</id>
        <title type="html">Devoxx 2013 + a Nordic Countries Speaking Tour</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries"/>
        <published>2013-11-28T12:07:26-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="css" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx" scheme="http://roller.apache.org/ns/tags/" />
        <category term="nordea" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="avegagroup" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jvm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webdevelopment" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7452/11089788834_b7541d335d_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/11089788834/&quot;
       title=&quot;Trish at Pelgrom by mraible, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;http://farm8.staticflickr.com/7452/11089788834_b7541d335d_t.jpg&quot; width=&quot;100&quot;
            alt=&quot;Trish at Pelgrom&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
    Two weeks ago, Trish and I boarded a flight for one of our favorite conferences: &lt;a
        href=&quot;http://devoxx.be/&quot;&gt;Devoxx&lt;/a&gt;. After a brief layover in Frankfurt, we arrived in Amsterdam and took a train to Antwerp. Within hours, we&apos;d settled into our hotel near the center of Antwerp and strolled over to the
    dungeonous, yet cozy, &lt;a href=&quot;http://www.pelgrom.be/&quot;&gt;Pelgrom restaurant&lt;/a&gt;. We were hoping for a delicious
    dinner, but found much more. We
    ran into James Ward, Dick Wall and a number of other enthusiastic speakers from the conference. Since I had to speak
    the next day, we didn&apos;t stay long, but we did share a number of laughs with some great people.
&lt;/p&gt;
&lt;p&gt;
    Tuesday (November 12), was a University Day at Devoxx, and I had my talk that afternoon. I spent a couple hours
    finishing up my talk
    that morning, then grabbed a taxi to head to the conference. I was honored with the opportunity to speak in Room 8,
    which is a huge theater that holds several hundred people.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm3.staticflickr.com/2809/11089790274_ac70261260_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/11089790274/&quot;
       title=&quot;Devoxx: A Speaker&apos;s Perspective by mraible, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;http://farm3.staticflickr.com/2809/11089790274_ac70261260_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;Devoxx: A Speaker&apos;s Perspective&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm3.staticflickr.com/2805/11102110783_8c0ea95e1d_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102110783/&quot;
       title=&quot;The Modern JVM Web Developer by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;http://farm3.staticflickr.com/2805/11102110783_8c0ea95e1d_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;The Modern JVM Web Developer&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm4.staticflickr.com/3727/11102183263_7fc28918f6_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102183263/&quot;
       title=&quot;AngularJS Deep Dive by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;http://farm4.staticflickr.com/3727/11102183263_7fc28918f6_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;AngularJS Deep Dive&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;
    I presented a lengthened version of The Modern Java Web Developer presentation I did early this year (at &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/the_modern_java_web_developer&quot;&gt;Denver&apos;s JUG&lt;/a&gt; and &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/javaone_2013_my_presentations&quot;&gt;JavaOne&lt;/a&gt;). Based on &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/the_modern_java_web_developer1&quot;&gt;your feedback&lt;/a&gt;, I chose to do deep
    dives on AngularJS, Bootstrap and Page Speed. I&apos;ve always enjoyed speaking at Devoxx because attendees are so
    enthusiastic and passionate about the conference. I received an immense amount of feedback, both in praises and
    criticisms. The critics indicated there were &lt;a
        href=&quot;http://steveschols.wordpress.com/2013/11/21/devoxx-2013-a-retrospective/&quot;&gt;too many buzzwords&lt;/a&gt; and not
    enough substance. Others complained that the AngularJS &lt;a
        href=&quot;http://www.informit.com/articles/article.aspx?p=1930512&amp;amp;seqNum=3&quot;&gt;Lipsync&lt;/a&gt; that I did was &lt;em&gt;too
    deep&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;
    I made sure to review and process everyone&apos;s comments, and then used them to improve the presentation throughout the
    following week. I learned to elaborate on the fact that many of the technologies were important to know about, but
    not important to know through-and-through. I made sure to mention that the use of CoffeeScript and LESS is often
    limited (or embraced) by team members and their willingness to try new things. If you&apos;re not writing thousands of
    lines of JavaScript or CSS, it probably doesn&apos;t make sense to use these languages. Furthermore, if your team members
    are struggling to write JavaScript or CSS, introducing a new language is probably not the best thing. I also
    reminded people to be skeptical of new technology, but also to be open-minded and give everything a chance. The
    10-minute, download-and-try test, is a great way to do that.
&lt;/p&gt;
&lt;p&gt;You can find my presentation below, download it from &lt;a href=&quot;http://raibledesigns.com/rd/page/publications&quot;&gt;my
    presentations page&lt;/a&gt;, or view it &lt;a
        href=&quot;http://www.slideshare.net/mraible/the-modern-java-web-developer-bootcamp-devoxx-2013&quot;&gt;on SlideShare&lt;/a&gt;.
&lt;/p&gt;
&lt;div style=&quot;text-align: center&quot;&gt;
    &lt;iframe src=&quot;http://www.slideshare.net/slideshow/embed_code/28649243?rel=0&quot; width=&quot;600&quot; height=&quot;375&quot; frameborder=&quot;0&quot;
            marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;
            style=&quot;border:1px solid #ccc;border-width:1px 1px 0;margin-bottom:5px&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;
    Within this presentation, there are links to each of the deep dives. The last two are screencasts that I added
    audio to a few days ago.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://static.raibledesigns.com/bootstrap3&quot;&gt;Bootstrap 3&lt;/a&gt; | 
&lt;a href=&quot;https://vimeo.com/mraible/angularjs-deep-dive&quot;&gt;AngularJS Deep Dive&lt;/a&gt; | 
&lt;a href=&quot;https://vimeo.com/mraible/page-speed-demo&quot;&gt;Page Speed Demo&lt;/a&gt;
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7452/11089788834_b7541d335d_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/11089788834/&quot;
       title=&quot;Trish at Pelgrom by mraible, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7452/11089788834_b7541d335d_t.jpg&quot; width=&quot;100&quot;
            alt=&quot;Trish at Pelgrom&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
    Two weeks ago, Trish and I boarded a flight for one of our favorite conferences: &lt;a
        href=&quot;http://devoxx.be/&quot;&gt;Devoxx&lt;/a&gt;. After a brief layover in Frankfurt, we arrived in Amsterdam and took a train to Antwerp. Within hours, we&apos;d settled into our hotel near the center of Antwerp and strolled over to the
    dungeonous, yet cozy, &lt;a href=&quot;http://www.pelgrom.be/&quot;&gt;Pelgrom restaurant&lt;/a&gt;. We were hoping for a delicious
    dinner, but found much more. We
    ran into James Ward, Dick Wall and a number of other enthusiastic speakers from the conference. Since I had to speak
    the next day, we didn&apos;t stay long, but we did share a number of laughs with some great people.
&lt;/p&gt;
&lt;p&gt;
    Tuesday (November 12), was a University Day at Devoxx, and I had my talk that afternoon. I spent a couple hours
    finishing up my talk
    that morning, then grabbed a taxi to head to the conference. I was honored with the opportunity to speak in Room 8,
    which is a huge theater that holds several hundred people.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm3.staticflickr.com/2809/11089790274_ac70261260_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/11089790274/&quot;
       title=&quot;Devoxx: A Speaker&apos;s Perspective by mraible, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm3.staticflickr.com/2809/11089790274_ac70261260_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;Devoxx: A Speaker&apos;s Perspective&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm3.staticflickr.com/2805/11102110783_8c0ea95e1d_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102110783/&quot;
       title=&quot;The Modern JVM Web Developer by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm3.staticflickr.com/2805/11102110783_8c0ea95e1d_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;The Modern JVM Web Developer&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm4.staticflickr.com/3727/11102183263_7fc28918f6_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102183263/&quot;
       title=&quot;AngularJS Deep Dive by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3727/11102183263_7fc28918f6_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;AngularJS Deep Dive&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;
    I presented a lengthened version of The Modern Java Web Developer presentation I did early this year (at &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/the_modern_java_web_developer&quot;&gt;Denver&apos;s JUG&lt;/a&gt; and &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/javaone_2013_my_presentations&quot;&gt;JavaOne&lt;/a&gt;). Based on &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/the_modern_java_web_developer1&quot;&gt;your feedback&lt;/a&gt;, I chose to do deep
    dives on AngularJS, Bootstrap and Page Speed. I&apos;ve always enjoyed speaking at Devoxx because attendees are so
    enthusiastic and passionate about the conference. I received an immense amount of feedback, both in praises and
    criticisms. The critics indicated there were &lt;a
        href=&quot;http://steveschols.wordpress.com/2013/11/21/devoxx-2013-a-retrospective/&quot;&gt;too many buzzwords&lt;/a&gt; and not
    enough substance. Others complained that the AngularJS &lt;a
        href=&quot;http://www.informit.com/articles/article.aspx?p=1930512&amp;amp;seqNum=3&quot;&gt;Lipsync&lt;/a&gt; that I did was &lt;em&gt;too
    deep&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;
    I made sure to review and process everyone&apos;s comments, and then used them to improve the presentation throughout the
    following week. I learned to elaborate on the fact that many of the technologies were important to know about, but
    not important to know through-and-through. I made sure to mention that the use of CoffeeScript and LESS is often
    limited (or embraced) by team members and their willingness to try new things. If you&apos;re not writing thousands of
    lines of JavaScript or CSS, it probably doesn&apos;t make sense to use these languages. Furthermore, if your team members
    are struggling to write JavaScript or CSS, introducing a new language is probably not the best thing. I also
    reminded people to be skeptical of new technology, but also to be open-minded and give everything a chance. The
    10-minute, download-and-try test, is a great way to do that.
&lt;/p&gt;
&lt;p&gt;You can find my presentation below, download it from &lt;a href=&quot;http://raibledesigns.com/rd/page/publications&quot;&gt;my
    presentations page&lt;/a&gt;, or view it &lt;a
        href=&quot;http://www.slideshare.net/mraible/the-modern-java-web-developer-bootcamp-devoxx-2013&quot;&gt;on SlideShare&lt;/a&gt;.
&lt;/p&gt;
&lt;div style=&quot;text-align: center&quot;&gt;
    &lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/28649243?rel=0&quot; width=&quot;600&quot; height=&quot;375&quot; frameborder=&quot;0&quot;
            marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;
            style=&quot;border:1px solid #ccc;border-width:1px 1px 0;margin-bottom:5px&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;
    Within this presentation, there are links to each of the deep dives. The last two are screencasts that I added
    audio to a few days ago.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://static.raibledesigns.com/bootstrap3&quot;&gt;Bootstrap 3&lt;/a&gt; | 
&lt;a href=&quot;https://vimeo.com/mraible/angularjs-deep-dive&quot;&gt;AngularJS Deep Dive&lt;/a&gt; | 
&lt;a href=&quot;https://vimeo.com/mraible/page-speed-demo&quot;&gt;Page Speed Demo&lt;/a&gt;
&lt;/p&gt;
&lt;div style=&quot;width: 575px; margin: auto;&quot;&gt;
    &lt;div style=&quot;display: inline;&quot;&gt;
        &lt;iframe src=&quot;//player.vimeo.com/video/80314102&quot; width=&quot;280&quot; height=&quot;175&quot; frameborder=&quot;0&quot; webkitallowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
    &lt;/div&gt;
    &lt;div style=&quot;display: inline; float: right;&quot;&gt;
        &lt;iframe src=&quot;//player.vimeo.com/video/80391343&quot; width=&quot;280&quot; height=&quot;175&quot; frameborder=&quot;0&quot; webkitallowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
    We stayed in Antwerp until Friday, attending the conference, taking pictures, networking over beers and having a
    fabulous time with everyone attending Devoxx.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7350/11102130224_8b2d9d1109_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102130224/&quot;
       title=&quot;Street Shadows by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7350/11102130224_8b2d9d1109_s.jpg&quot; width=&quot;75&quot;
            alt=&quot;Street Shadows&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm4.staticflickr.com/3706/11102238913_441d2fb728_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102238913/&quot;
       title=&quot;Devoxx posse at the Antwerp Town Hall by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3706/11102238913_441d2fb728_s.jpg&quot; width=&quot;75&quot;
            alt=&quot;Devoxx posse at the Antwerp Town Hall&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm6.staticflickr.com/5533/11102264113_6747b92a61_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102264113/&quot;
       title=&quot;Matt and Josh next to the Cathedral Antwerp by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm6.staticflickr.com/5533/11102264113_6747b92a61_s.jpg&quot; width=&quot;75&quot;
            alt=&quot;Matt and Josh next to the Cathedral Antwerp&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm8.staticflickr.com/7371/11089691035_34e95b21d0_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mraible/11089691035/&quot;
       title=&quot;Devoxx Late Night by mraible, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7371/11089691035_34e95b21d0_s.jpg&quot; width=&quot;75&quot;
            alt=&quot;Devoxx Late Night&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;http://farm4.staticflickr.com/3775/11102166205_fe4c769a30_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102166205/&quot;
       title=&quot;Trish and Amelia by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3775/11102166205_fe4c769a30_s.jpg&quot; width=&quot;75&quot;
            alt=&quot;Trish and Amelia&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm4.staticflickr.com/3801/11102112776_b8b7c90b9a_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102112776/&quot;
       title=&quot;Town Hall Antwerp by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3801/11102112776_b8b7c90b9a.jpg&quot; width=&quot;500&quot;
            alt=&quot;Town Hall Antwerp&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7399/11102078745_37f02cdc32_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102078745/&quot;
       title=&quot;Antwerp square by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7399/11102078745_37f02cdc32.jpg&quot; width=&quot;500&quot;
            alt=&quot;Antwerp square&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;

&lt;p&gt;
    Thursday night, we dined at &lt;a
        href=&quot;http://www.tripadvisor.com/Restaurant_Review-g188636-d2536032-Reviews-Matty-Antwerp_Antwerp_Province.html&quot;&gt;Matty&lt;/a&gt;,
    one of the best restaurants in Antwerp.
    The food was excellent and provided a nice start for a night that included the Devoxx Party at Noxx and a journey to
    &lt;a href=&quot;http://biercentral.be/&quot;&gt;Bier Central&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm3.staticflickr.com/2813/11102230866_8b45e6d1e5_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102230866/&quot;
       title=&quot;Matty Restaurant Antwerp by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm3.staticflickr.com/2813/11102230866_8b45e6d1e5_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;Matty Restaurant Antwerp&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;http://farm3.staticflickr.com/2872/11102238026_29af90ac67_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102238026/&quot;
       title=&quot;Steak at Matty by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm3.staticflickr.com/2872/11102238026_29af90ac67_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;Steak at Matty&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7291/11102264004_71837f43e7_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102264004/&quot;
       title=&quot;Dessert by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7291/11102264004_71837f43e7_q.jpg&quot; width=&quot;150&quot;
            alt=&quot;Dessert&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    On Friday, we traveled to Brussels for a weekend in one of the &lt;a
        href=&quot;http://www.usatoday.com/story/travel/destinations/2012/10/02/10-best-beer-cities-in-the-world/1608885/&quot;&gt;best
    beer cities in the world&lt;/a&gt;. Trish booked us a room at the &lt;a href=&quot;http://www.hotel-saint-michel.be/&quot;&gt;Hotel Saint
    Michel&lt;/a&gt;, which was right on the &lt;a href=&quot;http://www.europeish.com/25-amazingly-stunning-european-squares/&quot;&gt;most
    beautiful square in Europe&lt;/a&gt;. Their pre-Christmas light show was spectacular. The beer was delicious, the location
    was magnificent and we thoroughly enjoyed ourselves, especially the &lt;a
        href=&quot;http://farm8.staticflickr.com/7342/11102281806_49f4a65516_c.jpg&quot;
        data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102281806/&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;mussels in
    Brussels&lt;/a&gt;. Thanks in particular to &lt;a href=&quot;https://twitter.com/snicoll&quot;&gt;St&#233;phane&lt;/a&gt; and &lt;a
        href=&quot;https://twitter.com/philipluppens&quot;&gt;Philip&lt;/a&gt; for your recommendations.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm3.staticflickr.com/2830/11102444343_3005a06b72_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102444343/&quot;
       title=&quot;Grand Place Brussels by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm3.staticflickr.com/2830/11102444343_3005a06b72.jpg&quot; width=&quot;500&quot;
            alt=&quot;Grand Place Brussels&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p id=&quot;nordic-speaking-tour&quot;&gt;
    &lt;strong&gt;Nordic Countries Speaking Tour&lt;/strong&gt;&lt;br/&gt;
    On Sunday (November 17), we flew to Stockholm to being the second half our trip. Nordea hired me to deliver my
    Devoxx presentation as part their Java Competence Network. Nordea&apos;s Jonny Berggren first contacted me in March 2010
    about this opportunity, so it was fun to see it finally happen. We agreed that I&apos;d speak at their four main
    locations: Stockholm, Helsinki, Oslo and Copenhagen. 
&lt;/p&gt;
&lt;p&gt;
    Mattias Karlsson (of &lt;a href=&quot;http://www.jfokus.se/&quot;&gt;Jfokus&lt;/a&gt; fame) also presented me with an opportunity to speak at his
    company while I was in Sweden.
&lt;/p&gt;
&lt;p&gt;
    I started the week delivering my talk on Monday afternoon at Nordea. Then we met up with Mattias, walked to his
    company and I delivered it again 45 minutes later. It was exhausting to talk for six hours in one day, but it all seemed
    to go well. I especially enjoyed the enthusiasm of Mattias&apos;s Avega Group.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm4.staticflickr.com/3817/11102348876_9d52cb9c3e_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102348876/&quot;
       title=&quot;Speaking at Avega Group by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3817/11102348876_9d52cb9c3e_n.jpg&quot; width=&quot;320&quot;
            alt=&quot;Speaking at Avega Group&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    That evening, we took the train to the airport, boarded a flight to Helsinki and arrived just after midnight. While
    on the train, we sat next to a Finlander, Karol, that gave us all kinds of great advice on what to do. Tuesday in
    Helsinki was cold and dreary; perfect sauna weather. We walked around a bit in the rain that morning and visited
    Senate Square on Karol&apos;s recommendation. I mentioned to the developers there that my Mom&apos;s grandparents were from
    Finland (Oulu and Hamina), and that I&apos;d grown up in a rustic cabin built by my Finish grandfather, Matti Hill.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm4.staticflickr.com/3749/11102390024_179d50d342_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102390024/&quot;
       title=&quot;Matt in Helsinki Capitol by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3749/11102390024_179d50d342_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Matt in Helsinki Capitol&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;http://farm3.staticflickr.com/2824/11102401054_357a5665f8_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102401054/&quot;
       title=&quot;Downtown Helsinki by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm3.staticflickr.com/2824/11102401054_357a5665f8_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Downtown Helsinki&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    We flew to Oslo Tuesday evening, rode the super-fast train from the airport and got to bed just before midnight.
    Wednesday morning, we walked around the Vigelandsparken Sculpture Park, and then I headed to the Nordea office while
    Trish did a walkabout and rode a Viking ship around the bay. We met up afterwards at the wonderful &lt;a
        href=&quot;http://www.beerpalace.no/&quot;&gt;Beer Palace&lt;/a&gt; for some pizza and delicious German/Belgian beer.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm4.staticflickr.com/3716/11102323505_758f0b2480_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102323505/&quot;
       title=&quot;Oslo Opera by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3716/11102323505_758f0b2480.jpg&quot; width=&quot;500&quot;
            alt=&quot;Oslo Opera&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7445/11102326065_c9807c37d5_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102326065/&quot;
       title=&quot;Ship and Oslo Opera by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7445/11102326065_c9807c37d5_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Ship and Oslo Opera&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;http://farm4.staticflickr.com/3816/11102531063_e63b78e22e_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102531063/&quot;
       title=&quot;Crew&apos;s Sunset Oslo by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm4.staticflickr.com/3816/11102531063_e63b78e22e_m.jpg&quot; width=&quot;240&quot;
            alt=&quot;Crew&apos;s Sunset Oslo&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;

    &lt;a href=&quot;http://farm6.staticflickr.com/5512/11102364565_d44c5bf9dc_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102364565/&quot;
       title=&quot;Oslo ship in the Harbor by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm6.staticflickr.com/5512/11102364565_d44c5bf9dc.jpg&quot; width=&quot;500&quot;
            alt=&quot;Oslo ship in the Harbor&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    &lt;a href=&quot;http://farm8.staticflickr.com/7317/11102552963_caa2a844cd_c.jpg&quot;
       data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102552963/&quot;
       title=&quot;Danish Welcome by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img
            src=&quot;//farm8.staticflickr.com/7317/11102552963_caa2a844cd_t.jpg&quot; width=&quot;100&quot;
            alt=&quot;Danish Welcome&quot; class=&quot;picture&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    Copenhagen was our last stop, a city which neither of us have ever traveled to. After arriving, we quickly got a
    hint that Copenhagen was a special place. We did a bit of research on &lt;a
        href=&quot;http://en.wikipedia.org/wiki/Copenhagen&quot;&gt;Wikipedia&lt;/a&gt; and learned it was The City of Bikes and their
    craft brewing industry has blossomed in the last decade, now sporting over 100 microbreweries. My last talk on
    Thursday morning went very well, especially since my presentation and advice was well polished by that point.
&lt;/p&gt;
&lt;p&gt;
    That afternoon, we rented bikes from our hotel, slowly ate &lt;a
        href=&quot;http://www.tripadvisor.com/Restaurant_Review-g189541-d3454699-Reviews-Sticks_n_Sushi_Tivoli_Hotel_Congress_Center-Copenhagen_Zealand.html&quot;&gt;sushi&lt;/a&gt;
    on the top of the Tivoli Hotel, and then rode to &lt;a href=&quot;http://www.tivoli.dk/&quot;&gt;Tivoli Gardens&lt;/a&gt;. Tivoli Gardens
    was decorated as a Christmas wonderland and their amusement park made us smile and giggle with glee. We stayed there
    for hours before riding home. Biking around town with hundreds of other cyclists was really cool and fun. I hope
    Denver gets &lt;em&gt;Copenhagenized&lt;/em&gt; someday, the abundance of bike-only roads is simply awesome.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm4.staticflickr.com/3778/11102417765_08ccdbdc77_c.jpg&quot; data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102417765/&quot; title=&quot;Happy Couple at Tivoli by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3778/11102417765_08ccdbdc77_q.jpg&quot; width=&quot;150&quot; alt=&quot;Happy Couple at Tivoli&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm4.staticflickr.com/3821/11102492616_cc34d454e9_c.jpg&quot; data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102492616/&quot; title=&quot;Tivoli by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3821/11102492616_cc34d454e9_q.jpg&quot; width=&quot;150&quot; alt=&quot;Tivoli&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm4.staticflickr.com/3689/11102535804_660b10428a_c.jpg&quot; data-href=&quot;http://www.flickr.com/photos/mcginityphoto/11102535804/&quot; title=&quot;Tivoli by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[devoxx2013]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3689/11102535804_660b10428a_q.jpg&quot; width=&quot;150&quot; alt=&quot;Tivoli&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;
    Our Nordic countries tour was especially pleasant because Christmas decorations were everywhere. In the US, many
    folks look down upon Christmas decorations before Thanksgiving, but since Europeans don&apos;t celebrate Thanksgiving - there&apos;s no reason not to ease into the Christmas spirit. We figured Abbie and Jack would have a blast at Tivoli in a future November or December. Yes, it was
    a bit chilly (20-30&amp;deg;F) in most of the countries, but we were well dressed for it. Unfortunately, we didn&apos;t get
    to see any snow.
&lt;/p&gt;
&lt;p&gt;
    Many thanks to Devoxx, Nordea, Mattias&apos;s Avega Group and the hundreds of developers who listened to me talk about being a
    modern web developer. We had a wonderful time speaking, laughing, photographing, drinking your delicious beer and
    seeing all your smiling faces.&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; color: #999; padding-top: 5px&quot;&gt;
    For more photos of this whirlwind trip, see Trish&apos;s &lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/sets/72157638119009136/&quot;&gt;
    EU and Scandanavian Speaking Tour 2013&lt;/a&gt;, while mine are in
    &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157638123386015/&quot;&gt;Devoxx 2013 and Nordic Speaking Tour&lt;/a&gt;.
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv</id>
        <title type="html">Developing with AngularJS - Part IV: Making it Pop</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv"/>
        <published>2013-09-12T10:54:29-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="design" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css3" scheme="http://roller.apache.org/ns/tags/" />
        <category term="responsivedesign" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
Welcome to the final article in a series on my experience developing with &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. I learned its concepts, beat my head against-the-wall with and finally tamed it enough to create a &quot;My Dashboard&quot; feature for a client. For previous articles, please see the following:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;Part I: The Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii&quot;&gt;Part II: Dialogs and Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii&quot;&gt;Part III: Services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last mile of development for the My Dashboard feature was to spice things up a bit and make it look better. We hired a design company to come up a new look and feel and they went to work. Within a week, we had a meeting with them and they presented a few different options. We picked the one we liked the best and went to work. Below are screenshots that I used to implement the new design.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971086/&quot; href=&quot;http://farm6.staticflickr.com/5447/8904971086_e24b89fa7e_c.jpg&quot; title=&quot;My Dashboard - New Design&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;http://farm6.staticflickr.com/5447/8904971086_e24b89fa7e_m.jpg&quot; width=&quot;240&quot; height=&quot;229&quot; alt=&quot;My Dashboard - New Design&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971332/&quot; href=&quot;http://farm3.staticflickr.com/2866/8904971332_9e919549b8_c.jpg&quot; title=&quot;My Dashboard with Show More&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;http://farm3.staticflickr.com/2866/8904971332_9e919549b8_m.jpg&quot; width=&quot;240&quot; height=&quot;229&quot; alt=&quot;My Dashboard with Show More&quot; style=&quot;border: 1px solid silver; margin-left: 20px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
Welcome to the final article in a series on my experience developing with &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. I learned its concepts, beat my head against-the-wall with and finally tamed it enough to create a &quot;My Dashboard&quot; feature for a client. For previous articles, please see the following:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;Part I: The Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii&quot;&gt;Part II: Dialogs and Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii&quot;&gt;Part III: Services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last mile of development for the My Dashboard feature was to spice things up a bit and make it look better. We hired a design company to come up a new look and feel and they went to work. Within a week, we had a meeting with them and they presented a few different options. We picked the one we liked the best and went to work. Below are screenshots that I used to implement the new design.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971086/&quot; href=&quot;http://farm6.staticflickr.com/5447/8904971086_e24b89fa7e_c.jpg&quot; title=&quot;My Dashboard - New Design&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5447/8904971086_e24b89fa7e_m.jpg&quot; width=&quot;240&quot; height=&quot;229&quot; alt=&quot;My Dashboard - New Design&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971332/&quot; href=&quot;http://farm3.staticflickr.com/2866/8904971332_9e919549b8_c.jpg&quot; title=&quot;My Dashboard with Show More&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2866/8904971332_9e919549b8_m.jpg&quot; width=&quot;240&quot; height=&quot;229&quot; alt=&quot;My Dashboard with Show More&quot; style=&quot;border: 1px solid silver; margin-left: 20px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;At first, I thought implementing this design might take quite a bit of effort, since it looked like it used custom fonts. It&apos;s true we could use CSS3&apos;s &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face&quot;&gt;@font-face&lt;/a&gt;, but I knew it might take awhile to find the right fonts with the appropriate licenses. When I received the screenshot below, I was pleased to see that all fonts were web-safe.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971378/&quot; href=&quot;http://farm3.staticflickr.com/2839/8904971378_a0ac267cc0_c.jpg&quot; title=&quot;My Dashboard Fonts&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2839/8904971378_a0ac267cc0.jpg&quot; width=&quot;500&quot; height=&quot;430&quot; alt=&quot;My Dashboard Fonts&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;h3 id=&quot;design-elements&quot;&gt;Design Elements&lt;/h3&gt;
&lt;p&gt;There are a number of elements in this new design that I had to create. For example, if numbers were only 1 digit, we had to add a leading zero to them in the summary band. Other design elements we needed to implement are listed below:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;A background image that filled the page&lt;/li&gt;&lt;li&gt;Fade to white on summary widget titles&lt;/li&gt;&lt;li&gt;A responsive grid for summary widgets&lt;/li&gt;&lt;li&gt;Provide a colored background for odd rows in the summary grid&lt;/li&gt;&lt;li&gt;Add a &quot;Show More&quot; band at the bottom of Tasks, Summary and Reports when there&apos;s more items to display&lt;/li&gt;&lt;/ul&gt;
&lt;p class=&quot;nolink&quot;&gt;In addition to these elements, there was quite a bit of work to conform to the new colors, fonts and drop-shadows. I implemented all of these using CSS3 (border-radius, box-shadow, box-sizing, linear-gradient), and lots of trial-and-error. To use the best fonts across various devices, I used CSS-Trick&apos;s &lt;a href=&quot;http://css-tricks.com/snippets/css/font-stacks/&quot;&gt;Font Stacks&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;new-background&quot;&gt;New Background&lt;/h3&gt;
&lt;p&gt;The new background shown in the screenshots above has a light source in the middle of it. Therefore, it&apos;s impossible to tile/repeat it across the page since it&apos;s not uniform. To make it work, I used a 1024 x 768 image and CSS3&apos;s &lt;code&gt;background-size: cover&lt;/code&gt;. For more information on background-size, see SitePoint&apos;s &lt;a href=&quot;http://www.sitepoint.com/css3-background-size-property/&quot;&gt;How to Resize Background Images with CSS3&lt;/a&gt;. This worked great on smaller screens, but we noticed some issues on 30&quot; monitors. Therefore, we ended up getting a new repeatable background and stopped using background-size.&lt;/p&gt;
&lt;h3 id=&quot;leadingzero-filter&quot;&gt;LeadingZero filter&lt;/h3&gt;
&lt;p&gt;For the first leading zero feature, I wrote an Angular filter. I put the code for this in &lt;em&gt;filters.js&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
filter(&apos;leadingZero&apos;, function() {
    return function(input) {
        if (input.length === 1) {
            return &quot;0&quot; + input;
        } else if (input.length &amp;gt; 2) {
            return &quot;+99&quot;;
        } else {
            return input;
        }
    }
});
&lt;/pre&gt;
&lt;p&gt;This filter is used in the HTML template as follows:&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div class=&quot;summary-value&quot;&amp;gt;{{widget.value | leadingZero}}&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;h3 id=&quot;text-fadeout&quot;&gt;Text Fade Out&lt;/h3&gt;
&lt;p&gt;To implement the fade-to-white text in summary titles, I started with &lt;a href=&quot;http://css-tricks.com/text-fade-read-more/&quot;&gt;this tutorial&lt;/a&gt;. I quickly discovered that it worked best for vertical text blocks and not for horizontal text. Then I found &lt;a href=&quot;http://xion.org.pl/2011/12/26/text-ellipsis-with-gradient-fade-in-pure-css/&quot;&gt;Text Ellipsis with Gradient Fade in Pure CSS&lt;/a&gt;, which uses :after to position a block over the text that fades to white. Since the title is not the right-most element (the numbers are), I had to figure out best positioning that worked cross-browser. Below is the CSS I used to implement this feature.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.dashboard .summary-title:after {
    display: block;
    position: absolute;
    right: 66px;
    top: 5px;
    bottom: 5px;
    width: 30px;
    background: -moz-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* FF3.6+ */
    background: -webkit-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* Opera 11.10+ */
    background: -ms-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* IE10+ */
    background: linear-gradient(to right,  rgba(255,255,255,0) 0%, #fff 20px); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr=&apos;#00ffffff&apos;, endColorstr=&apos;#ffffff&apos;,GradientType=1 ); /* IE6-9 */
    content: &quot;&quot;;
}
&lt;/pre&gt;
&lt;h3 id=&quot;responsive-grid&quot;&gt;Responsive Grid&lt;/h3&gt;
&lt;p&gt;To implement the responsive grid of summary widgets, I started with Codrops&apos; &lt;a href=&quot;http://tympanus.net/codrops/2013/04/17/responsive-full-width-grid/&quot;&gt;Responsive Full Width Grid Tutorial&lt;/a&gt;. This proved to be a great model and I used the following CSS to position all the &amp;lt;li&amp;gt;&apos;s appropriately. In the code below, &lt;code&gt;.summary-item&lt;/code&gt; is the class on the &amp;lt;li&amp;gt; elements.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.dashboard .summary-item {
    border-right: 1px solid #d1d1d1;
    border-bottom: 1px solid #d1d1d1;
    /* put the border on the inside of the box */
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    box-sizing: border-box;
    font-family: Constantia, &quot;Lucida Bright&quot;, Lucidabright, &quot;Lucida Serif&quot;, Lucida, &quot;DejaVu Serif&quot;, &quot;Bitstream Vera Serif&quot;, &quot;Liberation Serif&quot;, Georgia, serif;
    font-size: 14px;
    color: #666;
    height: 50px;
    box-shadow: inset 0 0 6px rgba(0,0,0, 0.25);
    /* responsive grid */
    position: relative;
    float: left;
    overflow: hidden;
    width: 25% /* Fallback */
    width: -webkit-calc(100% / 4);
    width: calc(100% / 4);
}

@media screen and (max-width: 1400px) {
    .dashboard .summary-item {
        width: 33.33333333333333%; /* Fallback */
        width: -webkit-calc(100% / 3);
        width: calc(100% / 3);
    }
}

@media screen and (max-width: 1000px) {
    .dashboard .summary-item {
        width: 50%; /* Fallback */
        width: -webkit-calc(100% / 2);
        width: calc(100% / 2);
    }
}
&lt;/pre&gt;
&lt;p&gt;This worked great in most browsers, but we did find an issue with IE9. When squishing/expanding the browser window, sometimes there would be a blank column on the right side. To fix this, I changed the width on the default &lt;code&gt;.summary-item&lt;/code&gt; to be 25%, and removed the lines with &lt;code&gt;calc&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.dashboard .summary-item {
    ...
    width: 25%
}
&lt;/pre&gt;
&lt;h3 id=&quot;coloring-oddrows&quot;&gt;Coloring Odd Rows&lt;/h3&gt;
&lt;p&gt;Coloring odd rows in a table is easy, but when the rows are in a responsive grid, that&apos;s a whole different story. For tables, the CSS rules are extremely simple:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
tr:nth-child(even) {background: #CCC}
tr:nth-child(odd) {background: #FFF}
&lt;/pre&gt;
&lt;p&gt;Via Twitter, &lt;a href=&quot;http://twitter.com/tomaslin&quot;&gt;@tomaslin&lt;/a&gt; advised me that the nth-child selector could probably be used for this, but it&apos;d likely require some JavaScript to make it responsive. I found the excellent &lt;a href=&quot;http://nthmaster.com/&quot;&gt;Master of the :nth-child&lt;/a&gt; and began trying to figure it out. The following function is what we now use to color odd rows in the Summary Bar.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
function colorRows() {
    var lisInRow = 0;
    var items = $(&apos;.summary-items li&apos;);
    items.each(function() {
        if($(this).prev().length &amp;gt; 0) {
            if($(this).position().top != $(this).prev().position().top) return false;
            lisInRow++;
        }
        else {
            lisInRow++;
        }
    });
    var rows = items.length / lisInRow;
    for (var i = 0; i &amp;lt; rows; i++) {
        var selector = &quot;nth-child(n+{x}):nth-child(-n+{y})&quot;;
        var x = (lisInRow * i) + 1;
        var y = x + (lisInRow - 1);
        selector = selector.replace(&apos;{x}&apos;, &apos;&apos; + x);
        selector = selector.replace(&apos;{y}&apos;, &apos;&apos; + y);
        if (i % 2) {
            $(&apos;.summary-items li:&apos; + selector).addClass(&apos;odd&apos;);
        } else {
            $(&apos;.summary-items li:&apos; + selector).removeClass(&apos;odd&apos;);
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;The above code is in &lt;em&gt;dashboard.js&lt;/em&gt; and is called anytime the browser window is resized (to adapt to the responsive grid).&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$(window).resize(colorRows);
&lt;/pre&gt;
&lt;p&gt;It&apos;s also called when summary widgets are re-ordered, in the &lt;code&gt;updateOrder()&lt;/code&gt; function of &lt;code&gt;WidgetController&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$scope.updateOrder = function(event, ui) {
    ...
    Preferences.saveWidgetOrder(type, {items: items});
    if (type === &apos;summary&apos;) {
        colorRows();
    }
};
&lt;/pre&gt;
&lt;p&gt;I&apos;d like to figure out how to make this more Angular-esque, but all the &quot;how to hook into window.resize&quot; articles I found make it seem harder than this.&lt;/p&gt;
&lt;h3 id=&quot;show-more&quot;&gt;Show More&lt;/h3&gt;
&lt;p&gt;The last feature I had to implement was the &quot;Show More&quot; bar that appears when widgets are hidden. This was the most difficult thing to implement and I tried many different things before arriving at a solution that works. First of all, the widgets bars that can be expanded are put into their original (collapsed) state using &lt;code&gt;max-height&lt;/code&gt; and &lt;code&gt;overflow: hidden&lt;/code&gt;. From there, I look at the list inside the bar and compare the height&apos;s of the two elements. If the list is taller than the bar, the Show More bar is added.&lt;/p&gt;
&lt;div class=&quot;alert alert-info&quot;&gt;
I originally looked at the list&apos;s &lt;code&gt;:last-child&lt;/code&gt; to see if it was visible, but jQuery&apos;s &lt;a href=&quot;http://api.jquery.com/hidden-selector/&quot;&gt;:hidden selector&lt;/a&gt; only works on items that are hidden by &lt;code&gt;display: none&lt;/code&gt; rather than ones that are hidden by &lt;code&gt;overflow&lt;/code&gt;.
&lt;/div&gt;
&lt;p&gt;As you can see from the code below, there&apos;s special logic needed to expand the min-height of the Summary Bar since it doesn&apos;t have enough room at the bottom to add the bar in its collapsed state.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
function showMore(element) {
    var bar = element.parent().parent();
    var list = element.parent();
    var barId = bar.attr(&apos;id&apos;);
    var listHeight = list.height();
    var barHeight = bar.height();
    var isSummaryBar = (barId.indexOf(&apos;summary&apos;) &amp;gt; -1);
    var summaryBarMinHeight = 260;
    var showMoreShouldBeVisible = (isSummaryBar &amp;amp;&amp;amp; element.position().top &amp;gt;= 200) ? true : listHeight &amp;gt; barHeight;
    if (showMoreShouldBeVisible) {
        var messages = {};
        // the variables below are defined in the host page, before this file is loaded
        messages.more = showMoreText;
        messages.less = showLessText;

        var showMore = $(&apos;&amp;lt;div class=&quot;show-more&quot;/&amp;gt;&apos;).html(messages.more + &quot; &amp;lt;b class=&apos;caret&apos;&amp;gt;&amp;lt;/b&amp;gt;&quot;);
        showMore.appendTo(bar);
        // summary bar doesn&apos;t have enough room for the Show More bar in its collapsed state,
        // so change it from 242 to 260
        if (isSummaryBar) {
            bar.css({&apos;min-height&apos;: summaryBarMinHeight + &apos;px&apos;, &apos;max-height&apos;: &apos;&apos;});
        }

        showMore.bind(&apos;click&apos;, function (e) {
            var element = $(this);
            var parent = element.parent();

            if (element.hasClass(&apos;less&apos;)) {
                parent.css({&quot;max-height&quot;: &apos;&apos;});
                if (isSummaryBar) {
                    parent.css({&quot;min-height&quot;: summaryBarMinHeight + &apos;px&apos;}).animate(200);
                } else {
                    parent.css({&quot;min-height&quot;: &apos;&apos;}).animate(200);
                }
                element.removeClass(&apos;less&apos;);
                element.html(messages.more + &apos; &amp;lt;b class=&quot;caret&quot;&amp;gt;&amp;lt;/b&amp;gt;&apos;);
            } else {
                parent.css({
                    &quot;max-height&quot;: 9999,
                    &quot;min-height&quot;: &apos;auto&apos;
                }).animate({
                        &quot;min-height&quot;: parent.height() + 19
                    }, 200);

                element.addClass(&apos;less&apos;);
                element.html(messages.less + &apos; &amp;lt;b class=&quot;caret caret-up&quot;&amp;gt;&amp;lt;/b&amp;gt;&apos;);
            }

            // prevent jump-down
            return false;
        });
    } else {
        // Remove show-more in case it was previously added
        if (bar.find(&apos;.show-more&apos;).length &amp;gt; 0) {
            if (isSummaryBar) {
                bar.css(&apos;min-height&apos;, summaryBarMinHeight - 18)
            } else {
                bar.attr(&apos;style&apos;, &apos;&apos;);
            }

            bar.find(&apos;.show-more&apos;).remove();
        }
    }
}

function showMoreOnResize() {
    var dataItems = $(&apos;.task-items,.summary-items,.report-items&apos;);
    dataItems.each(function() {
        var lastItem = $(this).find(&apos;li:last-child&apos;);
        if (lastItem.length &amp;gt; 0) {
            showMore(lastItem);
        }
    });
}
&lt;/pre&gt;
&lt;p&gt;At first, I wrote this logic as a directive, but when I needed it for responsiveness, I moved it into &lt;em&gt;dashboard.js&lt;/em&gt;. The &lt;code&gt;showMoreOnResize()&lt;/code&gt; function is called on window resize.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$(window).resize(showMoreOnResize);
&lt;/pre&gt;
&lt;p&gt;I also found that I had to add it to the Preferences service after widgets were saved (since the number displayed could change).&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
factory(&apos;Preferences&apos;, function ($filter) {
    return {
        ...
        // Save hidden and visible (and order) widgets from config dialog
        saveWidgetPreferences: function (type, widgets) {
            ...
            DWRFacade.saveDashboardWidgetPreference(type, preferences, {
                callback: function() {
                    // recalculate show more bar
                    showMoreOnResize();
                },
                errorHandler: function (errorString) {
                    alert(errorString);
                }
            });
        }
    }
});
&lt;/pre&gt;
&lt;p&gt;To implement the .caret-up (the .caret class is from Bootstrap), I found &lt;a href=&quot;https://github.com/twitter/bootstrap/issues/2902&quot;&gt;a caret-right howto&lt;/a&gt; and used it to create &lt;code&gt;.caret-up&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.caret-up {
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 4px solid transparent;
    border-bottom: 4px solid black;
}
&lt;/pre&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;
The final My Dashboard feature is something that I&apos;m quite proud of. A fellow developer, Vlad, did an excellent job of implementing the backend and admin portions. The Product Team&apos;s vision and desire to make it &lt;em&gt;Pop!&lt;/em&gt; created something great. The fact that we didn&apos;t have to support IE8 helped a lot in the implementation. Below is a screenshot of how My Dashboard looked when we completed the project.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904354347/&quot; href=&quot;http://farm8.staticflickr.com/7380/8904354347_0b38a0bf2d_c.jpg&quot; title=&quot;My Dashboard&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7380/8904354347_0b38a0bf2d.jpg&quot; width=&quot;500&quot; height=&quot;338&quot; alt=&quot;My Dashboard&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Angular isn&apos;t mentioned much in this article. That&apos;s because we didn&apos;t have to do much to the existing Angular code to implement the new design. It was just a matter of writing/modifying some CSS as well as introducing some JavaScript for colored rows and show more. If you know how these features could be written in a more Angular Way, I&apos;d love to hear about it.&lt;/p&gt;
&lt;p&gt;If you&apos;d still like to learn more about Angular and why it&apos;s good to integrate it little by little, I encourage you to read &lt;a href=&quot;http://oscarvillarreal.com/2013/05/07/5-reasons-to-use-angularjs-in-the-corporate-app-world/&quot;&gt;5 reasons to use AngularJS in the corporate app world&lt;/a&gt;.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_i</id>
        <title type="html">Developing with AngularJS - Part I: The Basics</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_with_angularjs_part_i"/>
        <published>2013-06-18T09:06:52-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="jquery" scheme="http://roller.apache.org/ns/tags/" />
        <category term="highcharts" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;There&apos;s &lt;a href=&quot;http://hop.ie/blog/angularjs-introduction/&quot;&gt;many&lt;/a&gt;, &lt;a href=&quot;http://www.raweng.com/blog/2013/01/30/introduction-to-angularjs-part-1/&quot;&gt;many&lt;/a&gt; different &lt;a href=&quot;http://www.webdesignerdepot.com/2013/04/an-introduction-to-angularjs/&quot;&gt;introductions&lt;/a&gt; to &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; available on the internet. This article is not another introduction, but rather a story about my learning experience. It all started way back in January of this year. I was working as a UI Architecture Consultant at Taleo/Oracle, my client for the last 21 months. My gig there ended last month, but they agreed to let me publish a series of articles about the knowledge I gained.&lt;/p&gt;

&lt;h3 id=&quot;background&quot;&gt;Project Background&lt;/h3&gt;
&lt;p&gt;The Director of Product Management had been working on the concepts for a new project - codenamed &quot;Visual MyView&quot;. Below is a mockup he created for our kickoff meeting on January 4th.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904352595/&quot; href=&quot;http://farm3.staticflickr.com/2872/8904352595_1678cfd1ab_c.jpg&quot; title=&quot;My Dashboard - Original Mockup&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;http://farm3.staticflickr.com/2872/8904352595_1678cfd1ab.jpg&quot; width=&quot;500&quot; height=&quot;296&quot; alt=&quot;My Dashboard - Original Mockup&quot; style=&quot;border: 1px solid silver; box-shadow: 5px 5px 10px #888&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
From his original email about the above mockup:
&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
    &lt;p&gt;The intent here is that one of the columns has rows that have a similar width. The rows could be dragged and dropped into a different order &#8211; or potentially the two columns could also be reordered. The rows will basically be comprised of similar widgets. You can see in the mockup how the first two rows might look &#8211; and sample widgets. The widgets shown can be configured by the end user, as well as the order in which they are displayed. Other requirements given to us were the following.
    &lt;/p&gt;
    &lt;ul&gt;&lt;li&gt;Row 1 is comprised of &apos;summary&apos; widgets that are &apos;todo&apos; items. Reviews needing done &#8211; approvals required &#8211; etc.&lt;/li&gt;
        &lt;li&gt;Row 2 will be a graph row &#8211; having graphs and charts to display information &#8211; larger squares will build this row.&lt;/li&gt;
        &lt;li&gt;Row 3&apos;s content was not determined yet.&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;I started the initial layout with static HTML and CSS and had a wireframe to show by mid January.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904969226/&quot; href=&quot;http://farm3.staticflickr.com/2885/8904969226_c33d020e07_c.jpg&quot; title=&quot;Wireframe&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;http://farm3.staticflickr.com/2885/8904969226_c33d020e07_n.jpg&quot; width=&quot;320&quot; height=&quot;268&quot; alt=&quot;Wireframe&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;By the end of January, we&apos;d renamed the project to My Dashboard and had a working prototype using &lt;a href=&quot;http://randomibis.com/coolclock/&quot;&gt;CoolClock&lt;/a&gt; and &lt;a href=&quot;http://momentjs.com/&quot;&gt;moment.js&lt;/a&gt; for the clock in the top right, &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; to display widget data, &lt;a href=&quot;http://jqueryui.com/&quot;&gt;jQuery UI&lt;/a&gt; for drag-n-drop of rows and widgets, Bootstrap&apos;s &lt;a href=&quot;http://twitter.github.com/bootstrap/javascript.html#carousel&quot;&gt;Carousel&lt;/a&gt; for holding charts and &lt;a href=&quot;http://www.highcharts.com/&quot;&gt;Highcharts&lt;/a&gt; for rendering charts.&lt;/p&gt;
</summary>
        <content type="html">&lt;p&gt;There&apos;s &lt;a href=&quot;http://hop.ie/blog/angularjs-introduction/&quot;&gt;many&lt;/a&gt;, &lt;a href=&quot;http://www.raweng.com/blog/2013/01/30/introduction-to-angularjs-part-1/&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;http://www.codeproject.com/Articles/607873/Extending-HTML-with-AngularJS-Directives&quot;&gt;different&lt;/a&gt; &lt;a href=&quot;http://www.webdesignerdepot.com/2013/04/an-introduction-to-angularjs/&quot;&gt;introductions&lt;/a&gt; to &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; available on the internet. This article is not another introduction, but rather a story about my learning experience. It all started way back in January of this year. I was working as a UI Architecture Consultant at Taleo/Oracle, my client for the last 21 months. My gig there ended last month, but they agreed to let me publish a series of articles about the knowledge I gained.&lt;/p&gt;

&lt;h3 id=&quot;background&quot;&gt;Project Background&lt;/h3&gt;
&lt;p&gt;The Director of Product Management had been working on the concepts for a new project - codenamed &quot;Visual MyView&quot;. Below is a mockup he created for our kickoff meeting on January 4th.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904352595/&quot; href=&quot;http://farm3.staticflickr.com/2872/8904352595_1678cfd1ab_c.jpg&quot; title=&quot;My Dashboard - Original Mockup&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2872/8904352595_1678cfd1ab.jpg&quot; width=&quot;500&quot; height=&quot;296&quot; alt=&quot;My Dashboard - Original Mockup&quot; style=&quot;border: 1px solid silver; box-shadow: 5px 5px 10px #888&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
From his original email about the above mockup:
&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
    &lt;p&gt;The intent here is that one of the columns has rows that have a similar width. The rows could be dragged and dropped into a different order &#8211; or potentially the two columns could also be reordered. The rows will basically be comprised of similar widgets. You can see in the mockup how the first two rows might look &#8211; and sample widgets. The widgets shown can be configured by the end user, as well as the order in which they are displayed. Other requirements given to us were the following.
    &lt;/p&gt;
    &lt;ul&gt;&lt;li&gt;Row 1 is comprised of &apos;summary&apos; widgets that are &apos;todo&apos; items. Reviews needing done &#8211; approvals required &#8211; etc.&lt;/li&gt;
        &lt;li&gt;Row 2 will be a graph row &#8211; having graphs and charts to display information &#8211; larger squares will build this row.&lt;/li&gt;
        &lt;li&gt;Row 3&apos;s content was not determined yet.&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;I started the initial layout with static HTML and CSS and had a wireframe to show by mid January.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904969226/&quot; href=&quot;http://farm3.staticflickr.com/2885/8904969226_c33d020e07_c.jpg&quot; title=&quot;Wireframe&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2885/8904969226_c33d020e07_n.jpg&quot; width=&quot;320&quot; height=&quot;268&quot; alt=&quot;Wireframe&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;By the end of January, we&apos;d renamed the project to My Dashboard and had a working prototype using &lt;a href=&quot;http://randomibis.com/coolclock/&quot;&gt;CoolClock&lt;/a&gt; and &lt;a href=&quot;http://momentjs.com/&quot;&gt;moment.js&lt;/a&gt; for the clock in the top right, &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; to display widget data, &lt;a href=&quot;http://jqueryui.com/&quot;&gt;jQuery UI&lt;/a&gt; for drag-n-drop of rows and widgets, Bootstrap&apos;s &lt;a href=&quot;http://twitter.github.com/bootstrap/javascript.html#carousel&quot;&gt;Carousel&lt;/a&gt; for holding charts and &lt;a href=&quot;http://www.highcharts.com/&quot;&gt;Highcharts&lt;/a&gt; for rendering charts. For this prototype, we included 4 types of widgets:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Summary&lt;/li&gt;&lt;li&gt;Tasks&lt;/li&gt;&lt;li&gt;Charts&lt;/li&gt;&lt;li&gt;Reports&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;To create widgets, we had to decide on a common schema for them.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&quot;id&quot;: 1, // not necessary for display, but likely needed if we modify and save preferences
&quot;title&quot;: &quot;Appointments Today&quot;,
&quot;type&quot;: &quot;summary&quot;, // others include: task, chart, report
&quot;value&quot;: 3, 
&quot;description&quot;: &quot;10:30 Jim Smith&quot;,
&quot;events&quot;: &quot;url&quot;, // this can have click events
&quot;order&quot;: 1 // used to determine order
&lt;/pre&gt;
&lt;p&gt;Below is a screenshot of our wireframe with some sample widgets.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904969540/&quot; href=&quot;http://farm9.staticflickr.com/8556/8904969540_1cfe0e56c7_c.jpg&quot; title=&quot;Wireframe with Data&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8556/8904969540_1cfe0e56c7.jpg&quot; width=&quot;500&quot; height=&quot;342&quot; alt=&quot;Wireframe with Data&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id=&quot;basics&quot;&gt;Angular Basics&lt;/h3&gt;
&lt;p&gt;The decision to use AngularJS came early on in the project, after I read Tyler Renelle&apos;s &lt;a href=&quot;https://gist.github.com/lefnire/4454814&quot;&gt;Rant: Backbone, Angular, Meteor, Derby&lt;/a&gt;. To learn AngularJS, I briefly looked at its homepage documentation and played with some examples. Then I stumbled upon Misko Hevery and Igor Minar&apos;s &lt;a href=&quot;http://parleys.com/play/5148922b0364bc17fc56c91b/about&quot;&gt;AngularJS Presentation from Devoxx 2012&lt;/a&gt;. At that time, the video wasn&apos;t publicly available (it&apos;s free now), so I had to buy a Parley&apos;s subscription ($79). It was well worth the money because that one hour video greatly contributed to my understanding of how AngularJS works. Another resource I used frequently to figure out how to do things was John Lindquist&apos;s &lt;a href=&quot;http://www.egghead.io/&quot;&gt;egghead.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To begin with, we wrote the JSON for a bunch of sample widgets and embedded them into the page as a &lt;code&gt;widgetData&lt;/code&gt; JavaScript variable.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
var widgetData = [
    {&quot;id&quot;: 1, &quot;title&quot;: &quot;Appointments Today&quot;, &quot;type&quot;: &quot;summary&quot;, &quot;value&quot;: 3, &quot;description&quot;: &quot;10:30 Jim Smith&quot;, &quot;events&quot;: {&quot;click&quot;: &quot;alert(&apos;foo&apos;);&quot;}, &quot;order&quot;: 1},
    {&quot;id&quot;: 12, &quot;order&quot;: 2, &quot;title&quot;: &quot;Offer Approvals&quot;, &quot;type&quot;: &quot;task&quot;, &quot;class&quot;: &quot;sticky-note&quot;, &quot;value&quot;: 1},
    {&quot;id&quot;: 103, &quot;title&quot;: &quot;Browser market shares at a specific website, 2010&quot;, &quot;order&quot;: 1, &quot;type&quot;: &quot;chart&quot;, &quot;chartType&quot;: &quot;pie&quot;, 
         &quot;tooltip&quot;: {&quot;pointFormat&quot;: &quot;{series.name}: &amp;lt;b&gt;{point.percentage}%&amp;lt;/b&gt;&quot;, &quot;percentageDecimals&quot;: 1}, &quot;series&quot;: [
         {&quot;type&quot;: &quot;pie&quot;, &quot;name&quot;: &quot;Browser share&quot;, &quot;data&quot;: [
             [&quot;Firefox&quot;, 45.0],
             [&quot;IE&quot;, 26.8],
             {&quot;name&quot;: &quot;Chrome&quot;, &quot;y&quot;: 12.8, &quot;sliced&quot;: true, &quot;selected&quot;: true},
             [&quot;Safari&quot;, 8.5],
             [&quot;Opera&quot;, 6.2],
             [&quot;Others&quot;, 0.7]
         ]}
    ]},
    ...
];
&lt;/pre&gt;
&lt;p&gt;I used &lt;a href=&quot;https://github.com/angular/angular-seed&quot;&gt;angular-seed&lt;/a&gt; to create the initial structure of the prototype, and continued using the same JavaScript file names when we moved it into the product I worked on. Since the application takes a while to login and render the My Dashboard page (when working remotely), I decided not to use the &lt;a href=&quot;http://karma-runner.github.io/0.8/index.html&quot;&gt;Karma&lt;/a&gt; testing framework that ships with Angular. Below is what our directory structure looked like for our prototype.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904352305/&quot; href=&quot;http://farm3.staticflickr.com/2860/8904352305_e3b3d40f80_c.png&quot; title=&quot;Angular Seed Directory Structure&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2860/8904352305_e3b3d40f80_o.png&quot; width=&quot;327&quot; height=&quot;459&quot; alt=&quot;Angular Seed Directory Structure&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;The JavaScript files in the &quot;js&quot; folder are the most important for Angular. The first file, &lt;code&gt;app.js&lt;/code&gt;, loads the other files:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
angular.module(&apos;dashboard&apos;, [&apos;dashboard.filters&apos;, &apos;dashboard.services&apos;, &apos;dashboard.directives&apos;]);
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;controllers.js&lt;/code&gt; file contains the Controllers (functions) that get the data and make it available to the page. Here&apos;s the code for our first controller:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&apos;use strict&apos;;

/* Controllers */
function WidgetController($scope) {
    $scope.widgets = widgetData;
}
&lt;/pre&gt;
&lt;p&gt;This puts the widgets in scope and then we were able to render them using Angular&apos;s &lt;a href=&quot;http://docs.angularjs.org/api/ng.directive:ngRepeat&quot;&gt;ngRepeat&lt;/a&gt; directive and the following HTML:&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div ng-app=&quot;dashboard&quot; class=&quot;dashboard&quot;&amp;gt;
    &amp;lt;div class=&quot;container-widgets&quot; ng-controller=&quot;WidgetController&quot; ng-cloak&amp;gt;
        &amp;lt;div class=&quot;row-fluid&quot;&amp;gt;
            &amp;lt;div class=&quot;span9&quot;&amp;gt;
                &amp;lt;ul class=&quot;widgets&quot;&amp;gt;
                    &amp;lt;li id=&quot;summary-bar&quot;&amp;gt;
                        &amp;lt;div class=&quot;heading&quot;&amp;gt;Summary&amp;lt;/div&amp;gt;
                        &amp;lt;ul class=&quot;tiles&quot;&amp;gt;
                            &amp;lt;li class=&quot;span3&quot; ng-repeat=&quot;widget in widgets | filter:{type: &apos;summary&apos;} | orderBy: &apos;order&apos;&quot;&amp;gt;
                                &amp;lt;h3 class=&quot;events&quot;&amp;gt;{{widget.value}}&amp;lt;/h3&amp;gt;
                                &amp;lt;div class=&quot;title&quot;&amp;gt;{{widget.title}}&amp;lt;/div&amp;gt;
                                &amp;lt;div class=&quot;desc&quot;&amp;gt;{{widget.description}}&amp;lt;/div&amp;gt;
                            &amp;lt;/li&amp;gt;
                        &amp;lt;/ul&amp;gt;
                    &amp;lt;/li&amp;gt;
                    &amp;lt;li id=&quot;task-bar&quot;&amp;gt;
                        &amp;lt;div class=&quot;heading&quot;&amp;gt;My Tasks&amp;lt;/div&amp;gt;
                        &amp;lt;ul class=&quot;tasks&quot;&amp;gt;
                            &amp;lt;li class=&quot;task {{widget.class}}&quot; ng-repeat=&quot;widget in widgets | filter: {type: &apos;task&apos;} | orderBy: &apos;order&apos;&quot;&amp;gt;
                                &amp;lt;div class=&quot;title events&quot;&amp;gt;{{widget.title}}&amp;lt;/div&amp;gt;
                                &amp;lt;div class=&quot;value&quot;&amp;gt;{{widget.value}}&amp;lt;/div&amp;gt;
                            &amp;lt;/li&amp;gt;
                        &amp;lt;/ul&amp;gt;
                    &amp;lt;/li&amp;gt;
                    &amp;lt;li id=&quot;chart-bar&quot;&amp;gt;
                        &amp;lt;div class=&quot;heading&quot;&amp;gt;Charts&amp;lt;/div&amp;gt;
                        &amp;lt;div id=&quot;chartCarousel&quot; class=&quot;carousel slide&quot;&amp;gt;
                            &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
                                &amp;lt;li data-target=&quot;#chartCarousel&quot;
                                    ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot;
                                    data-slide-to=&quot;{{$index}}&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;&amp;lt;/li&amp;gt;
                            &amp;lt;/ol&amp;gt;
                            &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
                                &amp;lt;div class=&quot;item chart&quot;
                                     ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot;
                                     ng-class=&quot;{active: $index == 0}&quot;&amp;gt;
                                    &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget}}&quot; type=&quot;{{widget.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
                                &amp;lt;/div&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#chartCarousel&quot; data-slide=&quot;prev&quot;&amp;gt;&#8249;&amp;lt;/a&amp;gt;
                            &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#chartCarousel&quot; data-slide=&quot;next&quot;&amp;gt;&#8250;&amp;lt;/a&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/li&amp;gt;
                &amp;lt;/ul&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;span3&quot;&amp;gt;
                &amp;lt;!-- clock and reports --&amp;gt;
            &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The beginning of this HTML shows how Angular is instantiated: &lt;strong&gt;ng-app&lt;/strong&gt; matches the name defined in &lt;code&gt;app.js&lt;/code&gt;, &lt;strong&gt;ng-controller&lt;/strong&gt; instantiates the &lt;code&gt;WidgetController&lt;/code&gt; and &lt;strong&gt;ng-cloak&lt;/strong&gt; is used to hide everything until its processed.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div ng-app=&quot;dashboard&quot; class=&quot;dashboard&quot;&amp;gt;
    &amp;lt;div class=&quot;container-widgets&quot; ng-controller=&quot;WidgetController&quot; ng-cloak&amp;gt;
&lt;/pre&gt;
&lt;p&gt;If you take a closer look at the way ng-repeat attributes, you&apos;ll see how &lt;strong&gt;filters&lt;/strong&gt; are used to filter data. There&apos;s &lt;a href=&quot;http://docs.angularjs.org/api/ng.filter:filter&quot;&gt;&lt;em&gt;filter&lt;/em&gt;&lt;/a&gt;&lt;em&gt; &lt;/em&gt;and &lt;a href=&quot;http://docs.angularjs.org/api/ng.filter:orderBy&quot;&gt;&lt;em&gt;orderBy&lt;/em&gt;&lt;/a&gt; filters that are built in and allow you to filter data. The &lt;em&gt;filter &lt;/em&gt;filter allows you to query arrays by strings, objects and even functions. In the following code block, &quot;task&quot; widgets are filtered, ordered and displayed.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;li class=&quot;task {{widget.class}}&quot; ng-repeat=&quot;widget in widgets | filter: {type: &apos;task&apos;} | orderBy: &apos;order&apos;&quot;&amp;gt;
    &amp;lt;div class=&quot;title events&quot;&amp;gt;{{widget.title}}&amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;value&quot;&amp;gt;{{widget.value}}&amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This was pretty straightforward, but we quickly noticed that if a widget had HTML in its title, it didn&apos;t display correctly (rendering the raw HTML). To process the HTML, we had to use the &lt;a href=&quot;http://docs.angularjs.org/api/ngSanitize.directive:ngBindHtml&quot;&gt;ngBindHtml&lt;/a&gt; directive (tip: directives are camelCase, but written with dashes in HTML).&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div class=&quot;title events&quot; ng-bind-html=&quot;widget.title&quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;After getting this to work, we noticed that some titles weren&apos;t fully rendered because they were hidden with overflow: hidden. We tried adding a tooltip with &lt;code&gt;title=&quot;{{widget.title}}&quot;&lt;/code&gt;, but ran into the same issue. I &lt;a href=&quot;https://groups.google.com/d/topic/angular/hG-T1bsmlnk/discussion&quot;&gt;sent an email &lt;/a&gt;to the AngularJS Google Group and received a solution: create an htmlTitle directive:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.directive(&apos;htmlTitle&apos;, function ($sanitize) {
    return {
        restrict: &apos;A&apos;,
        link: function (scope, element, attrs) {
            attrs.$observe(&apos;htmlTitle&apos;, function (title) {
                // convert &amp;amp;value; to HTML
                var html = angular.element(&apos;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&apos;).html($sanitize(title)).text();
                element.attr(&apos;title&apos;, html);
                element.html(html);
            });
        }
    }
})
&lt;/pre&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div class=&quot;title events&quot; ng-bind-html=&quot;widget.title&quot; html-title=&quot;{{widget.title}}&quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;h3 id=&quot;dragndrop&quot;&gt;Drag-and-Drop&lt;/h3&gt;
&lt;p&gt;To implement drag-and-drop functionality, I originally used jQuery UI&apos;s &lt;a href=&quot;http://jqueryui.com/sortable/&quot;&gt;sortable&lt;/a&gt;. At the bottom of the page, the following code initialized sorting for the various lists:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$(document).ready(function() {
    $(&apos;.widgets&apos;).sortable({
        cursor: &quot;move&quot;,
        handle: &quot;.heading&quot;
    }).disableSelection();
    $(&apos;.tiles,.tasks&apos;).sortable();
    var carousel = $(&apos;.carousel&apos;);
    $(carousel).carousel({
        interval: 0
    });
};
&lt;/pre&gt;
&lt;p&gt;As you can see, it also initializes the carousel and stops it from cycling automatically.&lt;/p&gt;
&lt;h3 id=&quot;carousel-issues&quot;&gt;Carousel Issues&lt;/h3&gt;
&lt;p&gt;The first problem I ran into with Bootstrap&apos;s Carousel was a strange error from Highcharts. If you look in the above HTML, you&apos;ll see there&apos;s a &lt;code&gt;&amp;lt;chart&amp;gt;&lt;/code&gt; element. This is processed by a &lt;a href=&quot;https://github.com/rootux/angular-highcharts-directive/blob/master/src/directives/highchart.js&quot;&gt;highcharts directive&lt;/a&gt;. When I tried to use this directive for Highcharts in a carousel, it results in the following error:&lt;/p&gt;
&lt;p class=&quot;alert alert-error&quot;&gt;
TypeError: Cannot read property &apos;length&apos; of undefined at Object.ob.setMaxTicks
&lt;/p&gt;
&lt;p&gt;This seemed to be caused by the following css in Bootstrap:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.carousel-inner &gt; .item { display: none }
&lt;/pre&gt;
&lt;p&gt;When I added an override with &quot;display: block&quot; to my stylesheet, everything worked, but the charts were stacked instead of in a carousel. To fix this, I modified the directive to show/hide the &quot;item&quot; element so Highcharts was able to write to it. I also &lt;a href=&quot;https://github.com/rootux/angular-highcharts-directive/issues/1&quot;&gt;logged an issue&lt;/a&gt; for this.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
if (element.parent().not(&apos;:visible&apos;)) {
    element.parent().show();
}
var chart = new Highcharts.Chart(newSettings);
element.parent().attr(&apos;style&apos;, &apos;&apos;);
&lt;/pre&gt;
&lt;h3 id=&quot;grouping&quot;&gt;ngRepeat and Grouping&lt;/h3&gt;
&lt;p&gt;The last thing I accomplished in our end-of-January prototype was rendering 2 charts side-by-side. I got it working with plain HTML, created a &quot;groupBy&quot; filter for Angular and tried to get it to work with the following:&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div id=&quot;chartCarousel&quot; class=&quot;carousel slide&quot;&amp;gt;
    &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
        &amp;lt;li data-target=&quot;#chartCarousel&quot; ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | groupBy&quot;
            data-slide-to=&quot;{{$index}}&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/ol&amp;gt;
    &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
        &amp;lt;div class=&quot;item&quot; ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | groupBy&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;
            &amp;lt;div class=&quot;widget&quot;&amp;gt;{{widget&amp;#91;0&amp;#93;.title}}&amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;widget&quot;&amp;gt;{{widget&amp;#91;1&amp;#93;.title}}&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#chartCarousel&quot; data-slide=&quot;prev&quot;&amp;gt;&#8249;&amp;lt;/a&amp;gt;
    &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#chartCarousel&quot; data-slide=&quot;next&quot;&amp;gt;&#8250;&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This all worked like I expected it to in Chrome, but I the following errors showed in my console.&lt;/p&gt;
&lt;p class=&quot;alert alert-error&quot;&gt;
Error: 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations:
&lt;/p&gt;
&lt;p&gt;I &lt;a href=&quot;https://groups.google.com/d/topic/angular/gEv1-YV-Ojg/discussion&quot;&gt;sent an email&lt;/a&gt; to the Angular Google Group and received a link to &lt;a href=&quot;https://groups.google.com/d/msg/angular/IEIQok-YkpU/oKuLvzCnAcoJ&quot;&gt;a discussion&lt;/a&gt; where I found a &quot;chunk&quot; filter that solved the problem. This worked great, but I wanted to make it more responsive.&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;If the user has a screen size big enough to fit 2 charts, show 2 charts and paginate by 2.&lt;/li&gt;&lt;li&gt;If the user has a small screen size that only fits 1 chart, show 1 and paginate by 1.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;To solve #1 and #2, I ended up rendering two different sections (with classes .oneup and .twoup) and displayed them based on screen size.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;li id=&quot;chart-bar&quot;&amp;gt;
    &amp;lt;div class=&quot;heading&quot;&amp;gt;Charts&amp;lt;/div&amp;gt;
    &amp;lt;div id=&quot;chartCarousel1&quot; class=&quot;carousel slide oneup&quot; style=&quot;display: none&quot;&amp;gt;
        &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
            &amp;lt;li data-target=&quot;#chartCarousel1&quot;
                ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot;
                data-slide-to=&quot;{{$index}}&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ol&amp;gt;
        &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
            &amp;lt;div class=&quot;item chart&quot;
                 ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot;
                 ng-class=&quot;{active: $index == 0}&quot;&amp;gt;
                &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget}}&quot; type=&quot;{{widget.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#chartCarousel1&quot; data-slide=&quot;prev&quot;&amp;gt;&#8249;&amp;lt;/a&amp;gt;
        &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#chartCarousel1&quot; data-slide=&quot;next&quot;&amp;gt;&#8250;&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div id=&quot;chartCarousel2&quot; class=&quot;carousel slide twoup&quot; style=&quot;display: none&quot;&amp;gt;
        &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
            &amp;lt;li data-target=&quot;#chartCarousel2&quot;
                ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | chunk: 2 | orderBy: &apos;order&apos;&quot;
                data-slide-to=&quot;{{$index}}&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ol&amp;gt;
        &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
            &amp;lt;div class=&quot;item chart&quot;
                 ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | chunk: 2 | orderBy: &apos;order&apos;&quot;
                 ng-class=&quot;{active: $index == 0}&quot;&amp;gt;
                &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget&amp;#91;0&amp;#93;}}&quot; type=&quot;{{widget&amp;#91;0&amp;#93;.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
                &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget&amp;#91;1&amp;#93;}}&quot; type=&quot;{{widget&amp;#91;1&amp;#93;.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#chartCarousel2&quot; data-slide=&quot;prev&quot;&amp;gt;&#8249;&amp;lt;/a&amp;gt;
        &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#chartCarousel2&quot; data-slide=&quot;next&quot;&amp;gt;&#8250;&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The JavaScript to show the correct number of charts is below:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
var chartBar = $(&apos;#chart-bar&apos;);
function showCharts() {
    if (chartBar.width() &amp;lt; 960) {
        chartBar.find(&apos;.oneup&apos;).show();
        chartBar.find(&apos;.twoup&apos;).hide();
    } else {
        chartBar.find(&apos;.twoup&apos;).show();
        chartBar.find(&apos;.oneup&apos;).hide();
    }
}

$(document).ready(function () {
    showCharts();
});

$(window).resize(showCharts);
&lt;/pre&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Even though I got everything to work for our initial prototype using Angular and jQuery, it didn&apos;t quite feel like I was taking full advantage of Angular&apos;s power. In particular, I learned that Angular UI Bootstrap had their own carousel and Angular UI had a &lt;em&gt;sortable&lt;/em&gt; directive. My suspicions were confirmed when I read &lt;a href=&quot;http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background&quot;&gt;How do I &quot;think in AngularJS&quot; if I have a jQuery background?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii&quot;&gt;next article&lt;/a&gt;, I&apos;ll talk about how I migrated to use Angular UI&apos;s &lt;a href=&quot;http://angular-ui.github.io/bootstrap/#/carousel&quot;&gt;carousel&lt;/a&gt; and &lt;a href=&quot;https://github.com/angular-ui/ui-sortable&quot;&gt;sortable&lt;/a&gt; directives, as well as integrating dialogs.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/my_bootstrap_presentation_from_html5</id>
        <title type="html">My Bootstrap Presentation from HTML5 Denver</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/my_bootstrap_presentation_from_html5"/>
        <published>2013-04-23T10:50:07-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="css3" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5denver" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;Last year, I worked on a huge &lt;a href=&quot;http://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with&quot;&gt;redesign of Taleo&apos;s UI with HTML5, Twitter Bootstrap and CSS3&lt;/a&gt;. Management thought it would take 6-9 months and my colleague (&lt;a href=&quot;http://www.linkedin.com/in/vladimirbazarsky&quot;&gt;Vladimir Bazarsky&lt;/a&gt;) and I finished it in just over 3 months. Yes, we encountered many, many cross-browser compatibility issues in the process. While in QA, we found and fixed over 750 issues. This was no small feat since the app was over 2 million lines of code and contained 1700 JSPs.
&lt;/p&gt;
&lt;p&gt;After writing about working with Bootstrap, I was contacted by my good friends, &lt;a href=&quot;https://twitter.com/davidgeary&quot;&gt;David Geary&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/scottdavis99&quot;&gt;Scott Davis&lt;/a&gt; to speak at the &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/&quot;&gt;HTML5 Denver Users Group&lt;/a&gt;. Scott was &lt;em&gt;very&lt;/em&gt; persuasive with his words (a.k.a. lots of trash-talking) and I chuckled as I read one of the best emails I&apos;ve ever received. I replied that his strategy worked, I&apos;d come up with a excellent topic and agreed to speak in April.&lt;/p&gt;
&lt;p&gt;That speaking engagement was &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/events/93212272/&quot;&gt;last night&lt;/a&gt; and you can  &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/Refreshing_Your_UI_with_HTML5_Bootstrap_and_CSS3_HTML5Denver2013/#1&quot;&gt;view my presentation as an HTML5 app&lt;/a&gt; or &lt;a href=&quot;http://www.slideshare.net/mraible/refreshing-your-ui-with-html5-bootstrap-and-css3&quot;&gt;on SlideShare&lt;/a&gt;.&lt;/p&gt;
&lt;div style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/19776479?rel=0&quot; width=&quot;512&quot; height=&quot;421&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px&quot; allowfullscreen webkitallowfullscreen mozallowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Much of the Bootstrap content comes from &lt;a href=&quot;http://danvega.org/&quot;&gt;Dan Vega&lt;/a&gt;. He built a Bootstrap presentation last August using the &lt;a href=&quot;https://code.google.com/p/io-2012-slides/&quot;&gt;HTML5 slide template for Google I/O 2012&lt;/a&gt; and &lt;a href=&quot;https://github.com/cfaddict/bootstrap2&quot;&gt;put it on GitHub&lt;/a&gt;. In an email to the Bootstrap mailing list, &lt;a href=&quot;https://groups.google.com/forum/?fromgroups=#!topic/twitter-bootstrap/AjkFYYON3Xs&quot;&gt;he wrote&lt;/a&gt; &quot;feel free to use the slide deck if you plan on telling others about this awesome product&quot; and that&apos;s exactly what I did. 
&lt;/p&gt;
&lt;p&gt;
I updated all the statistics, added my redesign story, included a few slides on &lt;a href=&quot;http://smacss.com/&quot;&gt;Scalable and Modular Architecture for CSS&lt;/a&gt; and beautified it with &lt;a href=&quot;http://www.mcginityphoto.com/&quot;&gt;Trish&apos;s Photos&lt;/a&gt;. I&apos;ve &lt;a href=&quot;https://github.com/mraible/bootstrap2&quot;&gt;published the result on GitHub&lt;/a&gt; and encourage you to fork it. 
&lt;/p&gt;
&lt;p&gt;Speaking at HTML5 Denver was a real treat. The venue, &lt;a href=&quot;http://casselmans.com/&quot;&gt;Casselman&apos;s&lt;/a&gt;, was awesome. It has a huge room with a proper stage, sound system and lighting. If you&apos;ve done something cool with HTML5 lately, I encourage you to signup for a &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/events/93212472/&quot;&gt;10-minute lightning talk next month&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;Not only was the venue great, but the cold Guinness while speaking was delicious. It was also a nice networking opportunity. I met several folks afterwards and talked about what&apos;s next for me. My contract with Taleo/Oracle ends May 31st, hence the reason &quot;Free Agent&quot; is listed on my &lt;a href=&quot;http://www.linkedin.com/in/mraible&quot;&gt;LinkedIn profile&lt;/a&gt;. I&apos;ve got a few good opportunities so far, but nothing that I&apos;ve agreed to yet. I expect negotiations to heat up in the coming weeks, so please &lt;a href=&quot;http://raibledesigns.com/contact.jsp&quot;&gt;let me know&lt;/a&gt; if you&apos;d like a seat at the bargaining table. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot; /&gt;&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/responsive_design_with_css_media</id>
        <title type="html">Responsive Design with CSS Media Queries</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/responsive_design_with_css_media"/>
        <published>2013-04-10T11:38:12-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="mobile" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <category term="responsivedesign" scheme="http://roller.apache.org/ns/tags/" />
        <category term="iphone" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">In preparation for &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/events/93212272/&quot;&gt;my upcoming talk on Bootstrap and CSS3&lt;/a&gt;, I figured it was high time I made this blog responsive. Making my new theme mobile-friendly has been on my todo list ever since &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;last year&apos;s redesign&lt;/a&gt;. I also took the opportunity to 1) remove the geolocation lookup and associated sunset logic for theme selection and 2) change the default theme to the &quot;light&quot; one. If you prefer dark, just click on the black rectangle in the top-right.
&lt;/p&gt;
&lt;p&gt;I started by refreshing my knowledge of media queries by reading &lt;a href=&quot;http://css-tricks.com/css-media-queries/&quot;&gt;CSS Media Queries &amp; Using Available Space&lt;/a&gt;. This led me to &lt;a href=&quot;http://hicksdesign.co.uk/&quot;&gt;John Hick&apos;s site&lt;/a&gt;, where I grabbed a couple of his rules. Most notably, I started optimizing for iPhone, smartphones and any screens less than 1000px wide. I also decided to widen things a bit for larger screens.&lt;/p&gt;
&lt;pre&gt;
/* Smartphones */
@media handheld and (max-width: 480px), screen and (max-device-width: 480px), screen and (max-width: 1000px) {

}

/* Large Desktop */
@media screen and (min-width: 1200px) {

}
&lt;/pre&gt;
&lt;p&gt;
When I started hacking away, I noticed a lot of the elements had &quot;width&quot; hard-coded in pixels, so I changed a lot of these to use &quot;max-width&quot; instead, as well as &quot;width: 100%&quot;. This allowed me to only have a few elements that controlled the width and I was able to take advantage of larger screens with the following simple rules.&lt;/p&gt;
&lt;pre&gt;
@media screen and (min-width: 1200px) {
    #site-container, #body-content, #header-inner {
        max-width: 1170px;
    }

    #body-content #left-column, .next-previous {
        max-width: 808px;
    }
}
&lt;/pre&gt;
&lt;p&gt;For smartphones, I started by hiding the right column and menus with display: none. Then I decided the navigation menu might be useful if I ever wanted to manage the site on my phone. I found &lt;a href=&quot;http://blog.teamtreehouse.com/create-an-absolute-basic-mobile-css-responsive-navigation-menu&quot;&gt;Create an Absolute Basic Mobile CSS Responsive Navigation Menu&lt;/a&gt; from the treehouse blog and went to work. 20 minutes later, I had the menu I was looking for and everything was looking pretty good in iOS Simulator.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm9.staticflickr.com/8104/8637163765_97cb072017_o.png&quot; title=&quot;raibledesigns.com on iOS Simulator&quot; rel=&quot;lightbox[responsive-design]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8104/8637163765_db8b960779_m.jpg&quot; width=&quot;128&quot; height=&quot;240&quot; alt=&quot;raibledesigns.com on iOS Simulator&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;At this point, I discovered that rotating to landscape mode caused the content font to become quite a bit larger. So I added a rule for the iPhone in landscape mode.&lt;/p&gt;
&lt;pre&gt;
/* iPhone Landscape */
@media screen and (min-width: 321px) and (max-width: 480px) and (orientation: landscape) {
    #body-content #left-column .article .contents {
        font-size: 9px;
    }
}
&lt;/pre&gt;
&lt;p&gt;I&apos;m still not sure why this happens. I also noticed that rotating back to portrait causes some empty whitespace to show up on the right. The empty whitespace was a problem I spent most of my time solving. I ended up using &lt;a href=&quot;http://webdesign.tutsplus.com/tutorials/workflow-tutorials/quick-tip-using-web-inspector-to-debug-mobile-safari/&quot;&gt;web inspector with iOS Simulator&lt;/a&gt; to figure out which elements were bleeding past my desired width and then adjusted their width, or used overflow: hidden to hide them.
&lt;/p&gt;
&lt;p&gt;
I enjoyed this mobile design experience, even with the many &quot;why is there whitespace there!&quot; moments. I especially liked it when I found I didn&apos;t need any rules for the iPad. I&apos;m sure there&apos;s a few issues that still exist. For example, I haven&apos;t tested it on an Android device. Also, the Facebook button&apos;s &quot;color-scheme&quot; is still hard-coded in my templates. I hope to fix this by patching Roller to allow reading cookies on the server-side. If you notice anything that looks funny, please let me know. 
&lt;/p&gt;
&lt;p&gt;In the meantime, I hope you enjoy squishing and stretching your modern browser to see how this new responsive design works.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/2012_a_year_in_review</id>
        <title type="html">2012 - A Year in Review</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/2012_a_year_in_review"/>
        <published>2013-01-08T13:15:24-07:00</published>
        <updated>2014-05-08T19:47:26-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="2012" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yearinreview" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <category term="blogging" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I wrote my first &lt;a href=&quot;http://raibledesigns.com/rd/tags/yearinreview&quot;&gt;year in review&lt;/a&gt; blog entry way back in &lt;a href=&quot;http://raibledesigns.com/rd/entry/2005_a_year_in_review&quot;&gt;2005&lt;/a&gt;. That means this year&apos;s is number 8. Since they keep getting longer every year, I figured I&apos;d try something different this year and use sections similar to &lt;a href=&quot;http://remysharp.com/2012/12/31/my-2012/&quot;&gt;Remy Sharp&lt;/a&gt;. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#professional&quot;&gt;Professional&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#business&quot;&gt;Business&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#speaking&quot;&gt;Speaking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#projects&quot;&gt;Projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#personal&quot;&gt;Personal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#2013&quot;&gt;2013&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;professional&quot;&gt;Professional&lt;/h2&gt;
&lt;p&gt;I spent the entirety of the year with one client: Taleo. &lt;a href=&quot;http://www.businessweek.com/news/2012-02-10/oracle-buys-taleo-for-1-9-billion-adds-human-resource-tool.html&quot;&gt;Oracle bought them in February&lt;/a&gt;. In June, the transition to Oracle happened. My tasks and projects haven&apos;t changed much since the transition, but it has been a real pain to get paid on time. My contract with them is through the end of May. I hope to take July off (to get married) and August off (to honeymoon) and start a new gig in September.
&lt;/p&gt;
&lt;p&gt;I did minimal Java work throughout the year and spent most of my time doing CSS and JavaScript. I love doing front-end work much more than back-end, so day-to-day, it was very satisfying. 
&lt;/p&gt;</summary>
        <content type="html">I wrote my first &lt;a href=&quot;http://raibledesigns.com/rd/tags/yearinreview&quot;&gt;year in review&lt;/a&gt; blog entry way back in &lt;a href=&quot;http://raibledesigns.com/rd/entry/2005_a_year_in_review&quot;&gt;2005&lt;/a&gt;. That means this year&apos;s is number 8. Since they keep getting longer every year, I figured I&apos;d try something different this year and use sections similar to &lt;a href=&quot;http://remysharp.com/2012/12/31/my-2012/&quot;&gt;Remy Sharp&lt;/a&gt;. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#professional&quot;&gt;Professional&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#business&quot;&gt;Business&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#speaking&quot;&gt;Speaking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#projects&quot;&gt;Projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#personal&quot;&gt;Personal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#2013&quot;&gt;2013&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;professional&quot;&gt;Professional&lt;/h2&gt;
&lt;p&gt;I spent the entirety of the year with one client: Taleo. &lt;a href=&quot;http://www.businessweek.com/news/2012-02-10/oracle-buys-taleo-for-1-9-billion-adds-human-resource-tool.html&quot;&gt;Oracle bought them in February&lt;/a&gt;. In June, the transition to Oracle happened. My tasks and projects haven&apos;t changed much since the transition, but it has been a real pain to get paid on time. My contract with them is through the end of May. I hope to take July off (to get married) and August off (to honeymoon) and start a new gig in September.
&lt;/p&gt;
&lt;p&gt;I did minimal Java work throughout the year and spent most of my time doing CSS and JavaScript. I love doing front-end work much more than back-end, so day-to-day, it was very satisfying. 
&lt;/p&gt;

&lt;h3 id=&quot;business&quot;&gt;Business&lt;/h3&gt;
&lt;p&gt;Raible Designs had a great year, our best ever financially. I hired Trish as an assistant in January, doubling the size of the company. We had our Annual Shareholders Meeting on a &lt;a href=&quot;http://raibledesigns.com/rd/entry/cruising_around_the_western_caribbean&quot;&gt;Disney Cruise&lt;/a&gt; in April. I have no plans to do increase the size of the company in 2013, mostly because I enjoy not having the responsibility of employees. I&apos;ve employed sub-contractors in the past and it always seems to create more of a headache than it&apos;s worth.&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://www.dailymile.com/people/mraible/training/2012/summary&quot; title=&quot;My Daily Mile 2012 Year End Report&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8227/8360832357_858f34a10e_t.jpg&quot; width=&quot;100&quot; height=&quot;52&quot; alt=&quot;Daily Mile 2012 Year End Report&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
We moved our offices from downtown Littleton to &lt;a href=&quot;http://www.businessatthrive.com/v3/&quot;&gt;Thrive&lt;/a&gt; (in LoDo) last April and I plan on staying there. It&apos;s an easy 6-mile bicycle commute and the guys there have been great to work with. I highly recommend it if you&apos;re looking for co-working space.&lt;/p&gt;

&lt;h3 id=&quot;speaking&quot;&gt;Speaking&lt;/h3&gt;
&lt;p&gt;I spoke at 5 events in 2012:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Silicon Valley Spring User Group on &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_what_s_new_in&quot;&gt;What&apos;s New in Spring 3.1&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;JFokus on &lt;a href=&quot;http://raibledesigns.com/rd/entry/comparing_web_frameworks_and_html5&quot;&gt;Comparing Web Frameworks and HTML5 with Play Scala&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Spring I/O on &lt;a href=&quot;http://www.youtube.com/watch?v=QlQMt3W9fpU&quot;&gt;Comparing JVM Web Frameworks&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;JavaPosse Roundup on &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_build_a_shot&quot;&gt;How to build a Shot-Ski&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&#220;berConf on &lt;a href=&quot;http://raibledesigns.com/rd/entry/play_vs_grails_smackdown_at&quot;&gt;Play vs. Grails&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Traveling to &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_spectacular_trip_to_stockholm&quot;&gt;Stockholm and Madrid for JFokus and Spring I/O was spectacular&lt;/a&gt;. &lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8350765961/&quot; href=&quot;http://farm9.staticflickr.com/8357/8350765961_9eed5c5ee8.jpg&quot; title=&quot;James Ward and Enno Runne by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8357/8350765961_9eed5c5ee8_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;James Ward and Enno Runne by Trish McGinity&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351826862/&quot; href=&quot;http://farm9.staticflickr.com/8233/8351826862_87c90d6990.jpg&quot; title=&quot;Juergen Hoeller&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8233/8351826862_87c90d6990_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Juergen Hoeller&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351825042/&quot; href=&quot;http://farm9.staticflickr.com/8227/8351825042_0a15401fa8.jpg&quot; title=&quot;Speakers Dinner Singers by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8227/8351825042_0a15401fa8_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Speakers Dinner Singers&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351823962/&quot; href=&quot;http://farm9.staticflickr.com/8091/8351823962_5b8c06859f.jpg&quot; title=&quot;Matt Raible James Ward Rickard Oberg Jfokus speakers dinner by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8091/8351823962_5b8c06859f_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Matt Raible James Ward Rickard Oberg Jfokus speakers dinner&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351907734/&quot; href=&quot;http://farm9.staticflickr.com/8191/8351907734_28c5dd5e83.jpg&quot; title=&quot;Stockholm View by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8191/8351907734_28c5dd5e83.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Stockholm View&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;/p&gt;
    
    &lt;p style=&quot;text-align: center&quot;&gt;

    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8355701252/&quot; href=&quot;http://farm9.staticflickr.com/8509/8355701252_3f507c646a.jpg&quot; title=&quot;Templo de Debod by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8509/8355701252_3f507c646a.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Templo de Debod&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;&lt;/p&gt;
    
    &lt;p style=&quot;text-align: center&quot;&gt;

    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351930566/&quot; href=&quot;http://farm9.staticflickr.com/8366/8351930566_bb5f27f19a.jpg&quot; title=&quot;Iglesia San Gines by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8366/8351930566_bb5f27f19a_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Iglesia San Gines&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8355691858/&quot; href=&quot;http://farm9.staticflickr.com/8050/8355691858_8878582d57.jpg&quot; title=&quot;Evening Commute Puerta de Alcala Madrid by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8050/8355691858_8878582d57_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Evening Commute Puerta de Alcala Madrid&quot; style=&quot;margin-left: 15px; border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;/p&gt;
    
    
&lt;p&gt;I took the 2nd half of the year off from speaking, celebrating my temporary retirement at a &lt;a href=&quot;http://twitter.com/mraible/status/217459157689831424&quot;&gt;Def Leppard concert&lt;/a&gt; in June. When I decided to do this, I was planning on my VW Bus being finished. I was planning on switching from Java Conferences to VW Shows as a hobby. Unfortunately, now it&apos;s January and &lt;a href=&quot;http://raibledesigns.com/roller-ui/authoring/preview/rd/category/The+Bus&quot;&gt;The Bus&lt;/a&gt; still isn&apos;t done. I have high hopes for it being finished in 2013.
&lt;/p&gt;
&lt;p&gt;I paid for 2 conferences in 2012, the &lt;a href=&quot;http://raibledesigns.com/rd/entry/spring_break&quot;&gt;JavaPosse Roundup&lt;/a&gt; in March and &lt;a href=&quot;http://monktoberfest.com/&quot;&gt;Monktoberfest&lt;/a&gt; in October. While I enjoyed both, Monktoberfest will likely become a tradition. 
&lt;/p&gt;
&lt;h3 id=&quot;projects&quot;&gt;Projects&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;AppFuse:&lt;/strong&gt; I started the year by &lt;a href=&quot;http://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with&quot;&gt;refreshing AppFuse&apos;s UI with Twitter Bootstrap&lt;/a&gt; and blogged about the &lt;a href=&quot;http://raibledesigns.com/rd/entry/twitter_s_open_source_summit&quot;&gt;Twitter&apos;s Summit on Bootstrap&lt;/a&gt; shortly after. At the same time, I was overhauling &lt;a href=&quot;http://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with&quot;&gt;Taleo&apos;s UI with HTML5, Bootstrap and CSS3&lt;/a&gt;, but waited until August to blog about it.&lt;/p&gt;
&lt;p&gt;In the fall, the AppFuse Team &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_news_github_hibernate_search&quot;&gt;migrated to GitHub and integrated Hibernate Search&lt;/a&gt;. &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_2_1_released&quot;&gt;AppFuse 2.2.1 was released&lt;/a&gt; in December.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Happy Trails:&lt;/strong&gt; In May and June, I worked on &lt;a href=&quot;https://github.com/jamesward/happytrails&quot;&gt;Happy Trails&lt;/a&gt; with James Ward as part of our &lt;a href=&quot;http://raibledesigns.com/rd/entry/play_vs_grails_smackdown_at&quot;&gt;Play vs. Grails Smackdown&lt;/a&gt;. This was a very enjoyable experience and I learned a lot about GitHub, Cloudbees and Heroku. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Play More:&lt;/strong&gt; In June, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgrading_to_play_2_anorm&quot;&gt;Upgrading to Play 2: Anorm and Testing&lt;/a&gt;. A couple weeks later, I wrote about integrating &lt;a href=&quot;http://raibledesigns.com/rd/entry/migrating_to_play_2_and&quot;&gt;Validation and Secure Social&lt;/a&gt; into &lt;a href=&quot;http://play-more.com&quot;&gt;&lt;em&gt;Play More&lt;/em&gt;&lt;/a&gt;. In the aforementioned post, I also posted my &lt;a href=&quot;http://www.slideshare.net/mraible/html5-with-play-scala-coffeescript-and-jade-uberconf-2012&quot;&gt;&#220;berConf presentation&lt;/a&gt;, the &lt;a href=&quot;https://github.com/mraible/play-more&quot;&gt;source code&lt;/a&gt; and my thoughts on Play 2.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
I found Anorm and Scalate to be &lt;em&gt;huge&lt;/em&gt; time sinks and don&apos;t know if I&apos;d recommend using either one in a Play 2 project. I&apos;m sure Scalate will be easier to use as its Play 2 integration gets more refined, but I don&apos;t know if there&apos;s any hope for a JDBC abstraction that doesn&apos;t produce error messages when things go south.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Roller:&lt;/strong&gt; Although I didn&apos;t contribute much to &lt;a href=&quot;http://roller.apache.org&quot;&gt;Apache Roller&lt;/a&gt; in 2012, I do continue to use it frequently. This site celebrated its &lt;a href=&quot;http://raibledesigns.com/rd/entry/10_years_ago&quot;&gt;10-year anniversary&lt;/a&gt; in August. It got a &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;new look and feel&lt;/a&gt; shortly after.&lt;/p&gt; 
&lt;h2 id=&quot;personal&quot;&gt;Personal&lt;/h2&gt;
&lt;p&gt;Last year was a fantastic year for family time. Our &lt;a href=&quot;http://raibledesigns.com/rd/entry/cruising_around_the_western_caribbean&quot;&gt;cruise around the Western Caribbean&lt;/a&gt; was money well spent.&lt;/p&gt;&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358223863/&quot; href=&quot;http://farm9.staticflickr.com/8092/8358223863_5b7c2d31eb.jpg&quot; title=&quot;Raible&apos;s and McGinity&apos;s welcome to our Disney Cruise! by Trish, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8092/8358223863_5b7c2d31eb.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Raible&apos;s and McGinity&apos;s welcome to our Disney Cruise!&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358600474/&quot; href=&quot;http://farm9.staticflickr.com/8213/8358600474_ea26b2cc90.jpg&quot; title=&quot;Raible and McGinity dinner by Trish, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8213/8358600474_ea26b2cc90.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Raible and McGinity dinner&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;p&gt;My goal was to spend a month with my parents this year and we satisfied that by July. In total, we enjoyed their company for 8 weeks in 2012. 
&lt;/p&gt;
&lt;p&gt;
The worst ski season on record &lt;a href=&quot;http://raibledesigns.com/rd/entry/farewell_to_the_2011_2012&quot;&gt;ended in April&lt;/a&gt; and Trish and I headed for &lt;a href=&quot;http://raibledesigns.com/rd/entry/wine_tasting_in_napa_valley&quot;&gt;wine country&lt;/a&gt; in May.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://www.mcginityphoto.com/photos/i-cDtj2v6/1/M/i-cDtj2v6-M.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;Cade Vineyard Cave Table&quot;&gt;
    &lt;img alt=&quot;Cade Vineyard Cave Table&quot; src=&quot;//www.mcginityphoto.com/photos/i-cDtj2v6/1/Th/i-cDtj2v6-Th.jpg&quot; width=&quot;150&quot; height=&quot;100&quot;  alt=&quot;Cade Vineyard Cave Table&quot; style=&quot;border: 1px solid black&quot;&gt;
    &lt;/a&gt;
    
    &lt;a href=&quot;http://www.mcginityphoto.com/photos/i-mCJJZR3/0/M/i-mCJJZR3-M.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;Cade Vineyard Cask Row&quot;&gt;
    &lt;img src=&quot;//www.mcginityphoto.com/photos/i-mCJJZR3/0/Th/i-mCJJZR3-Th.jpg&quot; width=&quot;150&quot; height=&quot;99&quot;  alt=&quot;Cade Vineyard Cask Row&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
    
    &lt;a href=&quot;http://www.mcginityphoto.com/photos/i-r4fmDX2/0/M/i-r4fmDX2-M.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;Cade Flowers next to infinity pool&quot;&gt;
    &lt;img alt=&quot;Cade Flowers next to infinity pool&quot; src=&quot;//www.mcginityphoto.com/photos/i-r4fmDX2/0/Th/i-r4fmDX2-Th.jpg&quot; width=&quot;150&quot; height=&quot;100&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;/p&gt;
    &lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://www.mcginityphoto.com/photos/i-R9tmTz4/0/M/i-R9tmTz4-M.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;Cade Winery&quot;&gt;
    &lt;img alt=&quot;Cade Winery&quot; src=&quot;//www.mcginityphoto.com/photos/i-R9tmTz4/0/S/i-R9tmTz4-S.jpg&quot; width=&quot;400&quot; height=&quot;266&quot; title=&quot;Cade Winery&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    
    &lt;/p&gt;
    
    &lt;/p&gt;
    &lt;p&gt;Abbie and Jack&apos;s school year &lt;a href=&quot;http://raibledesigns.com/rd/entry/school_s_out_for_summer&quot;&gt;ended&lt;/a&gt; on June 5th and we &lt;a href=&quot;http://raibledesigns.com/rd/entry/father_s_day_weekend_at&quot;&gt;celebrated Father&apos;s Day on the Oregon coast&lt;/a&gt;.&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a data-href=&quot;http://farm8.staticflickr.com/7284/9071981163_9ecc04ec8f_c.jpg&quot; title=&quot;View from Ecola State Park Oregon&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7284/9071981163_9ecc04ec8f.jpg&quot; width=&quot;500&quot; height=&quot;332&quot; alt=&quot;View from Ecola State Park Oregon&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9071962289/&quot; href=&quot;http://farm4.staticflickr.com/3675/9071962289_a7fc891f15_c.jpg&quot; title=&quot;Raible Family on the Oregon coast&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3675/9071962289_a7fc891f15_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Raible Family on the Oregon coast&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9074185440/&quot; href=&quot;http://farm8.staticflickr.com/7404/9074185440_eebccb9acf_c.jpg&quot; title=&quot;Abbie and Jack frolicking in the waves&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7404/9074185440_eebccb9acf_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Abbie and Jack frolicking in the waves&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9074188854/&quot; href=&quot;http://farm3.staticflickr.com/2814/9074188854_f0b09a43ca_c.jpg&quot; title=&quot;Kalin and Joe on the beach&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2814/9074188854_f0b09a43ca_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Kalin and Joe on the beach&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;

    &lt;p&gt;Raible Road Trip #17 began in late June and we spent two weeks parading, rafting and golfing in Montana. From there, we flew to Maui for two weeks. I wrote about it all in &lt;a href=&quot;http://raibledesigns.com/rd/entry/summer_vacation_2012_in_montana&quot;&gt;Summer Vacation 2012 in Montana and Maui&lt;/a&gt;.
        &lt;/p&gt;
        &lt;p style=&quot;text-align: center&quot;&gt;

                        &lt;a title=&quot;Holland Lake Patio&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-rsL7ncX/0/M/DSC_4745-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-rsL7ncX/0/Ti/DSC_4745-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Holland Lake Patio&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;The Girls at Double Arrow&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-sdLBDmF/0/M/DSC_4755-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-sdLBDmF/0/Ti/DSC_4755-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;The Girls at Double Arrow&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Hard to believe I caused this guy to move to Montana in 1997&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-b5FCTrd/0/M/DSC_4845-2-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-b5FCTrd/0/Ti/DSC_4845-2-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Hard to believe I caused this guy to move to Montana in 1997&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Holland Lake Sunset&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-cH8CpGZ/0/M/HLL%20View-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-cH8CpGZ/0/Ti/HLL%20View-Ti.jpg&quot;
             style=&quot;border: 1px solid black; margin-left: 1px&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Holland Lake Sunset&quot;&gt;&lt;/a&gt;

                &lt;/p&gt;
                &lt;p style=&quot;text-align: center&quot;&gt;
&lt;a title=&quot;Api, Makao and Keaka. My Hawaiin name is Pualani&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-KHjFqdL/0/M/DSC_5402-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-KHjFqdL/0/S/DSC_5402-S.jpg&quot;
             width=&quot;400&quot; height=&quot;266&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Api, Makao and Keaka. My Hawaiin name is Pualani&quot;&gt;&lt;/a&gt;
                            &lt;/p&gt;
&lt;p&gt;
    After returning from Hawaii, we dropped the kids off at their Mom&apos;s and headed to my cousin&apos;s wedding in Beacon, NY for the weekend. From there, my parents, Trish and I rode the train to Boston, spent a few days in Cape Cod and returned home in time for the beginning of school.
    &lt;/p&gt;
    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7795952628/&quot; href=&quot;http://farm9.staticflickr.com/8294/7795952628_c0be6bc4ca.jpg&quot; title=&quot;Abbie and Jack on the First Day of School 2012 by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8294/7795952628_c0be6bc4ca_n.jpg&quot; width=&quot;209&quot; height=&quot;320&quot; alt=&quot;Abbie and Jack on the First Day of School 2012&quot; class=&quot;picture&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    
    The kids &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_first_day_of_school2&quot;&gt;started the school year in mid-August&lt;/a&gt; and we celebrated &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_birthday_jack4&quot;&gt;Jack&apos;s 8th birthday&lt;/a&gt; a couple weeks later.
    &lt;/p&gt;
&lt;p&gt;My lack of speaking engagements and work-related travel gave us more time to frolic in the Colorado mountains, so we &lt;a href=&quot;http://raibledesigns.com/rd/entry/we_bought_a_boat&quot;&gt;bought a boat&lt;/a&gt; for Labor Day weekend. That Saturday, we took it on its Maiden Voyage on the Colorado River, floating from Radium to Rancho del Rio. I guided and rowed the boat most of the time while our &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7921929062/in/photostream/&quot; href=&quot;http://farm9.staticflickr.com/8460/7921929062_9bcf7d15f0.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;The Maiden Voyage Crew&quot;&gt;7 passengers (and 2 dogs)&lt;/a&gt; enjoyed cold beverages, great scenery and relaxing in the sun. 
    &lt;/p&gt;
    &lt;p style=&quot;text-align: center&quot;&gt;
        &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7921917130/&quot; href=&quot;http://farm9.staticflickr.com/8041/7921917130_7389475dea.jpg&quot; title=&quot;Abbie with our new boat by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8041/7921917130_7389475dea_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Abbie with our new boat&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;p&gt;The next day, Abbie played her first game of golf.&lt;/p&gt;
    &lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a rev=&quot;http://www.flickr.com/photos/mraible/7937704316/&quot; href=&quot;http://farm9.staticflickr.com/8317/7937704316_ee30d6bc55.jpg&quot; title=&quot;Nice form kiddo!&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8317/7937704316_ee30d6bc55_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Nice form kiddo!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a rev=&quot;http://www.flickr.com/photos/mraible/7937705606/&quot; href=&quot;http://farm9.staticflickr.com/8318/7937705606_9d754545de.jpg&quot; title=&quot;Great day of golf at Pole Creek. Got a sweet framed set of Abbie pics at the end too!&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8318/7937705606_9d754545de_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Great day of golf at Pole Creek. Got a sweet framed set of Abbie pics at the end too!&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

    &lt;/p&gt;
    &lt;p&gt;In October, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_deck_project&quot;&gt;finished our deck project&lt;/a&gt; and work continued on our kitchen remodel (started in July by &lt;a href=&quot;http://peppergc.com/&quot;&gt;Ted Pepper&lt;/a&gt;). Our kitchen was far enough along for us to host our 1st Annual Halloween Party.&lt;/p&gt;
    &lt;p&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_birthday_abbie6&quot;&gt;Abbie turned 10&lt;/a&gt; the first week in November. That Friday, we headed to Kauai to &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_40th_anniversary_mom_and&quot;&gt;celebrate my parents&apos; 40th Anniversary&lt;/a&gt;. We spent &lt;a href=&quot;http://raibledesigns.com/rd/entry/november_travels_to_kauai_and#thanksgiving&quot;&gt;Thanksgiving in Wenatchee&lt;/a&gt;, Christmas in Boston/Vermont and &lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/sets/72157632448197212/&quot;&gt;New Years with good friends in Fraser&lt;/a&gt;.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8224084347/&quot; href=&quot;http://farm9.staticflickr.com/8341/8224084347_d146419dfd.jpg&quot; title=&quot;Our Lanai by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8341/8224084347_d146419dfd_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Our Lanai&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8224089763/&quot; href=&quot;http://farm9.staticflickr.com/8065/8224089763_03d7c6a657.jpg&quot; title=&quot;Hanalei Pier by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8065/8224089763_03d7c6a657_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Hanalei Pier&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8225168600/&quot; href=&quot;http://farm9.staticflickr.com/8059/8225168600_df36e98e1e.jpg&quot; title=&quot;Happy Family by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8059/8225168600_df36e98e1e_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Happy Family&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8225152842/&quot; href=&quot;http://farm9.staticflickr.com/8339/8225152842_6c987fae93.jpg&quot; title=&quot;Surf House by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8339/8225152842_6c987fae93_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Surf House&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8225154986/&quot; href=&quot;http://farm9.staticflickr.com/8480/8225154986_2641ec95bd.jpg&quot; title=&quot;St Regis Pool by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8480/8225154986_2641ec95bd_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;St Regis Pool&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;https://www.facebook.com/photo.php?fbid=10200109112597408&amp;amp;set=a.10200104543563185.2198356.1438065285&amp;amp;type=1&quot;&gt;
&lt;img alt=&quot;Mom and Dad in Kauai&quot; src=&quot;//sphotos-a.xx.fbcdn.net/hphotos-prn1/621352_10200109112597408_143056394_o.jpg&quot; style=&quot;width: 520px; height: 346px; border: 1px solid black&quot;&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;h2 id=&quot;2013&quot;&gt;2013&lt;/h2&gt;
&lt;p&gt;My goals for 2013 are to be happy and to &lt;em&gt;slow down&lt;/em&gt;. I don&apos;t plan on traveling any less, or spending less time with my family. I&apos;d just like to walk slower, smell the good ol&apos; Rocky Mountain air more and enjoy life. I believe our raft will help us facilitate this and we&apos;re already planning trips to the &lt;a href=&quot;http://www.tagalong.com/index.php?page=greeno&quot;&gt;Green River&lt;/a&gt; in Utah and &lt;a href=&quot;http://www.montanaliving.com/Outdoors/Schafer_Meadows_a_wilderness_trip_of_a_lifetime_97-097&quot;&gt;Schafer Meadows&lt;/a&gt; in the Bob.&lt;/p&gt;
&lt;p&gt;The big event for the year will be Trish and my wedding this summer. We&apos;ll be doing it in my hometown in Montana and we expect it to be quite the shindig. Afterward, we plan on &lt;a href=&quot;https://twitter.com/mraible/status/264452753596751874&quot;&gt;traveling the world&lt;/a&gt; for our honeymoon.&lt;/p&gt;
&lt;p&gt;Of course, if the bus gets finished, that&apos;s a huge bonus too.&lt;/p&gt;
&lt;p&gt;Professionally, I&apos;m starting an exciting project at Taleo that will involve heavy JavaScript, CSS3 and maybe even Canvas. I have speaking engagements lined up at &lt;a href=&quot;http://www.meetup.com/DenverJavaUsersGroup/events/93932082/&quot;&gt;Denver JUG in February&lt;/a&gt; and &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/&quot;&gt;HTML5 Denver&lt;/a&gt; in April. Also, I just submitted a couple talks to &lt;a href=&quot;http://www.devoxx.com/display/FR13/Home&quot;&gt;Devoxx France&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;For AppFuse, I&apos;d love to get 3-4 releases out this year, but 2 is probably more realistic. The &lt;a href=&quot;http://appfuse.org/display/APF/Roadmap&quot;&gt;AppFuse Roadmap&lt;/a&gt; shows our future releases + features. I&apos;d also like to work a bit more on my Play 2 app; finishing the mobile client and possibly switching from Anorm to Slick or Spring Data.&lt;/p&gt;
&lt;p&gt;While Oracle has been good to me, I look forward to finding and working for a new client in September.&lt;/p&gt;
&lt;p&gt;Last year &lt;a href=&quot;http://raibledesigns.com/rd/entry/2011_a_year_in_review&quot;&gt;at this time&lt;/a&gt;, we watched the Broncos execute the &lt;em&gt;Mile High Miracle&lt;/em&gt; over the Steelers. With tickets to both this week&apos;s game (vs. The Ravens) and next week&apos;s game (vs. The Patriots), I wouldn&apos;t be surprised if January turns out to be a great month. However, with the Powder Days, The Super Bowl, the raft trips, the Bus Shows and The Wedding, there&apos;s a good chance that all the months in 2013 are epic. &lt;img src=&quot;https://raibledesigns.com/images/smileys/smile.gif&quot; class=&quot;smiley&quot; alt=&quot;:)&quot; title=&quot;:)&quot; /&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with</id>
        <title type="html">Improving AppFuse&apos;s PageSpeed with Apache</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with"/>
        <published>2012-12-04T09:25:05-07:00</published>
        <updated>2012-12-05T02:34:08-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="contegix" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apache" scheme="http://roller.apache.org/ns/tags/" />
        <category term="pagespeed" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;One of the most important things when developing webapps is to make them &lt;em&gt;fast&lt;/em&gt;. With AppFuse, we&apos;ve tried to incorporate many of the &lt;a href=&quot;http://stevesouders.com/hpws/rules.php&quot;&gt;14 rules for faster-loading websites&lt;/a&gt;. We had a gzip filter before it was cool (2003) and replaced it with &lt;a href=&quot;http://ehcache.org/documentation/user-guide/web-caching&quot;&gt;the one from EhCache&lt;/a&gt;. However, users experienced issues with both of these, both &lt;a href=&quot;http://issues.appfuse.org/browse/APF-863&quot;&gt;with XFire/CXF&lt;/a&gt; and &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1037&quot;&gt;WebWork/Struts 2 and JSPs&lt;/a&gt;. Because of these issues, we disabled gzipping a few releases ago. &lt;/p&gt;

&lt;p&gt;This article is designed to show you how you can make your AppFuse webapp faster, without modifying any code. The good news is this applies to any webapp that you can deploy behind Apache. &lt;/p&gt;

&lt;p&gt;Last Friday, I sent an email to the good folks at &lt;a href=&quot;http://contegix.com&quot;&gt;Contegix&lt;/a&gt; to see if they could install &lt;a href=&quot;https://developers.google.com/speed/pagespeed/mod&quot;&gt;mod_pagespeed&lt;/a&gt; on the Apache server that sits in front of *.appfuse.org. My goal was to improve the YSlow and PageSpeed scores of the apps hosted on &lt;a href=&quot;http://demo.appfuse.or&quot;&gt;demo.appfuse.org&lt;/a&gt;. I discovered they were getting a dismal score of 24 and figured we could do a lot better. mod_pagespeed speeds up your site and reduces page load time by automatically applying web performance best practices. It seemed like an easy solution.
&lt;/p&gt;

&lt;p&gt;Unfortunately, we were unable to use mod_pagespeed. From the guys at Contegix:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;Attempting to install mod_pagespeed as you requested, we find that it requires Apache httpd 2.2 and libstdc++ 4.1.2, both of which are unsupported in RHEL4. To get mod_pagespeed to work on your present operating system basically means re-rolling the core components, which would make them unsupported. I&apos;m afraid mod_pagespeed is simply not an option on your present configuration.&lt;/p&gt;

&lt;p&gt;Since I still wanted to improve performance, I opted for another route instead: using mod_deflate (for gzipping) and mod_expires (for expires headers). I also turned on KeepAlive as recommended by &lt;a href=&quot;https://developers.google.com/speed/docs/insights/using_chrome&quot;&gt;PageSpeed Insights&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mod_deflate&lt;/strong&gt;&lt;br&gt;mod_deflate was already installed in Apache (version 2.0.52), so all I had to do was configure it. On RHEL4, Apache is installed at /etc/httpd and there&apos;s a &lt;em&gt;conf.d&lt;/em&gt; directory that contains all the configuration files. I created a file at &lt;em&gt;/etc/httpd/conf.d/deflate.conf&lt;/em&gt; and populated it with the following:&lt;/p&gt;

&lt;pre&gt;#
# mod_deflate configuration
#
&amp;lt;IfModule mod_deflate.c&gt;
    SetOutputFilter DEFLATE
    
    AddOutputFilterByType DEFLATE text/plain text/html text/xml text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript
    
    DeflateCompressionLevel 9
    
    BrowserMatch ^Mozilla/4 gzip-only-text/html
    BrowserMatch ^Mozilla/4\.0[678] no-gzip
    BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
    
    DeflateFilterNote Input instream
    DeflateFilterNote Output outstream
    DeflateFilterNote Ratio ratio
    
    LogFormat &apos;&quot;%r&quot; %{outstream}n/%{instream}n (%{ratio}n%%)&apos; deflate
    
&amp;lt;/IfModule&gt;&lt;/pre&gt;

&lt;p&gt;At first, I had separate lines for all the different content types (as recommended by &lt;a href=&quot;http://www.g-loaded.eu/2008/05/10/use-mod_deflate-to-compress-web-content-delivered-by-apache/&quot;&gt;this article&lt;/a&gt;). The Contegix support crew figured out the solution (everything needed to be on one line) in 14 minutes, updated the config and verified it worked using &lt;a href=&quot;http://www.whatsmyip.org/http-compression-test/&quot;&gt;an http compression testing page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mod_expires&lt;/strong&gt;&lt;br&gt;mod_expires was already installed, so I added a config file at &lt;em&gt;/etc/httpd/conf.d/expires.conf&lt;/em&gt;. I used &lt;a href=&quot;http://www.howtoforge.com/make-browsers-cache-static-files-with-mod_expires-on-apache2-debian-squeeze&quot;&gt;this howto&lt;/a&gt; and asked Contegix for help when it didn&apos;t work. Their response took quite a bit longer this time (49 minutes), but they once again figured it out:&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;It appears that FilesMatch does not like to play will JkMount. It does work using content type.&lt;/p&gt;

&lt;p&gt;My final config for &lt;em&gt;expires.conf&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;IfModule mod_expires.c&gt;
    ExpiresActive On
    
    &amp;lt;FilesMatch &quot;\.(jpe?g|png|gif|js|css)$&quot;&gt;
        ExpiresDefault &quot;access plus 1 week&quot;
    &amp;lt;/FilesMatch&gt;
    
    ExpiresByType image/jpeg &quot;access plus 1 week&quot;
    ExpiresByType image/png &quot;access plus 1 week&quot;
    ExpiresByType image/gif &quot;access plus 1 week&quot;
    ExpiresByType text/css &quot;access plus 1 week&quot;
    ExpiresByType application/javascript &quot;access plus 1 week&quot;
    ExpiresByType application/x-javascript &quot;access plus 1 week&quot;
&amp;lt;/IfModule&gt;
&lt;/pre&gt;

&lt;p&gt;I used &quot;1 week&quot; because we&apos;re changing things quite a bit right now and we haven&apos;t integrated &lt;a href=&quot;http://www.operatornew.com/2012/10/adding-web-resource-fingerprinting-to.html&quot;&gt;resource fingerprinting&lt;/a&gt; yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KeepAlive&lt;br&gt;&lt;/strong&gt;The last thing I did to improve performance was to turn on KeepAlive by editing &lt;em&gt;/etc/httpd/conf/httpd.conf &lt;/em&gt;and changing &lt;em&gt;Off &lt;/em&gt;to &lt;em&gt;On&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to &quot;Off&quot; to deactivate.
#
KeepAlive On&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;br&gt;&lt;/strong&gt;As a result of these changes, our PageSpeed score went from 24 to 96 and YSlow went from a 90 to a 98. When I started this experiment, I was only trying to fix demo.appfuse.org. However, it also improved the speed of all the other *.appfuse.org sites, including &lt;a href=&quot;http://appfuse.org&quot;&gt;Confluence&lt;/a&gt;, &lt;a href=&quot;http://builds.appfuse.or&quot;&gt;Bamboo&lt;/a&gt;, &lt;a href=&quot;http://issues.appfuse.or&quot;&gt;JIRA&lt;/a&gt; and &lt;a href=&quot;http://source.appfuse.org&quot;&gt;FishEye&lt;/a&gt;. Thanks for all the help Contegix! There&apos;s a good chance you&apos;ve given me back a few minutes in each day.&lt;/p&gt;
&lt;p style=&quot;text-align: right; font-size: .95em; margin-top: -10px&quot;&gt;&lt;em&gt;Originally posted &lt;a href=&quot;http://appfuse.org/display/APF/2012/12/04/Improving+AppFuse%27s+PageSpeed+with+Apache&quot;&gt;on the AppFuse Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/appfuse_news_github_hibernate_search</id>
        <title type="html">AppFuse News: GitHub, Hibernate Search and The Future</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/appfuse_news_github_hibernate_search"/>
        <published>2012-09-25T10:42:14-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="github" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roadmap" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;It&apos;s been a while since I&apos;ve written anything about &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;, but since the project
  has had quite a bit of activity lately, now seems like a good time.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;br&gt;First of all,
  we moved the source code from java.net to &lt;a href=&quot;https://github.com/appfuse/appfuse&quot;&gt;GitHub&lt;/a&gt; way back
  in June. Thanks to &lt;a href=&quot;https://twitter.com/sdeby&quot;&gt;Serge&lt;/a&gt; for helping with
  this process and making it &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/Migrating-to-GitHub-td4655148.html&quot;&gt;quick and
    painless&lt;/a&gt;. For some reason, shortly
  after moving, we started having quite a few build issues with &lt;a href=&quot;http://builds.appfuse.org&quot;&gt;Bamboo&lt;/a&gt;.
  I was able to diagnose the
  problem as not enough memory on our server. Thankfully, &lt;a href=&quot;http://www.contegix.com/&quot;&gt;Contegix&lt;/a&gt; was able to
  add another 2GB of
  RAM to our box and get everything back up-to-snuff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New Committer: J. Garcia&lt;/strong&gt;&lt;br&gt;
  J. Garcia has been a regular voice on the users and developers mailing list for several
  months. He&apos;s recently started contributing a lot of patches in JIRA and seems genuinely interested in the success of
  AppFuse. That&apos;s why we voted and &lt;a
        href=&quot;http://appfuse.547863.n4.nabble.com/VOTE-Add-J-Garcia-as-an-AppFuse-Committer-td4655395.html&quot;&gt;added him
    as a committer&lt;/a&gt;. To prove this was a smart move, he recently
  &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1298&quot;&gt;replaced Compass with
    Hibernate Search&lt;/a&gt; and &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1297&quot;&gt;upgraded to Hibernate 4&lt;/a&gt;.
  As part of this work, he removed iBATIS support, which brings me to my next point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Future&lt;/strong&gt;&lt;br&gt;

  In mid-August, I sent an email to the
  community, asking them &quot;&lt;a href=&quot;http://appfuse.547863.n4.nabble.com/Anyone-using-iBATIS-td4655369.html&quot;&gt;Anyone using iBATIS?&lt;/a&gt;&quot;&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;I&apos;m thinking of replacing AppFuse&apos;s Data Tier with Spring Data, especially because it has NoSQL and REST support. There&apos;s a good intro on InfoQ today: &lt;br&gt;&lt;br&gt;&lt;a href=&quot;http://www.infoq.com/articles/spring-data-intro&quot;&gt;http://www.infoq.com/articles/spring-data-intro&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Does anyone see an issue with this? The lack of iBATIS support could be an issue, but I doubt it since if we wanted to continue supporting it, we should move to MyBATIS.
&lt;/p&gt;

&lt;p&gt;Everyone agreed this was a good idea and it seemed like a logical time to remove iBATIS support. In
  addition, I posted a roadmap I jotted down in early May. Since we&apos;ve missed all the dates so far, I&apos;ve removed them
  from the listing below. We hope to get 1-2 releases done by the end of this year, with 2.2 in the next 2-3 weeks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;2.2&lt;/strong&gt;&lt;br&gt;Hibernate 4&lt;br&gt;Hibernate Search&lt;br&gt;Bootstrap&lt;br&gt;H5BP&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.3&lt;/strong&gt; &lt;br&gt;AMP for
  all light modules&lt;br&gt;Wicket&lt;br&gt;PrimeFaces&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.4&lt;/strong&gt;&lt;br&gt;JSR 303 (might require removing or developing
  client-side support)&lt;br&gt;Mockito instead of jMock/EasyMock&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.5&lt;/strong&gt;&lt;br&gt;AMP one-to-many&lt;br&gt;Spring Data&lt;br&gt;MyBatis
  (if there&apos;s interest in adding it back in)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.6&lt;/strong&gt;&lt;br&gt;wro4j for concatenation and minimizing JS and
  CSS&lt;br&gt;pjax - &lt;a href=&quot;https://github.com/defunkt/jquery-pjax&quot;&gt;https://github.com/defunkt/jquery-pjax&lt;/a&gt;
&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.7&lt;/strong&gt;&lt;br&gt;Scala example&lt;br&gt;Gradle example&lt;br&gt;Article about examples&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3.0&lt;/strong&gt;&lt;br&gt;GWT
&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;p&gt;&lt;strong&gt;Maven Central Statistics&lt;/strong&gt;&lt;br&gt;To prove there&apos;s still a fair amount of folks using
    AppFuse, here&apos;s some statistic from Sonatype&apos;s &lt;a href=&quot;https://oss.sonatype.org&quot;&gt;OSS Repository Hosting
      Service&lt;/a&gt;.&lt;/p&gt;
  &lt;p style=&quot;text-align: center&quot;&gt;&lt;a rev=&quot;http://www.flickr.com/photos/mraible/8023640406/&quot; href=&quot;http://farm9.staticflickr.com/8452/8023640406_6df4a3c6f8_o.png&quot; rel=&quot;lightbox[appfuse-201209]&quot;
                                        title=&quot;AppFuse Downloads&quot;&gt;&lt;img
        src=&quot;//farm9.staticflickr.com/8452/8023640406_a7e3613b94.jpg&quot; width=&quot;500&quot; height=&quot;188&quot;
        alt=&quot;AppFuse Downloads&quot;&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;From this screenshot, you can see that AppFuse artifacts are downloaded around 7,000 times per month. The
      following graph is even more interesting. Apparently, around 3,000 new projects are created with AppFuse
      archetypes each month.&lt;/p&gt;

    &lt;p style=&quot;text-align: center&quot;&gt;
      &lt;a rev=&quot;http://www.flickr.com/photos/mraible/8023642969/&quot; href=&quot;http://farm9.staticflickr.com/8182/8023642969_00801e1ce9_o.png&quot; rel=&quot;lightbox[appfuse-201209]&quot;
         title=&quot;AppFuse Archetype Downloads&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8182/8023642969_14111307ca.jpg&quot; width=&quot;500&quot; height=&quot;186&quot;
            alt=&quot;AppFuse Archetype Downloads&quot;&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;The AppFuse Name&lt;br&gt;&lt;/strong&gt;Finally, I &lt;a href=&quot;https://twitter.com/#!/search/?q=appfuse&quot;&gt;recently discovered&lt;/a&gt;
      that ShoreTel decided to &lt;a
            href=&quot;http://www.zdnet.com/shoretel-intros-appfuse-integrated-phone-system-aimed-at-salesforce-customers-7000004374/&quot;
            &gt;name a new product AppFuse&lt;/a&gt;. I guess this signifies two things: 1)
      it&apos;s a good name for a product and 2) someone didn&apos;t do their research before naming it. At this point, I&apos;m not
      too concerned, but it is an interesting development.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/core_html5_canvas_book_review</id>
        <title type="html">Core HTML5 Canvas Book Review</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/core_html5_canvas_book_review"/>
        <published>2012-09-14T09:21:56-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="canvas" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bookreview" scheme="http://roller.apache.org/ns/tags/" />
        <category term="davidgeary" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://amzn.to/HVE15S&quot;&gt;&lt;img src=&quot;//static.raibledesigns.com/repository/images/corehtml5canvas.jpg&quot; alt=&quot;Core HTML5 Canvas&quot; width=&quot;145&quot; height=&quot;190&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;/&gt;&lt;/a&gt;
I&apos;ve known &lt;a href=&quot;http://twitter.com/davidgeary&quot;&gt;David Geary&lt;/a&gt; for quite some time, from our &lt;a href=&quot;http://java.sys-con.com/node/46050&quot;&gt;original meeting in the blogosphere&lt;/a&gt; to speaking on the &lt;a href=&quot;http://nofluffjuststuff.com/&quot;&gt;No Fluff Tour&lt;/a&gt;. At first, I had trouble respecting the guy because he was such a JSF Fanboy. However, over the years, he&apos;s switched to Ruby on Rails, GWT and now he&apos;s all about HTML5. Specifically, HTML5&apos;s &amp;lt;canvas&amp;gt; element. When David asked me if I&apos;d like a copy of his &lt;a href=&quot;http://corehtml5canvas.com/&quot;&gt;lastest book&lt;/a&gt;, I jumped at the opportunity.&lt;/p&gt;
&lt;p&gt;I received it in the mail shortly before heading to Hawaii this summer. I started &lt;a href=&quot;http://twitter.com/mraible/status/224637867203366912&quot;&gt;reading it by the pool&lt;/a&gt; the next day.&lt;/p&gt;
&lt;p&gt;I was immediately impressed that the book was printed in color. The first copy I ever saw of my &lt;a href=&quot;http://www.amazon.com/The-Spring-Primer-Matt-Raible/dp/0974884375&quot;&gt;Spring Primer&lt;/a&gt; was in color and it really popped. Geary&apos;s book does the same and I&apos;m glad the publisher decided the extra cost of printing was worth it. In the preface (and in a &lt;a href=&quot;http://corehtml5canvas.wordpress.com/2012/08/08/the-making-of-core-html5-canvas/&quot;&gt;recent blog post&lt;/a&gt;), David explains how he wrote the book code-first in the Zen tradition, so you can read it without reading. I saw him write somewhere that he spent 2 years, 60 hours per week writing it. It really shows - the sheer amount of code and knowledge in this book is amazing.&lt;/p&gt;
&lt;p&gt;Looking back at the Table of Contents, I remember getting overwhelmed early on. Not overwhelmed in that I didn&apos;t understand how things were working, but more like &quot;there&apos;s too much in here to try and remember it all&quot;. I haven&apos;t used algebra since high school, and right there on page 53 it says:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
To do anything interesting with Canvas, you need a good basic understanding of basic mathematics, especially working with algebraic equations, trigonometry, and vectors.
&lt;/p&gt;
&lt;p&gt;Reading the book poolside wasn&apos;t a huge motivator to refresh my algebraic knowledge, but I did enjoy David&apos;s brief 10-page refresher. In Chapter 2 on Drawing, the book dives into the low-level API that canvas offers for drawing rectangles, circles and polygons. It also goes on to show you how to do gradients, patterns and shadows as well as all there is to know about paths, stroking and filling. This is when it hits you that &amp;lt;canvas&amp;gt; is more about JavaScript than HTML. In fact, it&apos;s usually only a couple lines of HTML and a whole lotta JavaScript.&lt;/p&gt;
&lt;p&gt;In Chapter 3, you learn about text and how to work with fonts and paragraphs. David even spends 10 pages showing you how to implement a Paragraph, complete with positioning the cursor, adding new lines and working with backspace. It really makes you appreciate what HTML offers you with the good ol&apos; &amp;lt;p&amp;gt; and &amp;lt;input type=&quot;text&quot;&amp;gt;.
&lt;/p&gt;
&lt;p&gt;Chapter 4 is where you learn about working with images and video, using offscreen canvases and working with a canvas within a canvas. I believe I was back in Colorado when I started reading this chapter. It&apos;s also where I succumbed to the fact that this was an excellent reference book and not something I was going to read, learn from and start using the next week. It feels like a book I&apos;ll refer back to many times when using &amp;lt;canvas&amp;gt; on a project. The amount of knowledge in the book seems akin to Rod Johnson&apos;s &lt;a href=&quot;http://www.amazon.com/Expert-One-One-Development-without/dp/0764558315&quot;&gt;J2EE Development without EJB&lt;/a&gt;. I remember getting the general gist of Rod&apos;s ideas while reading the book, but not knowing how to put them into use. Then the Spring Framework was introduced and everything became clear. As I read Geary&apos;s book, I thought the same thing - &lt;em&gt;someone really needs to develop a simpler API for Canvas&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;As I read on, through chapters on Animations, Sprites, Physics, Collision Detection and Game Development, it hit me - &lt;em&gt;maybe that&apos;s what David is doing!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Throughout the book, David builds a framework for working with Shapes, animating them and finally, for putting them to work in a gaming environment. The &lt;em&gt;Ungame&lt;/em&gt; is nice in that it shows you how to use a game engine for building your own games. Then he goes on to show you a Pinball game that looks overly complex, but breaks it down into terms you can understand.&lt;/p&gt;
&lt;p&gt;The last chapter is on Mobile development. It explains in detail about the viewport metatag, media queries for CSS and touch events. The section on iOS5 is good, but does make the book seem slightly outdated with iOS6 coming out next week. I&apos;m sure all of the content is still relevant, but it almost seems like labeling it iOS5+ would&apos;ve been better. In the final pages of the book, you learn how using a canvas that requires typing on a touch device might suck. David shows you how to implement a Virtual Keyboard to handle these situations, since the native keyboard won&apos;t pop up unless you&apos;re typing into HTML controls like &amp;lt;input&amp;gt; and &amp;lt;textarea&amp;gt;.
&lt;/p&gt;
&lt;p&gt;I read this book to learn more about Canvas and what it was capable of. I learned all it can do and much, much more. I learned how animations and timing can be different between browsers and how you might need to create a polyfill for &lt;code&gt;requestAnimationFrame&lt;/code&gt; for it to work consistently. 
&lt;/p&gt;
&lt;p&gt;
More than anything, I recognized that this is one of the few technical books I&apos;ve read in a long time that&apos;s become an instant valuable resource. With other books, the information is often available online. Not so with Geary&apos;s book. To me, it seems like the best resource available for learning and using HTML5 Canvas.&lt;/p&gt;
&lt;p&gt;Well done, David, well done.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with</id>
        <title type="html">Refreshing Taleo&apos;s UI with HTML5, Twitter Bootstrap and CSS3</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with"/>
        <published>2012-08-20T12:27:21-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css3" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="twitterbootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Back in December, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/what_have_i_been_working&quot;&gt;what I&apos;ve been working on at Taleo&lt;/a&gt;. Shortly after finishing up the Profile Picture, Talent Card and Org Chart features for TBE, I spent two weeks doing page speed optimization. By following &lt;a href=&quot;http://code.google.com/speed/page-speed/docs/rules_intro.html&quot;&gt;Web Performance Best Practices&lt;/a&gt;, I was able to make the TBE application twice as fast and improve its score into the low 90s. 
&lt;/p&gt;
&lt;p&gt;
Next, I started working on a new project - refreshing the UI. &lt;a href=&quot;http://www.linkedin.com/pub/nick-morgan/1/449/637&quot;&gt;Nick&lt;/a&gt;, the Lead UX Designer at Taleo (at the time), had developed a number of mockups and presented it to the developers and product folks in early November. I listened to a WebEx of that meeting and learned that everyone thought it&apos;d take 6-9 months to complete the work. They figured they could release the new design in Q3 2012.  
&lt;/p&gt;
&lt;p&gt;Since I like to provide high-value for my clients, I offered to help with the redesign and do a spike to help estimate. They agreed it&apos;d be a good use of my time and I started working on it the week before Christmas. Since I&apos;d used &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Twitter Bootstrap&lt;/a&gt; for my &lt;a href=&quot;http://raibledesigns.com/rd/entry/migrating_to_play_2_and&quot;&gt;&lt;em&gt;Play More!&lt;/em&gt; app&lt;/a&gt;, I recommended we use it as a foundation of the redesign. They agreed and I went to work. By the end of the week, I&apos;d made good progress and told them I thought the redesign was possible in 2-3 months (including QA and cross-browser compatibility).&lt;/p&gt;
&lt;p&gt;When I came back to work in January, we decided to split the redesign into two phases. Rather than moving elements around and introducing new features, we decided to do that in the 2nd phase. The 1st phase would entail simply re-skinning the existing UI, with minimal HTML changes. I spent a week refining my spike and integrating it into a branch. The next week, I switched images from individual images to &lt;a href=&quot;http://www.alistapart.com/articles/sprites&quot;&gt;CSS sprites&lt;/a&gt;. Next, I implemented a new theming system with different colors/icons and got everything looking good in Chrome, Safari and IE8/9. 
&lt;/p&gt;
&lt;p&gt;The result is something I&apos;m quite proud of. IE8 doesn&apos;t have the rounded corners (via border-radius), but it still looks good. Forms look much better thanks to Bootstrap&apos;s styling and even jQuery UI&apos;s widgets look good thanks to &lt;a href=&quot;http://addyosmani.github.com/jquery-ui-bootstrap/&quot;&gt;jQuery UI Bootstrap&lt;/a&gt;. I did have to override quite a few Bootstrap styles in the process, but the result is something that doesn&apos;t look too &lt;em&gt;bootstrappy&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;One technique I found to be extremely useful during this process was to pair with Nick (the designer) as mentioned in &lt;a href=&quot;http://www.alistapart.com/articles/building-twitter-bootstrap/&quot;&gt;Building Twitter Bootstrap&lt;/a&gt;. At one point, when we were trying to refine slight nuances and spacing in the UI, I paired with the Product Manager and found this to be a real time-saving effort as well.&lt;/p&gt;

&lt;p&gt;Taleo&apos;s UI Refresh project has been a great experience for me in sharpening my CSS skills. I used quite a bit of &lt;a href=&quot;http://css-tricks.com/child-and-sibling-selectors/&quot;&gt;child and sibling selectors&lt;/a&gt;, which work great in all the browser&apos;s we&apos;re supporting. Also, by using CSS sprites and colors (vs. images), I was able to get the manual theme-creation process down to around 15 minutes. After getting the manual process greatly reduced, I wrote a Theme Generator (based on Ant, LESS and &lt;a href=&quot;http://code.google.com/p/wro4j/&quot;&gt;wro4j&lt;/a&gt;) and got it down to mere minutes. I found &lt;a href=&quot;http://www.spritecow.com/&quot;&gt;Sprite Cow&lt;/a&gt; to be an invaluable resources for working with CSS sprites.
&lt;/p&gt;
&lt;p&gt;Below are some before and after shots of what we&apos;ve been able to accomplish in the first quarter of this year.
&lt;/p&gt;  
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7159/6777477811_66d6401bd9_b.jpg&quot; rel=&quot;lightbox[taleo-uirefresh]&quot; title=&quot;Old UI - My View&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7159/6777477811_66d6401bd9_m.jpg&quot; width=&quot;240&quot; height=&quot;148&quot; alt=&quot;Old UI - My View&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7007/6777477639_4ca42665a7_b.jpg&quot; rel=&quot;lightbox[taleo-uirefresh]&quot; title=&quot;Old UI - New Employee&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7007/6777477639_4ca42665a7_m.jpg&quot; style=&quot;margin-left: 10px&quot; width=&quot;240&quot; height=&quot;148&quot; alt=&quot;Old UI - New Employee&quot;&gt;&lt;/a&gt;
&lt;p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  
  &lt;a href=&quot;http://farm8.staticflickr.com/7022/6777477527_b1abb3b204_b.jpg&quot; rel=&quot;lightbox[taleo-uirefresh]&quot; title=&quot;New UI - My View&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7022/6777477527_b1abb3b204_m.jpg&quot; width=&quot;240&quot; height=&quot;148&quot; alt=&quot;New UI - My View&quot;&gt;&lt;/a&gt;
  
  &lt;a href=&quot;http://farm8.staticflickr.com/7162/6777477443_37241a4e85_b.jpg&quot; rel=&quot;lightbox[taleo-uirefresh]&quot; title=&quot;New UI - New Employee&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7162/6777477443_37241a4e85_m.jpg&quot; style=&quot;margin-left: 10px&quot; width=&quot;240&quot; height=&quot;148&quot; alt=&quot;New UI - New Employee&quot;&gt;&lt;/a&gt;  
  &lt;/p&gt;
  &lt;p&gt;
I originally wrote this post at the end of January. We ran into some stumbling blocks shorty after its original composition: Nick (the designer) moved onto &lt;a href=&quot;http://www.salesforce.com/&quot;&gt;greener pastures&lt;/a&gt; and &lt;a href=&quot;http://www.businessweek.com/news/2012-02-10/oracle-buys-taleo-for-1-9-billion-adds-human-resource-tool.html&quot;&gt;Oracle bought Taleo&lt;/a&gt;. What I didn&apos;t expect when I wrote this was to spend the next two months fixing slight bugs that occurred with spacing, alignment and dependent applications I didn&apos;t know about at the time. And then there was IE7. We didn&apos;t realize we needed to support it until mid-March. Then it took us around a month to make it all work &lt;em&gt;good enough&lt;/em&gt;. 
&lt;/p&gt;
&lt;p&gt;The good news is the UI Refresh was released a few months ago and seems to be humming along just fine. Sure, there were slight nuances and customizations we had conflicts with (clashing CSS classes), but overall it seems to have gone well. I can&apos;t thank the Bootstrap developers enough for motivating us to move to HTML5 and CSS3. Also, cheers to the excellent co-workers that helped make this happen: &lt;a href=&quot;http://www.linkedin.com/in/mnewton&quot;&gt;Murray Newton&lt;/a&gt; (Product Manager) and &lt;a href=&quot;http://www.linkedin.com/in/vladimirbazarsky&quot;&gt;Vladimir Bazarsky&lt;/a&gt;. I couldn&apos;t have done it without you guys.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/new_look_and_feel_designed</id>
        <title type="html">New Look and Feel, Designed by Gillen&apos;s Army</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/new_look_and_feel_designed"/>
        <published>2012-08-14T22:58:11-06:00</published>
        <updated>2012-08-15T05:28:37-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gillensarmy" scheme="http://roller.apache.org/ns/tags/" />
        <category term="design" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">As part of my &lt;a href=&quot;http://raibledesigns.com/rd/entry/10_years_ago&quot;&gt;10-year blogiversary&lt;/a&gt;, I was hoping to refresh this site with a new look and feel. A few months ago, I contacted my friend &lt;a href=&quot;http://www.linkedin.com/pub/mark-waggoner/6/693/9a9&quot;&gt;Mark Waggoner&lt;/a&gt; to see about getting his design help. We promptly worked out a logo/business card/website deal and &lt;a href=&quot;http://gillensarmy.com/&quot;&gt;Gillen&apos;s Army&lt;/a&gt; went to work. &lt;/p&gt;
&lt;p&gt;I picked a logo from numerous choices in late June, finalized a business card for printing in July and received the HTML and CSS for the site on August 2nd. I started converting it to a Roller theme last week and did a whole bunch of other modifications in the process.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Upgraded to &lt;a href=&quot;http://rollerweblogger.org/project/entry/roller_501_security_fix&quot;&gt;Roller 5.0.1&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Upgraded &lt;a href=&quot;http://code.google.com/p/wro4j/&quot;&gt;wro4j&lt;/a&gt; to the latest version (1.4.8.1) to workaround using a &amp;rarr; (\2192) in CSS.&lt;/li&gt; 
    &lt;li&gt;Changed to use jQuery and &lt;a href=&quot;http://lokeshdhakar.com/projects/lightbox2/&quot;&gt;Lightbox2&lt;/a&gt; for pictures.&lt;/li&gt;
    &lt;li&gt;Upgraded to the latest version (3.0.83) of &lt;a href=&quot;http://alexgorbatchev.com/SyntaxHighlighter/&quot;&gt;SyntaxHighlighter&lt;/a&gt;. You might notice &lt;a href=&quot;https://github.com/alexgorbatchev/SyntaxHighlighter/issues/44&quot;&gt;there is no longer a toolbar&lt;/a&gt; in this version. However, you can still double-click on code and easily copy/paste it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to these upgrades, I made a few enhancements. I converted to HTML5 (by switching the doctype), added Modernizr and a feature that detects if the sun is up in your location. If you allow your browser to send me your lat and long, I&apos;ll give you a dark theme when the sun is down and a light theme when it&apos;s daylight. I used Preston&apos;s Hunt&apos;s &lt;a href=&quot;http://prestonhunt.com/story/124&quot;&gt;JavaScript Class for Sunrise and Sunset Calculations&lt;/a&gt; to determine isDaylight. You can also change the theme to light or dark using the small rectangles above the search box on the right. This sets a cookie and overrides the HTML5 Geo check. You can see the implementation of this logic in &lt;a href=&quot;http://raibledesigns.com/themes/darklight/js/site.js&quot;&gt;site.js&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;The stylesheet switching doesn&apos;t happen as fast as I&apos;d hoped (there&apos;s a flash even if using cookies), so I&apos;ll likely be converting some theme-setting logic to the server-side. The HTML5 version of the &lt;a href=&quot;https://developers.facebook.com/docs/reference/plugins/like/&quot;&gt;FaceBook Like Button&lt;/a&gt; requires you to specify the &quot;data-colorscheme&quot; in markup so this further supports moving to the server.&lt;/p&gt;
&lt;p&gt;I have other minor adjustments I&apos;d like to make, but more importantly - I wanted to get it out to you all. Tell me what you like and don&apos;t like. Among other things, the form inputs for comments and &lt;a href=&quot;http://raibledesigns.com/contact.jsp&quot;&gt;contact&lt;/a&gt; forms have backgrounds that might not be great for those color-impaired. Also, you can see how the iframe on the contact page has a white background instead of one based on the theme.&lt;/p&gt;

&lt;p&gt;Here&apos;s some stats comparing my old &lt;em&gt;andreas08&lt;/em&gt; theme to the new &lt;em&gt;darklight&lt;/em&gt;:

&lt;table class=&quot;comparison&quot;&gt;
    &lt;thead&gt;
        &lt;tr&gt;
        &lt;th&gt;Metric&lt;/th&gt;
        &lt;th&gt;andreas08&lt;/th&gt;
        &lt;th&gt;darklight&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;Size and Speed&lt;/td&gt;                
            &lt;td&gt;167 requests, 3.6MB, 9.89s&lt;/td&gt;
            &lt;td&gt;148 requests, 3.2MB, 7.34s&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;YSlow&lt;/td&gt;    
            &lt;td&gt;76 &lt;!-- Grade F on Make fewer HTTP requests
            This page has 23 external Javascript scripts. Try combining them into one.
            This page has 4 external stylesheets. Try combining them into one.
            This page has 8 external background images. Try combining them with CSS sprites.
            --&gt;&lt;/td&gt;
            &lt;td&gt;87&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;PageSpeed&lt;/td&gt;    
            &lt;td&gt;91 &lt;!-- Optimize images, Prefer asynchronous resources --&gt;&lt;/td&gt;
            &lt;td&gt;96 &lt;!-- Optimize images --&gt;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Sweet! It looks like this site is faster than ever. Cheers to Mark and &lt;a href=&quot;http://gillensarmy.com/&quot;&gt;Gillen&apos;s Army&lt;/a&gt; for the new design. I dig it!
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/migrating_to_play_2_and</id>
        <title type="html">Migrating to Play 2 and My &#220;berConf Presentation</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/migrating_to_play_2_and"/>
        <published>2012-06-21T16:09:22-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="github" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="anorm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="heroku" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="uberconf" scheme="http://roller.apache.org/ns/tags/" />
        <category term="iphone" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jqtouch" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">In my &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgrading_to_play_2_anorm&quot;&gt;last post&lt;/a&gt; about migrating to Play 2, I said I&apos;d write another post on the rest of my experience. While I&apos;m not completely finished with migrating to Play 2, I feel like I&apos;ve done enough to talk about the issues I encountered.
&lt;/p&gt;
&lt;p id=&quot;validation&quot;&gt;&lt;strong&gt;Validation and Displaying Errors&lt;/strong&gt;&lt;br/&gt;
With Play 1, I can&apos;t help but think validation was a bit more intuitive. For example, here&apos;s how I populated an object from request parameters, converted a value and validated its data was fit to put in a database.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
var workout = params.get(&quot;workout&quot;, classOf[Workout])

// change duration to time
var duration = params.get(&quot;workout.duration&quot;)
workout.duration = convertWatchToTime(duration)

Validation.valid(&quot;workout&quot;, workout)

if (Validation.hasErrors) {
  renderArgs.put(&quot;template&quot;, &quot;Profile/edit&quot;)
  edit(id);
  ...
} else { // put into db
&lt;/pre&gt;
&lt;p&gt;With Play Scala 2, you have to define a &lt;a href=&quot;http://www.playframework.org/documentation/2.0/ScalaForms&quot;&gt;Form structure&lt;/a&gt; and bind it from the request. Based on what I was able to conjure up, I ended up writing the following code to accomplish the same thing:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
val workoutForm = Form(
  mapping(
    &quot;id&quot; -&gt; ignored(NotAssigned: anorm.Pk[Long]),
    &quot;title&quot; -&gt; text,
    &quot;description&quot; -&gt; text,
    &quot;duration&quot; -&gt; nonEmptyText,
    &quot;distance&quot; -&gt; nonEmptyText,
    &quot;postedAt&quot; -&gt; optional(date),
    &quot;athleteId&quot; -&gt; optional(longNumber)
  )((id, title, description, duration, distance, postedAt, athleteId) =&gt;
    Workout(id, title, description, convertWatchToTime(duration), distance.toDouble, null, 0))
    ((w: Workout) =&gt;
      Some((w.id, w.title, w.description, w.duration.toString, w.distance.toString, null, Some(0))))
)
...
workoutForm.bindFromRequest.fold(
  form =&gt; {
    Ok(Scalate(&quot;/Profile/edit.jade&quot;).render(request, &apos;errors -&gt; form.errors))
  },
  workout =&gt; { // put into db
&lt;/pre&gt;
&lt;p&gt;First of all, the Play 2 version is quite a bit more verbose, but most of that comes from the re-defining of my model object as a form. It seems strange that the Java API allows you to &lt;a href=&quot;http://www.playframework.org/documentation/2.0/JavaForms&quot;&gt;do it in one line&lt;/a&gt; whereas the Scala version does not. Also, I was unable to figure out how to get the data from my &quot;form&quot; back into the request so I could refill input fields. I&apos;ll admit, I didn&apos;t spend a lot of time trying to figure it out, but it did fail the 10 minute test. Note to self: &lt;a href=&quot;http://wufoo.com/html5/attributes/09-required.html&quot;&gt;use HTML5&apos;s &lt;em&gt;required&lt;/em&gt; attribute&lt;/a&gt; to reduce the need for server-side validation on modern browsers.
&lt;/p&gt;
&lt;p&gt;On a more positive note, I did like the way I was able to use routes in my Jade templates. It was as simple as importing the routes class and using it as you would in Play&apos;s Scala Templates:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-import controllers._

form(method=&quot;post&quot; class=&quot;form-stacked&quot; id=&quot;workoutForm&quot;
  action={routes.Profile.postWorkout(workout.map(_.id.get))})
  input(type=&quot;hidden&quot; name=&quot;id&quot; value=&quot;#{workout.map(_.id)}&quot;)
&lt;/pre&gt;
&lt;p id=&quot;securesocial&quot;&gt;&lt;strong&gt;Secure Social&lt;/strong&gt;&lt;br/&gt;After getting most of my UI working, I started looking at the &lt;a href=&quot;https://github.com/jaliss/securesocial&quot;&gt;Secure Social Module for Play 2&lt;/a&gt;. Below are the steps I had to go through to install it:
&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Cloned &lt;a href=&quot;https://github.com/jaliss/securesocial&quot;&gt;GitHub project&lt;/a&gt; to my hard drive.&lt;/li&gt;
    &lt;li&gt;Copied &lt;code&gt;module-code/*&lt;/code&gt; into my projects&apos; &lt;code&gt;modules/securesocial&lt;/code&gt; directory.&lt;/li&gt;
    &lt;li&gt;Modified &lt;code&gt;project/Build.scala&lt;/code&gt; to add secureSocial and dependsOn to my project.
&lt;pre class=&quot;brush: scala&quot;&gt;
val secureSocial = PlayProject(
  appName + &quot;-securesocial&quot;, appVersion, mainLang = SCALA, path = file(&quot;modules/securesocial&quot;)
)

val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
  // Add your own project settings here
).dependsOn(secureSocial).aggregate(secureSocial)
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Added a &lt;a href=&quot;https://github.com/jaliss/securesocial/blob/master/samples/scala/demo/conf/securesocial.conf&quot;&gt;conf/securesocial.conf&lt;/a&gt; and included it in my application.conf with the following line:
&lt;pre style=&quot;margin-top: 5px&quot;&gt;
include &quot;securesocial.conf&quot;
&lt;/pre&gt;&lt;/li&gt;
    &lt;li&gt;Added a conf/play.plugins with the following to get Twitter to load as a provider:
&lt;pre style=&quot;margin-top: 5px&quot;&gt;
10000:securesocial.core.providers.TwitterProvider
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Created an &lt;a href=&quot;https://github.com/jaliss/securesocial/blob/master/samples/scala/demo/app/service/InMemoryUserService.scala&quot;&gt;InMemoryUserService.scala&lt;/a&gt; and referenced it in my play.plugins file:
&lt;pre style=&quot;margin-top: 5px&quot;&gt;
9999:services.InMemoryUserService
&lt;/pre&gt;&lt;/li&gt;
    &lt;li&gt;Added &lt;a href=&quot;https://github.com/jaliss/securesocial/blob/master/samples/scala/demo/conf/routes&quot;&gt;Secure Social&apos;s routes&lt;/a&gt; to my conf/routes file.
        &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once I&apos;d finished all these steps, I fired up my app and was pleasantly surprised to find I could navigate to /login and successfully authenticate via Twitter. Installing Secure Social in a Play 2 app is quite a bit harder than adding it as a dependency in Play 1, but I was thankful that I got it to work in under 10 minutes.
&lt;/p&gt;
&lt;p id=&quot;heroku&quot;&gt;
&lt;strong&gt;Heroku&lt;/strong&gt;&lt;br/&gt;
The next thing I did was attempt to deploy my app to Heroku. I knew there might be some issues with Scalate after reading &lt;a href=&quot;http://janhelwich.wordpress.com/2012/04/22/getting-play-2-with-scalate-to-run-on-heroku/&quot;&gt;Jan Helwich&apos;s blog post&lt;/a&gt; about Scalate on Heroku. The first things I encountered were 1) a successful startup and 2) an error in my browser.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;img src=&quot;//farm6.staticflickr.com/5346/7416344424_b25593ccdc_n.jpg&quot; width=&quot;320&quot; height=&quot;253&quot; alt=&quot;Action not found&quot;&gt;
&lt;/p&gt;
&lt;p&gt;I was able to reproduce this issue locally by running &quot;play clean stage&quot; and starting the app with &quot;target/start&quot;. After 30 minutes of banging my head against the wall, I guessed it might be caused by Secure Social. Removing Secure Social solved the problem and I was back in business. However, this time when I deployed, I received the error that Jan had mentioned might happen.
&lt;/p&gt;
&lt;pre&gt;
2012-06-21T07:07:12+00:00 app[web.1]: [error] o.f.s.l.DefaultLayoutStrategy - Unhandled: org.fusesource.scalate.TemplateException: target/../tmp/src/app/target/../app/views/layouts/default.jade.scala (No such file or directory)
2012-06-21T07:07:12+00:00 app[web.1]: [error] application - 
2012-06-21T07:07:12+00:00 app[web.1]: 
2012-06-21T07:07:12+00:00 app[web.1]: ! @6amfgf02h - Internal server error, for request [GET /] -&gt;
2012-06-21T07:07:12+00:00 app[web.1]: 
2012-06-21T07:07:12+00:00 app[web.1]: play.core.ActionInvoker$$anonfun$receive$1$$anon$1: Execution exception [[TemplateException: target/../tmp/src/app/target/../app/views/layouts/default.jade.scala (No such file or directory)]]
2012-06-21T07:07:12+00:00 app[web.1]:   at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:134) [play_2.9.1-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:115) [play_2.9.1-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at akka.actor.Actor$class.apply(Actor.scala:311) [akka-actor-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at play.core.ActionInvoker.apply(Invoker.scala:113) [play_2.9.1-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at akka.actor.ActorCell.invoke(ActorCell.scala:619) [akka-actor-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:196) [akka-actor-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]: Caused by: org.fusesource.scalate.TemplateException: target/../tmp/src/app/target/../app/views/layouts/default.jade.scala (No such file or directory)
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.compileAndLoad(TemplateEngine.scala:834) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.compileAndLoadEntry(TemplateEngine.scala:691) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.liftedTree1$1(TemplateEngine.scala:411) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.load(TemplateEngine.scala:405) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.load(TemplateEngine.scala:475) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.layout.DefaultLayoutStrategy.org$fusesource$scalate$layout$DefaultLayoutStrategy$$tryLayout(DefaultLayoutStrategy.scala:77) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]: Caused by: java.io.FileNotFoundException: target/../tmp/src/app/target/../app/views/layouts/default.jade.scala (No such file or directory)
2012-06-21T07:07:12+00:00 app[web.1]:   at java.io.FileOutputStream.open(Native Method) ~[na:1.6.0_20]
2012-06-21T07:07:12+00:00 app[web.1]:   at java.io.FileOutputStream.&lt;init&gt;(FileOutputStream.java:209) ~[na:1.6.0_20]
2012-06-21T07:07:12+00:00 app[web.1]:   at java.io.FileOutputStream.&lt;init&gt;(FileOutputStream.java:160) ~[na:1.6.0_20]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.util.IOUtil$.writeBinaryFile(IOUtil.scala:111) ~[scalate-util-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.compileAndLoad(TemplateEngine.scala:747) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.compileAndLoadEntry(TemplateEngine.scala:691) ~[scalate-core-1.5.3.jar:1.5.3]
&lt;/pre&gt;
&lt;p&gt;
I tried his suggestion (removing the first slash on my Scalate paths) but it didn&apos;t work. I tried adding in Scalate pre-compilation, but that didn&apos;t solve the problem either. The good news is I did solve it this afternoon by &lt;a href=&quot;https://github.com/mraible/play-more/commit/3e981ab4f31c6333ee0276cd2d84b44cba47b7c8&quot;&gt;changing my Scalate object to use a canonical path&lt;/a&gt; instead of an absolute one.
&lt;/p&gt;
&lt;p id=&quot;iphone-app&quot;&gt;&lt;strong&gt;iPhone App&lt;/strong&gt;&lt;br/&gt;
In addition to the changes mentioned here, I re-wrote the iPhone app for Play More. I upgraded it to &lt;a href=&quot;http://phonegap.com/2012/06/13/phonegap-1-8-1-released/&quot;&gt;PhoneGap 1.8.1&lt;/a&gt;, used &lt;a href=&quot;http://www.jqtouch.com/&quot;&gt;jQTouch&lt;/a&gt;, developed with &lt;a href=&quot;http://www.jetbrains.com/objc/&quot;&gt;AppCode&lt;/a&gt; (instead of Xcode) and had a pretty good experience. The only issue I ran into was with the &lt;a href=&quot;https://github.com/DataZombies/jQTouch&quot;&gt;jqt.bars extension from
DataZombies&lt;/a&gt;. I briefly tried to integrate it and then decided not to. However, I left all its JS and CSS in my page and this caused scrolling to not work and made the app sluggish. Removing the files solved the problem. The other big improvement I made was moving all the static assets (JS, CSS, images) into the mobile app instead of referencing them from http://play-more.com. This reduced the startup time from 30-40 seconds to 3-4 seconds!&lt;/p&gt;
&lt;p&gt;
&lt;p id=&quot;presentation&quot;&gt;&lt;strong&gt;Presentation and Source Code&lt;/strong&gt;&lt;br/&gt;
I presented all of these findings and told my story at &lt;a href=&quot;http://uberconf.com&quot;&gt;&#220;berConf&lt;/a&gt; this morning. In addition, I announced that the code is now &lt;a href=&quot;http://github.com/mraible/play-more&quot;&gt;open source and available on GitHub&lt;/a&gt;. You can view my presentation below or &lt;a href=&quot;http://www.slideshare.net/mraible/html5-with-play-scala-coffeescript-and-jade-uberconf-2012&quot;&gt;on Slideshare&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/13410673?rel=0&quot; width=&quot;510&quot; height=&quot;426&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC;border-width:1px 1px 0&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/p&gt;
&lt;p id=&quot;conclusion&quot;&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
Would I do it again? Learning Scala was my primary motivator for digging into Play. When Play 2 was announced, I thought migrating my app to the new version would be easy. Unfortunately, the Play Developers decided to break backwards-compatibility and wrote a whole new framework that still seems to be in its infancy. I think you can see from my last couple of posts that migrating from Play 1.x to 2.x was not an easy task. It&apos;s been nice to learn more about Play and Scala in the process, but living on the bleeding edge was also quite frustrating at times. Play Scala 1.x seemed to be quite a bit more productive than Play 2, especially because of the Magic[T] in Anorm, but also because it required less code in Controllers. 
&lt;/p&gt;
&lt;p&gt;I found Anorm and Scalate to be &lt;em&gt;huge&lt;/em&gt; time sinks and don&apos;t know if I&apos;d recommend using either one in a Play 2 project. I&apos;m sure Scalate will be easier to use as its Play 2 integration gets more refined, but I don&apos;t know if there&apos;s any hope for a JDBC abstraction that doesn&apos;t produce error messages when things go south.
&lt;/p&gt;
&lt;p&gt;On the upside, my experience with HTML5 and CoffeeScript was wonderful. They did what I asked them to do and didn&apos;t cause much pain. When a browser-based webapp couldn&apos;t handle geo running in the background, PhoneGap came to the rescue. 
&lt;/p&gt;
&lt;p&gt;
I plan on continuing to develop &lt;a href=&quot;http://play-more.com&quot;&gt;&lt;em&gt;Play More!&lt;/em&gt;&lt;/a&gt; If you&apos;d like to help, checkout the &lt;a href=&quot;https://github.com/mraible/play-more/issues?state=open&quot;&gt;open issues&lt;/a&gt; and viva la open source!  </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/cruising_around_the_western_caribbean</id>
        <title type="html">Cruising around the Western Caribbean</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/cruising_around_the_western_caribbean"/>
        <published>2012-04-15T18:05:57-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/General" label="General" />
        <category term="disney" scheme="http://roller.apache.org/ns/tags/" />
        <category term="trish" scheme="http://roller.apache.org/ns/tags/" />
        <category term="legoland" scheme="http://roller.apache.org/ns/tags/" />
        <category term="travel" scheme="http://roller.apache.org/ns/tags/" />
        <category term="dad" scheme="http://roller.apache.org/ns/tags/" />
        <category term="mom" scheme="http://roller.apache.org/ns/tags/" />
        <category term="cruise" scheme="http://roller.apache.org/ns/tags/" />
        <category term="vacation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jack" scheme="http://roller.apache.org/ns/tags/" />
        <category term="abbie" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;style type=&quot;text/css&quot;&gt;
img.picture { margin-top: 5px }
&lt;/style&gt;
I&apos;ve heard great things about &lt;a href=&quot;http://disneycruise.disney.go.com/&quot;&gt;Disney Cruise Lines&lt;/a&gt; from friends, especially as a fun adventure for kids. That&apos;s why I was super pumped to talk my family into going on a Western Caribbean Cruise for Spring Break. As you might&apos;ve read in my &lt;a href=&quot;http://raibledesigns.com/rd/entry/spring_break&quot;&gt;last post&lt;/a&gt;, we spent a week in Crested Butte before embarking on our cruise. 
&lt;/p&gt;
&lt;p&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7058699527/&quot; href=&quot;http://farm8.staticflickr.com/7119/7058699527_fd6376755f.jpg&quot; title=&quot;ShotSki Research at The Secret Stash&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7119/7058699527_fd6376755f_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;ShotSki Research at The Secret Stash&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
While in Crested Butte, I enjoyed the &lt;a href=&quot;http://www.mindviewinc.com/Conferences/JavaPosseRoundup/&quot;&gt;Java Posse Roundup&lt;/a&gt;, but unfortunately had to do client work most of the week. However, that didn&apos;t stop &lt;a href=&quot;http://jamesward.com&quot;&gt;James Ward&lt;/a&gt;, &lt;a href=&quot;http://mcginityphoto.com&quot;&gt;Trish&lt;/a&gt; and me from building a Shot-Ski and doing a 5-minute lightning talk on it. We had a great time doing research, getting locals to help us out and beta testing it with JPR attendees. I&apos;ll write a blog post on what we learned in the near future. In the meantime, you can check out Trish&apos;s &lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/sets/72157629825646555/&quot;&gt;Making a Shot-Ski photos&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;We left Crested Butte on Thursday (March 29th) and drove back to Denver to meetup with my parents (who drove down from Montana). Friday morning we hopped on a plane and flew to Orlando, Florida and took a bus to &lt;a href=&quot;http://www.portcanaveral.com/&quot;&gt;Port Canaveral&lt;/a&gt;. Abbie and Jack were at their Grandma&apos;s in West Palm Beach, so Trish&apos;s parents picked them up on their drive up from Naples. We all met with near perfect timing and boarded the &lt;a href=&quot;http://disneycruise.disney.go.com/ships-activities/ships/magic/&quot;&gt;Disney Magic&lt;/a&gt; around 4 o&apos;clock Friday afternoon.
&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358223863/&quot; href=&quot;http://farm9.staticflickr.com/8092/8358223863_5b7c2d31eb.jpg&quot; title=&quot;Raible&apos;s and McGinity&apos;s welcome to our Disney Cruise! by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8092/8358223863_5b7c2d31eb.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Raible&apos;s and McGinity&apos;s welcome to our Disney Cruise!&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center; margin-top: 10px&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/6913165196/&quot; href=&quot;http://farm8.staticflickr.com/7110/6913165196_3852220c54.jpg&quot; title=&quot;The Joes&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7110/6913165196_3852220c54_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;The Joes&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7059248379/&quot; href=&quot;http://farm8.staticflickr.com/7067/7059248379_48dc7faa8f.jpg&quot; title=&quot;Maureen and Abbie&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7067/7059248379_48dc7faa8f_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Maureen and Abbie&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/6913166028/&quot; href=&quot;http://farm8.staticflickr.com/7139/6913166028_525167a03e.jpg&quot; title=&quot;Leaving Port Canaveral&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7139/6913166028_525167a03e_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Leaving Port Canaveral&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7059249129/&quot; href=&quot;http://farm6.staticflickr.com/5454/7059249129_73faf0346e.jpg&quot; title=&quot;First Dinner!&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5454/7059249129_73faf0346e_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;First Dinner!&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a rev=&quot;http://www.flickr.com/photos/mraible/6913168284/&quot; href=&quot;http://farm8.staticflickr.com/7189/6913168284_21978748ef.jpg&quot; title=&quot;My Dad&apos;s Stateroom&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7189/6913168284_21978748ef_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;My Dad&apos;s Stateroom&quot; class=&quot;picture&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
We were immediately impressed with our rooms (we had 3 staterooms next to each other on the 5th floor forward) and their nice portal windows. We saw dolphins jumping in Port Canaveral as we departed and witnessed a horn battle between our ship and the Disney Fantasy. The Disney Fantasy was embarking on its maiden voyage and we saw it many times throughout the trip. That evening, we ventured to the first dinner seating (at 17:45). We were pleased to find out we&apos;d have the same servers all week, at different restaurants throughout the ship.&lt;/p&gt;
&lt;p id=&quot;key-west&quot;&gt;&lt;strong&gt;Key West&lt;/strong&gt;&lt;br/&gt;
Saturday morning, we arrived in Key West and had from 12am-9pm to explore. We took a tour in Key West and learned a lot about the history and architecture of the town. &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_fun_week_in_florida&quot;&gt;Last time we visited Key West&lt;/a&gt;, I was super impressed with &lt;a href=&quot;http://www.melfisher.org/&quot;&gt;Mel Fisher Maritime Museum&lt;/a&gt;. It was fun to show it to everyone and we savored some tasty beverages at Margaritaville afterward. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358246099/&quot; href=&quot;http://farm9.staticflickr.com/8496/8358246099_ced7e505fb.jpg&quot; title=&quot;Abbie on stage in Margaritaville Key West Florida by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8496/8358246099_ced7e505fb_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Abbie on stage in Margaritaville Key West Florida&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358247879/&quot; href=&quot;http://farm9.staticflickr.com/8218/8358247879_bcb948daa2.jpg&quot; title=&quot;Jimmy Buffet mural at Margaritaville Key West Florida by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8218/8358247879_bcb948daa2_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Jimmy Buffet mural at Margaritaville Key West Florida&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;We returned to the ship for dinner that evening and enjoyed the service of Omar (our main server) and Marijah (his assistant) once again. A couple hours laster, the boys (minus Jack) returned to shore to do some bonding. We wandered about, set a match to some cigars and made it back on the ship before it departed. We spent the remainder of the evening in the ship&apos;s Sports Bar watching March Madness and talking about Trish and my upcoming wedding.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7059251353/&quot; href=&quot;http://farm8.staticflickr.com/7137/7059251353_c391fa2b97.jpg&quot; title=&quot;Bonding in Key West&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7137/7059251353_c391fa2b97_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Bonding in Key West&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7059251961/&quot; href=&quot;http://farm8.staticflickr.com/7037/7059251961_6ed875ca80.jpg&quot; title=&quot;Key West Memories&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7037/7059251961_6ed875ca80_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Key West Memories&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7059252187/&quot; href=&quot;http://farm6.staticflickr.com/5156/7059252187_b845e44508.jpg&quot; title=&quot;Bonding Night with the Queen&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5156/7059252187_b845e44508_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Bonding Night with the Queen&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Sunday was a day at sea and we made the most of it. Lots of swimming, relaxing, playing with kids and enjoying each other&apos;s company. The ship had two different places that you could drop your kids off and Abbie (9) had a lot of fun attending their activities. Jack wasn&apos;t that into it and spent most of his day visiting the soft-serve ice cream machine. That evening, Trish and I journeyed to a romantic dinner at the ship&apos;s 18-and-older restaurant and celebrated our 1-year &quot;moved in together&quot; &lt;a href=&quot;http://raibledesigns.com/rd/entry/two_opening_days_with_a&quot;&gt;anniversary&lt;/a&gt;. 
&lt;/p&gt;
&lt;p id=&quot;grand-cayman&quot;&gt;&lt;strong&gt;Grand Cayman&lt;/strong&gt;&lt;br/&gt;
Monday, we arrived at Grand Cayman early in the morning and were ashore by 8am. We had a snorkeling expedition planned so promptly gathered and headed out to sea on a snorkeling boat. The weather was beautiful, the water was warm and snorkeling around a shipwreck was a very memorable experience.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358605930/&quot; href=&quot;http://farm9.staticflickr.com/8504/8358605930_10faae13ec.jpg&quot; title=&quot;Abbie and Jack arrive in Grand Cayman Islands by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8504/8358605930_10faae13ec_m.jpg&quot; width=&quot;160&quot; height=&quot;240&quot; alt=&quot;Abbie and Jack arrive in Grand Cayman Islands&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358610968/&quot; href=&quot;http://farm9.staticflickr.com/8083/8358610968_81fe3ffc3c.jpg&quot; title=&quot;Baba is excited to go snorkeling! by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8083/8358610968_81fe3ffc3c_m.jpg&quot; width=&quot;160&quot; height=&quot;240&quot; alt=&quot;Baba is excited to go snorkeling!&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center; margin-top: 10px&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358614606/&quot; href=&quot;http://farm9.staticflickr.com/8517/8358614606_bc30e195ae.jpg&quot; title=&quot;Barb and Joe going shipwreck snorkeling in Grand Cayman by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8517/8358614606_bc30e195ae_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Barb and Joe going shipwreck snorkeling in Grand Cayman&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358611866/&quot; href=&quot;http://farm9.staticflickr.com/8189/8358611866_c6465a4b55.jpg&quot; title=&quot;Abbie is a little nervous about the shipwreck by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8189/8358611866_c6465a4b55_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Abbie is a little nervous about the shipwreck&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;We visited the Hard Rock cafe to get my Dad a t-shirt, purchased some Tobago Rum Cakes and finished with a tour of the new &lt;a href=&quot;http://www.museum.ky/&quot;&gt;Cayman Islands National Museum&lt;/a&gt;. We were back on the ship by 4 so our parents could have their own romantic dinner that evening.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358600474/&quot; href=&quot;http://farm9.staticflickr.com/8213/8358600474_ea26b2cc90.jpg&quot; title=&quot;Raible and McGinity dinner by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8213/8358600474_ea26b2cc90.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Raible and McGinity dinner&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p id=&quot;cozumel&quot;&gt;&lt;strong&gt;Cozumel&lt;/strong&gt;&lt;br/&gt;
Tuesday morning, we woke up in Cozumel. Trish and my parents headed to shore for some exploring while I slept in with the kids and ate at the &quot;something for everyone&quot; breakfast buffet. In the afternoon, we all headed to shore for a submarine ride. The submarine adventure was very cool, going down 120 ft. and even taking us to the edge of a cliff on the ocean floor. We got to see several schools of fish, loads of coral reef and even a sea turtle swimming along the bottom.
&lt;/p&gt;
&lt;p style=&quot;text-align: center; style=&quot;margin-top: 10px&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7059257183/&quot; href=&quot;http://farm6.staticflickr.com/5346/7059257183_2255c37bbb.jpg&quot; title=&quot;Cute Kids&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5346/7059257183_2255c37bbb_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Cute Kids&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/6913175048/&quot; href=&quot;http://farm8.staticflickr.com/7191/6913175048_89a5099d94.jpg&quot; title=&quot;Submariners&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7191/6913175048_89a5099d94_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Submariners&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8359352848/&quot; href=&quot;http://farm9.staticflickr.com/8503/8359352848_935f576bbe.jpg&quot; title=&quot;Disney Double Date! by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8503/8359352848_935f576bbe_m.jpg&quot; width=&quot;240&quot; height=&quot;179&quot; alt=&quot;Disney Double Date!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358526648/&quot; href=&quot;http://farm9.staticflickr.com/8336/8358526648_9fd9f4deb7.jpg&quot; title=&quot;Mimi and Baba Monkeys by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8336/8358526648_9fd9f4deb7_m.jpg&quot; width=&quot;240&quot; height=&quot;179&quot; alt=&quot;Mimi and Baba Monkeys&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
The highlight of Cozumel was Manuel. After the submarine ride, we took a taxi to a restaurant with a great view and had a couple margaritas and some excellent steak tacos. While we were finishing up, a man walked up to us and asked if we wanted him to play us a song on his guitar. We said yes and gave him our small change ($1.75) for his serenade. 
&lt;/p&gt;
&lt;p&gt;
Shortly after, we got up to leave the restaurant and walk back to the ship. That&apos;s when Trish came up with a brilliant idea and asked Manuel if he&apos;d be willing to walk with us around town and play music the whole time. She offered him $20 and he quickly agreed. 
&lt;/p&gt;
&lt;p&gt;What followed was lots of laughs, smiling and dancing through the streets of Cozumel. Not only from us, but also from many of the locals. Manuel was an excellent mariachi musician and provided an exciting sense of celebration around us for the next 20 minutes.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/6913177494/&quot; href=&quot;http://farm6.staticflickr.com/5160/6913177494_f52f14d798.jpg&quot; title=&quot;Trish paid Manuel $20 to walk with us and play...&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5160/6913177494_f52f14d798_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Trish paid Manuel $20 to walk with us and play...&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/6913177864/&quot; href=&quot;http://farm8.staticflickr.com/7111/6913177864_6f6e4a2156.jpg&quot; title=&quot;Trish paid Manuel $20 to walk with us and play...&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7111/6913177864_6f6e4a2156_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Trish paid Manuel $20 to walk with us and play...&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center; margin-top: 10px&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8357526297/&quot; href=&quot;http://farm9.staticflickr.com/8463/8357526297_2b44023ebb.jpg&quot; title=&quot;Our posse with our Guitarr&#243;n Amigo in the Plaza Cozumel Mexico by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8463/8357526297_2b44023ebb.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Our posse with our Guitarr&#243;n Amigo in the Plaza Cozumel Mexico&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Wednesday was another day at sea as we traveled from Cozumel to Disney&apos;s private island in the Bahamas: Castaway Cay. We got a kick out of the ship&apos;s activities, from Beer Tasting to watching the John Carter movie in 3D. I snapped some sweet photos that night as we were watching the sun set over the horizon.
  &lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7059262891/&quot; href=&quot;http://farm6.staticflickr.com/5234/7059262891_9670e6a376.jpg&quot; title=&quot;A thing of beauty&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5234/7059262891_9670e6a376.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;A thing of beauty&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
  &lt;p style=&quot;text-align: center; padding-top: 5px&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7059263233/&quot; href=&quot;http://farm8.staticflickr.com/7139/7059263233_c1db64df83.jpg&quot; title=&quot;Long Lasting Love&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7139/7059263233_c1db64df83.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;Long Lasting Love&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p id=&quot;castaway-cay&quot;&gt;&lt;strong&gt;Castaway Cay&lt;/strong&gt;&lt;br/&gt;
At first, I thought Castaway Cay was going to be like Disney World, just on an island. I was expecting all kinds of rides and an amusement park. I was surprised to find it was nothing like that, but more of a private beach with all kinds of bars, games, water sports and even a water slide. The kids had a blast building sand castles, learning how to play checkers and exploring the island. I had a massage to the sound of ocean waves in the afternoon and we hopped back on the ship around 5 that afternoon. 
&lt;/p&gt;
&lt;p&gt;
That night, we had to pack up our luggage by 10 and put it out so it could be checked into our flight the next day. Disney did an awesome job of handling our luggage both to and from the cruise. All we had to do was attach bag tags in Denver and our bags arrived at our room shortly after boarding. On the way home, we gave them our bags on Thursday night and didn&apos;t see them until arriving in Denver.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8359355716/&quot; href=&quot;http://farm9.staticflickr.com/8234/8359355716_54dc5b7a31.jpg&quot; title=&quot;Our crew on the Disney Magic! by Trish, on Flickr&quot; rel=&quot;lightbox[cruise2012]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8234/8359355716_54dc5b7a31.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Our crew on the Disney Magic!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;
The last morning on the Disney Magic was a bit early. We had to be at breakfast at 6:45, were off the ship by 8 and at the Orlando airport by 9. Our flight was at 7 that night, so we suddenly found ourselves with 9 hours of free time and nothing to do. 
&lt;/p&gt;
&lt;p id=&quot;legoland&quot;&gt;&lt;strong&gt;LEGOLAND&lt;/strong&gt;&lt;br/&gt;
Last year around this time, Jack was struggling in school and getting in trouble almost every-other-day. In an attempt to promote good behavior, I told him I&apos;d take him to LEGOLAND if he had 10 days in a row of good behavior. This didn&apos;t seem to help and we ended the school year with no trip planned. However, when this school year started, he had excellent behavior (which has continued all year) and quickly remembered my promise.
&lt;/p&gt;
&lt;p&gt;As we were sitting in Orlando&apos;s airport, I remembered this promise and 30 minutes later we had a car rented and were driving to see some legos. We spent a joyous day there, riding the few rides they had and marveling over all the things you could build with legos. It was a &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157629772999213/&quot; title=&quot;Matt&apos;s LEGOLAND photos&quot;&gt;fun&lt;/a&gt; &lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/sets/72157632468036047/&quot; title=&quot;Trish&apos;s LEGOLAND photos&quot;&gt;way&lt;/a&gt; to end our wonderful vacation. &lt;/p&gt;
&lt;p&gt;This cruise was a first for myself, my kids and my parents. We were extremely impressed with Disney&apos;s Customer Service and have never felt more pampered in our lives. All the crew we interacted with knew our names by the second days and were some of the most kid-friendly people I&apos;ve ever met. The food was excellent, the weather was beautiful and the activities were plentiful. It wasn&apos;t until the last night that we realized there were 15 bars on the ship, and by that time it was too late for a pub crawl. Oh well, there&apos;s always next time. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;-)&quot; title=&quot;;-)&quot; /&gt;
&lt;p&gt;For more pictures from our Disney Magic voyage, see &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157629408393014&quot; title=&quot;Matt&apos;s Western Caribbean Cruise photos&quot;&gt;our&lt;/a&gt; &lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/sets/72157632459738307/&quot; title=&quot;Trish&apos;s Western Caribbean Cruise photos&quot;&gt;photos&lt;/a&gt; on Flickr. </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/comparing_web_frameworks_and_html5</id>
        <title type="html">Comparing Web Frameworks and HTML5 with Play Scala at Jfokus 2012</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/comparing_web_frameworks_and_html5"/>
        <published>2012-02-16T00:01:05-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jfokus" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/8355301175/&quot; title=&quot;Riddenholm Church by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8330/8355301175_36050ce11c_t.jpg&quot; width=&quot;66&quot; height=&quot;100&quot; alt=&quot;Riddenholm Church&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
Stockholm seems a lot like Denver this time of year. Cold, snowy and beautiful. &lt;a href=&quot;http://www.mcginityphoto.com/&quot;&gt;Trish&lt;/a&gt; and I arrived in Stockholm (Sweden) on Monday for the &lt;a href=&quot;http://jfokus.se&quot;&gt;Jfokus&lt;/a&gt; conference and we&apos;re traveling to Madrid today for the &lt;a href=&quot;http://springio.net/&quot;&gt;Spring I/O&lt;/a&gt; conference. I was invited to Jfokus within minutes of delivering my &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_html5_with_play_scala&quot;&gt;HTML5 with Play Scala talk at Devoxx&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Both the Jfokus and Spring I/O Organizers were interested in my Comparing JVM Web Frameworks talk, so I updated it to reflect my latest thoughts. First of all, I mentioned that there&apos;s a lot of great frameworks out there and I think the reason people are so apprehensive to choose one is because they&apos;ve chosen badly at one point. This might&apos;ve been Struts back in the day (even thought it was one of the best frameworks at the time) or it might be because a vendor talked them into it. However, if you look at the modern JVM frameworks today, you should be able to see that they&apos;re all pretty awesome.
&lt;/p&gt;
&lt;p&gt;
I mentioned how I think &lt;em&gt;Web&lt;/em&gt; developers should know JavaScript and CSS. If you&apos;re a &lt;em&gt;Java&lt;/em&gt; developer and you call yourself a web developer, you&apos;re letting your framework do too much of the work for you. I mentioned Rich Manalang&apos;s &lt;a href=&quot;http://blogs.atlassian.com/2012/01/modern-principles-in-web-development/&quot;&gt;Modern Principles in Web Development&lt;/a&gt;, where he talks about his core web development principles.
&lt;ul&gt;
&lt;li&gt;Designing for mobile first (even if you&#8217;re not building a mobile app)&lt;/li&gt;
&lt;li&gt;Build only single page apps&lt;/li&gt;
&lt;li&gt;Create and use your own REST API&lt;/li&gt;
&lt;li&gt;&#8220;Sex sells&#8221; applies to web apps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;ve found these principles to be true in my own experience and suggested that if you want to be a web developer, the frameworks you might want to learn are not traditional JVM web frameworks, but rather &lt;a href=&quot;http://paulhammant.com/2012/02/13/client-side-mvc-frameworks-compared/&quot;&gt;client-side MVC frameworks&lt;/a&gt;. For those Java developers that don&apos;t want to be web developers, I suggest they strengthen their services development knowledge by reading &lt;a href=&quot;http://www.infoq.com/articles/webber-rest-workflow&quot;&gt;Hot to GET a Cup of Coffee&lt;/a&gt;.
&lt;/p&gt;
You can see my updated presentation below, &lt;a href=&quot;http://www.slideshare.net/mraible/comparing-jvm-web-frameworks-jfokus-2012&quot;&gt;on Slideshare&lt;/a&gt; or as a &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/Comparing_JVM_Web_Frameworks_Jfokus2012.pdf&quot;&gt;downloadable PDF&lt;/a&gt;. You can also &lt;a href=&quot;http://www.jfokus.se/jfokus/video.jsp?v=3084&quot;&gt;watch the video&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt; &lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/11581955?rel=0&quot; width=&quot;510&quot; height=&quot;426&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;&gt;&lt;/iframe&gt;
&lt;/p&gt;
&lt;p&gt;I delivered my 2nd presentation on HTML5 with Play Scala, CoffeeScript and Jade on Wednesday morning. This talk is one of my favorites and I prepared for it over the last several weeks by adding &lt;a href=&quot;http://raibledesigns.com/rd/entry/secure_json_services_with_play&quot;&gt;JSON CRUD Services and SecureSocial&lt;/a&gt; to my HTML5 Fitness Tracking application. Right before we left for Jfokus, I was able to get everything to work, but didn&apos;t spend as much time as I&apos;d like working on the mobile client. If this talk gets accepted for &lt;a href=&quot;http://www.devoxx.com/display/FR12/Accueil&quot;&gt;Devoxx France&lt;/a&gt;, I plan on spending most of my time enhancing the mobile client. After my latest experience developing, I can see how Rich&apos;s first principle (above) makes a lot of sense.
&lt;/p&gt;
&lt;p&gt;Below is my presentation for this talk. Of course, it&apos;s &lt;a href=&quot;http://www.slideshare.net/mraible/html5-with-play-scala-coffeescript-and-jade-jfokus-2012&quot;&gt;on Slideshare&lt;/a&gt; and &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/HTML5_with_Play_Scala_CoffeeScript_and_Jade_Jfokus2012.pdf&quot;&gt;downloadable as a PDF&lt;/a&gt;.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/11582395?rel=0&quot; width=&quot;510&quot; height=&quot;426&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;&gt;&lt;/iframe&gt;
&lt;/p&gt;
&lt;p&gt;I also updated the &lt;a href=&quot;http://vimeo.com/36826202&quot;&gt;Developing Play More demo video&lt;/a&gt; to show my latest efforts.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//player.vimeo.com/video/36826202?title=0&amp;amp;byline=0&amp;amp;portrait=0&quot; width=&quot;510&quot; height=&quot;287&quot; frameborder=&quot;0&quot; webkitAllowFullScreen mozallowfullscreen allowFullScreen&gt;&lt;/iframe&gt;
&lt;/p&gt;
&lt;p&gt;Delivering these talks at Jfokus was a lot of fun. Yes, it was a lot of work and stress to prepare them. However, I also learned a lot creating them and I hope the audience benefitted from that. 
&lt;/p&gt;
&lt;p&gt;  
&lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/8350751579/&quot; title=&quot;Jfokus 2012 by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8371/8350751579_33fe72872a_t.jpg&quot; width=&quot;67&quot; height=&quot;100&quot; alt=&quot;Jfokus 2012&quot; style=&quot;border: 1px solid black; float: left; margin: 0 10px 0 0&quot;&gt;&lt;/a&gt;
The conference itself was incredible. I got to meet &lt;a href=&quot;https://twitter.com/peterhilton&quot;&gt;Peter Hilton&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/javahelena&quot;&gt;Helena Hjert&#233;n&lt;/a&gt; as I was registering.
The speaker&apos;s dinner at &lt;a href=&quot;http://f12.se/&quot;&gt;F12&lt;/a&gt; was off-the-hook good and I had the pleasure of finally meeting &lt;a href=&quot;http://rickardoberg.wordpress.com/&quot;&gt;Rickard &#214;berg&lt;/a&gt;. 

&lt;/p&gt;
&lt;p&gt;I also attended some fantastic presentations, including Peter Hilton&apos;s &lt;a href=&quot;http://raibledesigns.com/rd/entry/play_framework_2_0_with&quot;&gt;Play Framework 2.0&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/bodiltv&quot;&gt;Bodil Stokke&apos;s&lt;/a&gt; &lt;a href=&quot;http://bodil.github.com/coffeescript/#landing-slide&quot;&gt;CoffeeScript: JavaScript without the Fail&lt;/a&gt;, &lt;a href=&quot;http://twitter.com/pamelafox&quot;&gt;Pamela Fox&apos;s&lt;/a&gt; &lt;a href=&quot;http://client-side-storage.appspot.com&quot;&gt;Client-side Storage&lt;/a&gt; and &lt;a href=&quot;http://twitter.com/hseeberger&quot;&gt;Heiko Seeberger&apos;s&lt;/a&gt; Scala in Action. I don&apos;t know if Heiko has published any slides, but I&apos;m guessing not since most of his presentation was live coding. 
&lt;/p&gt;
&lt;p&gt;I have lots of good memories from Jfokus. Many thanks to &lt;a href=&quot;http://twitter.com/matkar&quot;&gt;Mattias&lt;/a&gt; for inviting me!</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/play_framework_2_0_with</id>
        <title type="html">Play Framework 2.0 with Peter Hilton at Jfokus</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/play_framework_2_0_with"/>
        <published>2012-02-14T07:17:08-07:00</published>
        <updated>2012-02-14T13:19:40-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jfokus" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="http" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This week, I&apos;m at &lt;a href=&quot;http://jfokus.se&quot;&gt;Jfokus&lt;/a&gt; in Stockholm, Sweden. After a fun speaker&apos;s dinner last night, I got up this morning and polished up my presentations and demo before attending the conference. The first session I attended was &lt;a href=&quot;http://twitter.com/PeterHilton&quot;&gt;Peter Hilton&apos;s&lt;/a&gt; &lt;a href=&quot;http://www.jfokus.se/jfokus/talks.jsp#Play%20Framework%202.0&quot;&gt;Play Framework 2.0&lt;/a&gt; presentation. Below are my notes from this talk.
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;Peter is a Senior &lt;em&gt;Web&lt;/em&gt; Developer, not a Java Developer. His first slide states the following:&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&quot;Play brings type safe high-productivity web development to the JVM.&quot;
&lt;/p&gt;
&lt;p&gt;
New features in Play 2.0: type-safety, template syntax, compile-time checking and asynchronous HTTP programming. Java, Scala - the language you use is less important than the fact that Play is a &lt;em&gt;web&lt;/em&gt; framework. It&apos;s a full-stack framework and has everything you need out-of-the-box to build a web application. Play focuses on HTTP and doesn&apos;t try to hide it. It&apos;s designed &lt;em&gt;by&lt;/em&gt; web developers &lt;em&gt;for&lt;/em&gt; web developers.
&lt;/p&gt;
&lt;p&gt;
With Play, the Back button just works. Your web framework shouldn&apos;t break the first button on your browser&apos;s toolbar. The Reload button also works: make a change, hit reload and your changes (even in Scala classes) are shown. You design the URLs and you can use &quot;clean&quot; URLs. DX (Developer eXperience) is Peter&apos;s new term. Usability matters: as a developer, you deserve a framework that provides a good experience.
&lt;/p&gt;
&lt;p&gt;
Play doesn&apos;t fight HTTP or the browser. It&apos;s stateless and HTTP-centric. A few years ago, it seemed like a good idea to try and keep state on the server. It sounded like a good idea, but in practice, it&apos;s a really bad idea - especially for things like the back button. Play matches the web&apos;s stateless HTTP architecture. 
&lt;/p&gt;
&lt;p&gt;
As a Java EE developer, PHP and Rails developers have been laughing at us for years. Like Father Christmas, Peter&apos;s heard of class-reloading, but he hasn&apos;t actually seen it. Code reloading is the most important part of DX and about achieving high-productivity in web development. 
&lt;/p&gt;
&lt;p&gt;
URLs want to be loved too. REST architecture isn&apos;t just for web service APIs. When you have clean URLs, you can tweet them, post them and email them. 
&lt;/p&gt;
&lt;p&gt;
&quot;You would need to be a super-hero to successfully use some web frameworks.&quot; They show you a blank screen in the browser and you have to look at your console&apos;s stack trace to figure it out. With Play, the error is shown in your browser and you can see the exact line it happens on.
&lt;/p&gt;
&lt;p&gt;
In Play 1.x, there was a lot of magic and a lot of bytecode enhancement at runtime. This allowed the API to be a lot nicer than traditional Java APIs. However, it caused issues when users viewed the enhanced source and it also caused issues in IDEs. With Play 2.0, the framework itself is implemented in Scala. Scala removes the need for so much bytecode enhancement. There is less &apos;magic&apos; and strangeness in the API. The code you see in the IDE is the code that runs. Scala source code is not necessarily harder to read. 1.x had some pretty hairy Java code, and you could tell when you dug into it. Especially when you were deep into the source code and saw that a lot of the comments were in French.
&lt;/p&gt;
&lt;p&gt;
Play 2.0&apos;s template system is based on Scala. It&apos;s similar to the lightweight template syntax in Play 1.x. Templates are compiled into class files for run-time speed. For example:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
@(products: Seq[Product])

&amp;lt;ul&gt;
@for(product &amp;lt;- products) {
  &amp;lt;li&gt;@product.name&amp;lt;/li&gt;
}
&amp;lt;/ul&gt;

@summary(products)
&lt;/pre&gt;
&lt;p&gt;We used to think XML-based templates were great, but it turns out it&apos;s a terrible idea. Mostly because you end up having to invent an expression language to create valid XML (to avoid putting XML in your HTML attributes). With Play 2.0&apos;s templates, you can define tags in your templates as regular Scala methods.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
@display(product: models.Product) ={
 &amp;lt;a href=&quot;@routes.Product.details(product.id)&quot;&gt;@product.name&amp;lt;/a&gt; 
}

@for(product &amp;lt;- products) {
  @display(product)
}
&lt;/pre&gt;
&lt;p&gt;The compile-time checking in Play 2.0 is not just for Java and Scala classes. It also compiles your HTTP routes file (which maps requests to controller actions). Furthermore, it compiles your templates, JavaScript files (using Google Closure Compiler), CoffeeScript files and LESS stylesheets. 
&lt;/p&gt;
&lt;p&gt;
Play supports modern web development. It&apos;s designed to work with HTML5, but there&apos;s no constraints on HTML output. It&apos;s front-end developer friendly and has great DX. UI components belong in the client, e.g. jQuery UI. It also has built-in support for improvements to CSS (LESS) and JavaScript (CoffeeScript).
&lt;/p&gt;
&lt;p&gt;
A few years ago, it seemed like a really good idea to hide JavaScript from the web developer. Web frameworks used to say &quot;You don&apos;t need to see the JavaScript or the HTML, we&apos;ll handle generating your components for you.&quot; Now, if you&apos;re building a web application and you don&apos;t know any jQuery, you doing it the hard way. You should learn how to work with front-end developers or learn how to do it yourself. And make sure your web framework allows this sort of development. 
&lt;/p&gt;
&lt;p&gt;
The future of web programming is asynchronous. You&apos;ll perform simultaneous web service requests. You&apos;ll process streams of data, instead of filling up memory or disk. You&apos;ll publish real-time data and have predictable and minimal resource consumption. In the long term, this changes everything. The future of the web is real-time and asynchronous. With Play 2.0, it&apos;s not just another feature, it&apos;s a fundamental aspect of the architecture. Play&apos;s internal architecture uses a reactive model based on Iteratee IO. 
&lt;/p&gt;
&lt;p&gt;In summary, use Play 2, use HTML5, deploy to the Cloud. There&apos;s two forthcoming books on Play (both from Manning) and Play 2.0 RC1 will be released today.&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
I think Peter did a good job of summarizing the new features in Play 2.0, especially how templates work. I enjoyed his emphasis on HTTP and how Play leverages the browser (back, reload and as a console). I liked his humorous speaking style, and agree with his emphasis that client-side development skills are important for modern web applications. I think Play 2.0 is making a big bet on Scala and asynchronous programming, but if they live up to the hype, it should be a very enjoyable web framework to develop with.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/twitter_s_open_source_summit</id>
        <title type="html">Twitter&apos;s Open Source Summit: Bootstrap 2.0 Edition</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/twitter_s_open_source_summit"/>
        <published>2012-02-01T11:28:40-07:00</published>
        <updated>2012-02-01T17:32:46-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="opensource" scheme="http://roller.apache.org/ns/tags/" />
        <category term="twitter" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ossummit" scheme="http://roller.apache.org/ns/tags/" />
        <category term="hogan.js" scheme="http://roller.apache.org/ns/tags/" />
        <category term="nasa" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Every few months, &lt;a href=&quot;http://twitter.com&quot;&gt;Twitter&lt;/a&gt; hosts an Open Source Summit to talk about tools they&apos;re using. Since I happened to be near San Fransisco, I happily attended their latest &lt;a href=&quot;https://twitter.com/search?q=%23ossummit&quot;&gt;#ossummit&lt;/a&gt; to learn about &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Bootstrap 2.0&lt;/a&gt;. Below are my notes from last night&apos;s event.
&lt;/p&gt;
&lt;p&gt;
95% of Twitter&apos;s infrastructure is powered by open source. They hope to contributing back to open source by doing 2-3 summits per year. Without open source, there would be no Twitter. You can find a bunch of Twitter&apos;s open source contributions at &lt;a href=&quot;http://twitter.github.com&quot;&gt;twitter.github.com&lt;/a&gt;. They&apos;re also big fans of Apache and commit to a wide variety of projects there.
&lt;/p&gt;
&lt;p id=&quot;bootstrap&quot;&gt;
&lt;strong&gt;Bootstrap&lt;/strong&gt;&lt;br/&gt;
Bootstrap is developed by two main guys: &lt;a href=&quot;http://twitter.com/mdo&quot;&gt;@mdo&lt;/a&gt; and &lt;a href=&quot;http://twitter.com/fat&quot;&gt;@fat&lt;/a&gt;. 
Mark (@mdo) has been a designer at Twitter for 2 years. He started on the Revenue Team with ads, but has been working on redesign for last 4 months. Has been doing HTML and CSS for about 11 years. He used Notepad on Windows to build his GeoCities site. 
&lt;/p&gt;
&lt;p&gt;boot&amp;middot;strap: simple and flexible HTML, CSS, and JavaScript for popular user interface components and interactions.
&lt;/p&gt;
&lt;p&gt;
Work on Bootstrap started about a 1.5 years ago. Internal tools didn&apos;t get the proper attention they needed. They figured out there was a lot of people that wanted good looking UIs and interactions. It became Twitter Blueprint and was mostly used internally. Jacob (@fat) started shortly after first release and decided to add some JavaScript on top of it. The JavaScript for Bootstrap was &lt;a href=&quot;https://twitter.com/#!/pamelafox/status/164532457654329347&quot;&gt;originally the &quot;Twitter Internal Toolkit&quot; or &quot;TIT&quot;&lt;/a&gt; and was built on Moo Tools. Jacob was like &quot;we gotta open source this, it&apos;s gonna be huge!&quot; (he was right).
&lt;/p&gt;
&lt;p&gt;The 1.0 release supported Chrome, Safari and Firefox (everyone at Twitter was on Macs). 1.3 added cross-browser support and JavaScript plugins. 
&lt;/p&gt;
&lt;p&gt;
Now there&apos;s Bootstrap 2 (just released!). They rewrote all the documentation and components and removed legacy code. 
&lt;/p&gt;
&lt;p&gt;So, &lt;em&gt;what&apos;s new?&lt;/em&gt;
The biggest thing is the docs. Previously had live examples, now shows live examples and why you would do something, as well as additional options. The &quot;topbar&quot; has been renamed to &quot;navbar&quot;, but it&apos;s still got all the hotness. It&apos;s &lt;a href=&quot;http://www.alistapart.com/articles/responsive-web-design/&quot;&gt;responsive&lt;/a&gt; with CSS media queries for small devices, tablets, small desktops and large desktops. This means the layout &lt;em&gt;breaks&lt;/em&gt; at certain points and stacks elements to fit on smaller screens.
&lt;/p&gt;
&lt;p&gt;
CSS: smarter defaults, better classes. In 1.4, all forms were stacked. Now they can flow horizontally. Tables are now namespaced so Bootstrap&apos;s styles don&apos;t apply to all tables. The available table, form and navigation classes are as follows:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
// Tables
.table { ... }
.table-striped { ... }
.table-bordered { ... }
.table-condensed { ... }

// Forms
.form-inline { ... }
.form-search { ... }
.form-horizontal { ... }

// Nav
.nav { ... }
.nav-tabs { ... }
.nav-pills { ... }
&lt;/pre&gt;
&lt;p&gt;The goals with 2.0 are consistency, simplification and future-proofing styles. With 1.4, buttons used &quot;btn primary&quot; and it caused problems if you wanted to have a &quot;primary&quot; class in your project. With 2.0, buttons and all elements are namespaced to avoid collisions (now it&apos;s .btn-primary).
&lt;/p&gt;
&lt;p&gt;After Mark finished talking about the design of Bootstrap, Jacob (@fat) started talking about Bootstrap&apos;s JavaScript. Jacob works on The Platform Team at Twitter and claims he made a lot of mistakes with 1.x. However, thanks to semantic versioning, 2.0 is a new version and he got to start over!
&lt;/p&gt;
&lt;p&gt;The biggest change in 2.0 is the use of data attributes (a.k.a. data-*). They were using them in 1.x, but not to the full potential of what they can be and should be. The first class API for Bootstrap JavaScript is data attributes (or HTML), not JavaScript.
&lt;/p&gt;
&lt;p&gt;With 1.x, you could add an anchor to close modals and alerts.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
// 1.x closing modal/alerts
&amp;lt;a class=&quot;close&quot; href=&quot;#&quot;&gt;&amp;times;&amp;lt;/a&gt;
&lt;/pre&gt;
&lt;p&gt;However, if you put your alerts in your modals, you close them all when you likely only wanted to close one. 2.0 uses a &quot;data-dismiss&quot; attribute.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;a class=&quot;close&quot; data-dismiss=&quot;model&quot;&gt;&amp;times&amp;lt;/a&gt;
&lt;/pre&gt;
&lt;p&gt;This allows you to target what you want closed (modals or alerts, etc.). You know exactly what&apos;s going to happen just by reading the code. Another example is the &quot;href&quot; attribute of an anchor. Rather than using &quot;href&quot;, you can now use &quot;data-target&quot;. 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
// 1.x href = target
&amp;lt;a href=&quot;#myModal&quot; data-toggle=&quot;modal&quot;&gt;Launch&amp;lt;/a&gt;

// 2.x data-target = target
&amp;lt;a data-target=&quot;.fat&quot; data-toggle=&quot;modal&quot;&gt;Launch&amp;lt;/a&gt;
&lt;/pre&gt;
&lt;p&gt;If you&apos;d rather turn off the data attribute API, or just part of it, you can do so by using the following:
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
// Turn off all data-api
$(&apos;body&apos;).off(&apos;.data-api&apos;)

// Turn off alert data-api
$(&apos;body).off(&apos;.alert.data-api&apos;)
&lt;/pre&gt;
&lt;p&gt;2.0&apos;s JavaScript API has the same stuff, but better. You can turn off the data-api and do everything with JavaScript. They copied jQuery UI in a lot of ways (defaults, constructors, etc.). &lt;a href=&quot;http://twitter.github.com/bootstrap/javascript.html&quot;&gt;Bootstrap&apos;s JavaScript&lt;/a&gt; has 12 plugins. New ones include collapse, carousel and typeahead. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://twitter.github.com/bootstrap/download.html&quot;&gt;Customize&lt;/a&gt; - a new tab that lets you customize and download Bootstrap. It&apos;s basically an alternative to customizing .less files and allows you to choose components, select jQuery plugins, customize variables (colors, font-sizes, backgrounds) and download. &lt;/p&gt;

&lt;p&gt;What does the future hold? Internationalization, improving responsiveness, more new features and bug fixes. 
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
After both Mark and Jacob gave their talks, they talked together about Community and how great it&apos;s been. Even if you&apos;re not into writing CSS and JavaScript, they mentioned they still wanted to hear from you. To give an example of great community contributions, one guy opened 50 issues in the last 2 days.&lt;/p&gt;
&lt;p&gt;Someone in the audience asked why they used LESS over SASS. Jacob said the main reason they use LESS is because they&apos;re good friends with the guy who invented it (&lt;a href=&quot;http://cloudhead.io/&quot;&gt;Alexis&lt;/a&gt;). SASS turns CSS into a programming language, but they wanted to maintain the approachability of CSS, which LESS does. There&apos;s no plans to do an official SASS port, but there is talk of doing one. One advantage of the current LESS compiler is they rewrote it to have better output so it&apos;s far more readable.
&lt;/p&gt;
&lt;p id=&quot;nasa&quot;&gt;&lt;strong&gt;NASA&lt;/strong&gt;&lt;br/&gt;
After Mark and Jacob finished, there was a 5 minute break to grab beers and snacks. Then Sean Herron (&lt;a href=&quot;http://twitter.com/seanherron&quot;&gt;@seanherron&lt;/a&gt;) (a.k.a. &quot;NASA Bro&quot;) talked about Bootstrap at NASA. He actually didn&apos;t talk about Bootstrap much, except that they used it for &lt;a href=&quot;http://code.nasa.gov&quot;&gt;code.NASA&lt;/a&gt;. He talked about NASA and how it&apos;s playing a key role in the movement towards open data, open source and open standards in our federal government. He mentioned how &lt;a href=&quot;http://data.nasa.gov/&quot;&gt;data.NASA&lt;/a&gt; was launched last August and that they helped develop &lt;a href=&quot;http://openstack.org/&quot;&gt;OpenStack&lt;/a&gt;. Finally, he mentioned &lt;a href=&quot;http://open.nasa.gov/&quot;&gt;open.NASA&lt;/a&gt;, which is a collaborative approach to open, direct and transparent communication about our space program.&lt;/p&gt;
&lt;/p&gt;
&lt;p id=&quot;hogan.js&quot;&gt;&lt;strong&gt;Hogan.js&lt;/strong&gt;&lt;br/&gt;
Next up, Rob Sayre (&lt;a href=&quot;http://twitter.com/sayrer&quot;&gt;@sayrer&lt;/a&gt;) talked about &lt;a href=&quot;http://twitter.github.com/hogan.js/&quot;&gt;Hogan.js&lt;/a&gt;. 
Rob has been at Twitter for a few months, before that he wrote JavaScript at other places. Hogan.js is a compiler for &lt;a href=&quot;http://mustache.github.com/&quot;&gt;Mustache templates&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
&lt;em&gt;Why Mustache?&lt;/em&gt; Because it&apos;s similar to HTML and easy to edit. You can mock data as JSON files and programmers are not required.
&lt;/p&gt;
&lt;p&gt;
At Twitter, designers can do the CSS and Mustache without connecting to the backend. It has cross-language support in Ruby, Java and JavaScript. However, client-side template compilation has performance problems, especially in IE7 on a Windows XP box with 4 viruses. 
&lt;/p&gt;
&lt;p&gt;So they had a few choices: work on mustache.js, or use Dust.js or Handlebars.js. The compilers are very nice for Dust.js and Handlebars.js, but they&apos;re huge. Handlebar&apos;s parser is 4000 lines. The entire Hogan.js file is 500 lines. They decided they were too large to send to the browser&apos;s of their users, so they chose to write a better compiler for Mustache.
&lt;/p&gt;
&lt;p&gt;
Hogan.js&apos;s main features:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compile on the server&lt;/li&gt;
&lt;li&gt;Parser API&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Performance is &lt;em&gt;much&lt;/em&gt; better with Hogan.js than Mustache.js. On IE7 - Hogan is 5x faster than Mustache. On an iPhone, it&apos;s about the same (and an iPhone&apos;s browser is faster than IE7 on a decent computer). With modern browsers (Chrome 17, Safari 5 and Firefox 10), it&apos;s more than 10x faster.
&lt;/p&gt;
&lt;p&gt;Hogan.js is currently used at Twitter for Tweet embedding, the Bootstrap build process and soon, Twitter.com.
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
It&apos;s been awhile since I got excited about an open source project. Bootstrap has helped me a lot recently, in my &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_html5_with_play_scala&quot;&gt;&lt;em&gt;Play More!&lt;/em&gt;&lt;/a&gt; mobile app, on some client projects and I&apos;m in the process of &lt;a href=&quot;http://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with&quot;&gt;refreshing AppFuse&apos;s UI to use it&lt;/a&gt;. I love how you can add a class or two to an element and all of a sudden they pop with good looks. The main problem with Bootstrap at this point is that a lot of Bootstrapped apps look the same. There&apos;s talk of adding themes in a future release to help alleviate this problem. In the meantime, there&apos;s a lot to get excited about with 2.0.
&lt;/p&gt;
&lt;p&gt;Thanks to Twitter for hosting this event and kudos to Mark and Jacob (and the community!) for such a fantastic project. </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with</id>
        <title type="html">Refreshing AppFuse&apos;s UI with Twitter Bootstrap</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with"/>
        <published>2012-01-31T17:12:17-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="springmvc" scheme="http://roller.apache.org/ns/tags/" />
        <category term="twitter" scheme="http://roller.apache.org/ns/tags/" />
        <category term="struts2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ui" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">The last time AppFuse had an update done to its look and feel was in way back in 2006. I&apos;ve done a lot of consulting since then, which has included a fair bit of page speed optimization, HTML5 development and integrating smarter CSS. It was way back in &apos;05 when we first started looking at adding a CSS Framework to AppFuse.  It was Mike Stenhouse&apos;s &lt;a href=&quot;http://www.contentwithstyle.co.uk/content/a-css-framework/&quot;&gt;CSS Framework&lt;/a&gt; that provided the &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_css_framework&quot;&gt;inspiration&lt;/a&gt; and my &lt;a href=&quot;http://raibledesigns.com/rd/entry/css_framework_design_contest_final&quot;&gt;CSS Framework Design Contest&lt;/a&gt; that provided its current themes (&lt;a href=&quot;http://css.appfuse.org/themes/puzzlewithstyle&quot;&gt;puzzlewithstyle&lt;/a&gt;, &lt;a href=&quot;http://css.appfuse.org/themes/andreas01&quot;&gt;andreas01&lt;/a&gt; and &lt;a href=&quot;http://css.appfuse.org/themes/simplicity&quot;&gt;simplicity&lt;/a&gt;).
&lt;/p&gt;
&lt;p&gt;Since then, a lot of CSS Frameworks have been invented, including &lt;a href=&quot;http://www.blueprintcss.org/&quot;&gt;Blueprint&lt;/a&gt; in 2007 and &lt;a href=&quot;http://compass-style.org/&quot;&gt;Compass&lt;/a&gt; in 2008. However, neither has taken the world by storm like &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Twitter Bootstrap&lt;/a&gt;. From &lt;a href=&quot;http://www.alistapart.com/articles/building-twitter-bootstrap/&quot;&gt;Building Twitter Bootstrap&lt;/a&gt;:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
A year-and-a-half ago, a small group of Twitter employees set out to improve our team&#8217;s internal analytical and administrative tools. After some early meetings around this one product, we set out with a higher ambition to create a toolkit for anyone to use within Twitter, and beyond. Thus, we set out to build a system that would help folks like us build new projects on top of it, and Bootstrap was conceived.&lt;br/&gt;
...&lt;br/&gt;
Today, it has grown to include dozens of components and has become the most popular project on GitHub with more than 13,000 watchers and 2,000 forks.&lt;/p&gt;
&lt;p&gt;&lt;p&gt;The fact that Bootstrap has become the most popular project on GitHub says a lot. For &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/AppFuse-next-td3634415.html&quot;&gt;AppFuse.next&lt;/a&gt;, I&apos;d like to integrate a lot of my learnings over the past few years, as well as support HTML5 and modern browsers as best we can. This means &lt;a href=&quot;http://code.google.com/speed/page-speed/docs/rules_intro.html&quot;&gt;page speed optimizations&lt;/a&gt;, getting rid of Prototype and Scriptaculous in favor of jQuery, adding &lt;a href=&quot;http://code.google.com/p/wro4j/&quot;&gt;wro4j&lt;/a&gt; for resource optimization and integrating &lt;a href=&quot;http://html5boilerplate.com/&quot;&gt;HTML5 Boilerplate&lt;/a&gt;. I&apos;ve used Twitter Bootstrap for my &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_html5_with_play_scala&quot;&gt;&lt;em&gt;Play More!&lt;/em&gt; app&lt;/a&gt;, as well as some recent client projects. Its excellent documentation has made it easy to use and I love the way you can simply add classes to elements to make them transform into something beautiful.
&lt;/p&gt;
&lt;p&gt;Last week, I spent a couple late nights integrating &lt;a href=&quot;http://thinkvitamin.com/design/twitter-bootstrap-2-0/&quot;&gt;Twitter Bootstrap 2.0&lt;/a&gt; into the Struts 2 and Spring MVC versions of AppFuse. The layout was pretty straightforward thanks to &lt;a href=&quot;http://markdotto.com/bs2/docs/scaffolding.html&quot;&gt;Scaffolding&lt;/a&gt;. Creating the Struts Menu Velocity template to produce &lt;a href=&quot;http://markdotto.com/bs2/docs/javascript.html#dropdowns&quot;&gt;dropdowns&lt;/a&gt; wasn&apos;t too difficult. I added &lt;a href=&quot;http://markdotto.com/bs2/docs/base-css.html#tables&quot;&gt;class=&quot;table table-condensed&quot;&lt;/a&gt; to the list screen tables, &lt;a href=&quot;http://markdotto.com/bs2/docs/base-css.html#forms&quot;&gt;class=&quot;well form-horizontal&quot;&lt;/a&gt; to forms and &lt;a href=&quot;http://markdotto.com/bs2/docs/base-css.html#buttons&quot;&gt;class=&quot;btn primary&quot;&lt;/a&gt; to buttons. 
&lt;/p&gt;
&lt;p&gt;  
I also added validation errors with the &quot;help-inline&quot; class. This is also where things got tricky with Struts and Spring MVC. For the form elements in Bootstrap, they recommend you use a &quot;control-group&quot; element that contains your label and a &quot;controls&quot; element. The control contains the input/select/textarea and also the error message if there is one. Here&apos;s a sample element waiting for data: 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div class=&quot;control-group&quot;&amp;gt;
    &amp;lt;label for=&quot;name&quot; class=&quot;control-label&quot;&amp;gt;Name&amp;lt;/label&amp;gt;
    &amp;lt;div class=&quot;controls&quot;&amp;gt;
        &amp;lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot;&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Below is what that element should look like to display a validation error:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div class=&quot;control-group error&quot;&amp;gt;
    &amp;lt;label for=&quot;name&quot; class=&quot;control-label&quot;&amp;gt;Name&amp;lt;/label&amp;gt;
    &amp;lt;div class=&quot;controls&quot;&amp;gt;
        &amp;lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; value=&quot;&quot;&amp;gt;
        &amp;lt;span class=&quot;help-inline&quot;&amp;gt;Please enter your name.&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;You can see this markup is pretty easy, you just need to add an &quot;error&quot; class to &lt;em&gt;control-group&lt;/em&gt; and span to show the error message. With Struts 2, this was pretty easy thanks to its customizable templates for its &lt;a href=&quot;http://struts.apache.org/2.x/docs/struts-tags.html&quot;&gt;tags&lt;/a&gt;. All I had to do was create a &quot;template/css_xhtml&quot; directory in &lt;em&gt;src/main/webapp&lt;/em&gt; and modify checkbox.ftl, controlfooter.ftl, controlheader-core.ftl and controlheader.ftl to match Bootstrap&apos;s conventions. 
&lt;/p&gt;
&lt;p&gt;
Spring MVC was a bit trickier. Since its tags don&apos;t have the concept of writing an entire control (label and field), I had to do a bit of finagling to get things to work. In the current implementation, Struts 2 forms have a single line for a &lt;em&gt;control-group&lt;/em&gt; and its &lt;em&gt;control-label&lt;/em&gt; and &lt;em&gt;controls&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;s:textfield key=&quot;user.firstName&quot; required=&quot;true&quot;/&gt;&lt;/pre&gt;
&lt;p&gt;With Spring MVC, it&apos;s a bit more complex:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;spring:bind path=&quot;user.firstName&quot;&amp;gt;
&amp;lt;fieldset class=&quot;control-group${(not empty status.errorMessage) ? &apos; error&apos; : &apos;&apos;}&quot;&amp;gt;
&amp;lt;/spring:bind&amp;gt;
    &amp;lt;appfuse:label styleClass=&quot;control-label&quot; key=&quot;user.firstName&quot;/&amp;gt;
    &amp;lt;div class=&quot;controls&quot;&amp;gt;
        &amp;lt;form:input path=&quot;firstName&quot; id=&quot;firstName&quot; maxlength=&quot;50&quot;/&amp;gt;
        &amp;lt;form:errors path=&quot;firstName&quot; cssClass=&quot;help-inline&quot;/&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/fieldset&amp;gt;
&lt;/pre&gt;
&lt;p&gt;You could probably overcome this verbosity with &lt;a href=&quot;http://today.java.net/pub/a/today/2003/11/14/tagfiles.html&quot;&gt;Tag Files&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Figuring out if a &lt;em&gt;control-group&lt;/em&gt; needed an error class before the input tag was rendered was probably the hardest part of this exercise. This was mostly due to Bootstrap&apos;s great documentation and useful examples (viewed by inspecting the markup). Below are some screenshots of the old screens and new ones. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center; vertical-align: top&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7173/6787781357_c4c65c7c74_b.jpg&quot; title=&quot;Old UI - Login&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7173/6787781357_c4c65c7c74_t.jpg&quot; width=&quot;100&quot; height=&quot;60&quot; alt=&quot;Old UI - Login&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm8.staticflickr.com/7142/6787781421_0c7851b414_b.jpg&quot; title=&quot;Old UI - Users&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7142/6787781421_0c7851b414_t.jpg&quot; width=&quot;100&quot; height=&quot;60&quot; alt=&quot;Old UI - Users&quot; style=&quot;margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm8.staticflickr.com/7035/6787781725_3a1f0218c1_b.jpg&quot; title=&quot;Old UI - Edit Profile&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7035/6787781725_3a1f0218c1_t.jpg&quot; width=&quot;100&quot; height=&quot;82&quot; alt=&quot;Old UI - Edit Profile&quot; style=&quot;margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;p&gt;  
&lt;p style=&quot;text-align: center; vertical-align: top&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7025/6787781477_ec2ac7a93b_b.jpg&quot; title=&quot;New UI - Login&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7025/6787781477_ec2ac7a93b_t.jpg&quot; width=&quot;100&quot; height=&quot;60&quot; alt=&quot;New UI - Login&quot;&gt;&lt;/a&gt;
  
&lt;a href=&quot;http://farm8.staticflickr.com/7015/6787781597_6558d94bb5_b.jpg&quot; title=&quot;New UI - Users&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7015/6787781597_6558d94bb5_t.jpg&quot; width=&quot;100&quot; height=&quot;60&quot; alt=&quot;New UI - Users&quot; style=&quot;margin-left: 10px&quot;&gt;&lt;/a&gt;
  
  &lt;a href=&quot;http://farm8.staticflickr.com/7010/6787781681_81b7977414_b.jpg&quot; title=&quot;New UI - Edit Profile&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7010/6787781681_81b7977414_t.jpg&quot; width=&quot;100&quot; height=&quot;82&quot; alt=&quot;New UI - Edit Profile&quot; style=&quot;margin-left: 10px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #666; text-align: center&quot;&gt;Check out the &lt;a style=&quot;color: #666&quot; href=&quot;http://www.flickr.com/photos/mraible/sets/72157629094630763/&quot;&gt;full set on Flickr&lt;/a&gt; if you&apos;d like a closer look.
  &lt;/p&gt;
  &lt;p&gt;
Even though I like the look of the old UI, I can&apos;t help but think a lot of the themes are designed for blogs and content sites, not webapps. The old &lt;a href=&quot;http://wufoo.com/&quot;&gt;Wufoo&lt;/a&gt; forms were a lot better looking though. And if you&apos;re going to develop &lt;a href=&quot;http://blogs.atlassian.com/2012/01/modern-principles-in-web-development/&quot;&gt;kick-ass webapps&lt;/a&gt;, you need to make them look good. Bootstrap goes a long way in doing this, but it certainly doesn&apos;t replace a good UX Designer. Bootstap simply helps you get into HTML5-land, start using CSS3 and it takes the pain out of making things work cross-browser. Its fluid layouts and responsive web design seems to work great for business applications, which I&apos;m guessing AppFuse is used for the most. 
&lt;/p&gt;
&lt;p&gt;
I can&apos;t thank the Bootstrap developers enough for helping me make this all look good. With Bootstrap 2 &lt;a href=&quot;http://www.markdotto.com/2012/01/24/bootstrap-2-ready-for-testing-and-feedback/&quot;&gt;dropping this week&lt;/a&gt;, I can see myself using this more and more on projects. In the near future, I&apos;ll be helping integrate Bootstrap into AppFuse&apos;s &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/Tapestry-5-3-2-td4339578.html&quot;&gt;Tapestry 5 and JSF versions&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;What do you think of this CSS change? Do you change your CSS and layout a fair bit when starting with AppFuse archetypes? What can we do to make AppFuse apps look better out-of-the-box?
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I &lt;a href=&quot;http://source.appfuse.org/changelog/appfuse/?cs=3593&quot;&gt;updated&lt;/a&gt; AppFuse to the final &lt;a href=&quot;http://raibledesigns.com/rd/entry/twitter_s_open_source_summit&quot;&gt;Bootstrap 2.0 release&lt;/a&gt;. Also, Johannes Geppert wrote a &lt;a href=&quot;http://www.jgeppert.com/2012/02/new-struts2-bootstrap-plugin-released/&quot;&gt;Struts 2 Bootstrap Plugin&lt;/a&gt;. I hope to integrate this into AppFuse in the near future.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/what_have_i_been_working</id>
        <title type="html">What have I been working on at Taleo?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/what_have_i_been_working"/>
        <published>2011-12-09T12:57:36-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jquery" scheme="http://roller.apache.org/ns/tags/" />
        <category term="happiness" scheme="http://roller.apache.org/ns/tags/" />
        <category term="overstock" scheme="http://roller.apache.org/ns/tags/" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">2011 has been a year of great clients for me. I started working with &lt;a href=&quot;http://overstock.com&quot;&gt;O.co&lt;/a&gt; and very much enjoyed my time there, especially on &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_greatest_snow_on_earth&quot;&gt;powder days in Utah&lt;/a&gt;. The people were great, the contract was great (no end date), but the work was not my forte. I was on a project to &lt;em&gt;modularize&lt;/em&gt; the main shopping site&apos;s codebase, which involved mostly refactoring. By refactoring, I mean creating new Maven projects, modifying lots of pom.xml files and literally moving files from one directory to another. IntelliJ made this easy, the hard part was refactoring tests, moving from EasyMock to Mockito and splitting classes into interfaces and implementations where appropriate. As a developer who likes developing UIs and visually seeing my accomplishments, the project wasn&apos;t that exciting. However, I knew that it was strategically important to O.co, so I didn&apos;t complain much.
&lt;/p&gt;
&lt;p&gt;In mid-May, I received a &lt;a href=&quot;http://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt; message from the Director of Software Engineering at &lt;a href=&quot;http://www.taleo.com/&quot;&gt;Taleo&lt;/a&gt;. 
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
This is OB, I am the Director of Software Engineering at Taleo. We are the 2nd largest Software as a Service company. I am building a new specialist UI team that will take the product to the next level. I am looking for someone to lead this initiative. If you are interested to have a chat about it, please let me know.
&lt;/p&gt;
&lt;p&gt;At that time, I&apos;d never heard of Taleo and quickly recommended they not hire me.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
This probably isn&apos;t the best position for me. While I am a good leader, I&apos;m not willing to relocate from Denver. I&apos;ve found that leaders usually do best when face-to-face with their developers.
&lt;/p&gt;
&lt;p&gt;This conversation continued back-and-forth where I explained how I wasn&apos;t willing to go full-time and I didn&apos;t want to leave Overstock. In the end, OB was persistent and explained how the position would entail lots of UI work and wouldn&apos;t require me to travel much. Our negotiations trailed off in June and resumed in July after I returned &lt;a href=&quot;http://raibledesigns.com/rd/entry/4th_of_july_adventures_in&quot;&gt;from vacation in Montana&lt;/a&gt;. Shortly after, we met each other&apos;s expectations, agreed on a start date and I started working at Taleo in early September. 
&lt;/p&gt;
&lt;p&gt;When I started, there were three features they wanted to add to to &lt;a href=&quot;http://www.taleo.com/solutions/taleo-business-edition&quot;&gt;Taleo Business Edition&lt;/a&gt;: Profile Pictures, Talent Cards and Org Charts. They knew the schedule was tight (8 weeks), but I was confident I could make it happen. At first, I groaned at the fact that they were using Ant to build the project. Then I smiled when I learned they&apos;d standardized on IntelliJ and set things up so you could do everything from the IDE. After using Maven for many years, this setup has actually become refreshing and I rarely have to restart or long for something like JRebel. Of course, a &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_macbook_pro_and_imac&quot;&gt;new kick-ass laptop&lt;/a&gt; and &lt;a href=&quot;http://blogs.jetbrains.com/idea/2011/12/intellij-idea-11-is-out-get-ready-for-a-productivity-takeoff/&quot;&gt;awesome IDE&lt;/a&gt; make it so I rarely wait for anything to happen.
&lt;/p&gt;
&lt;p&gt;
To give you a taste of how I implemented each of these new features in 8 weeks, I&apos;ve broken them into sections below.
&lt;/p&gt;
&lt;p id=&quot;profile-pictures&quot;&gt;&lt;strong&gt;Profile Pictures&lt;/strong&gt;&lt;br/&gt;
Adding profile pictures was a pretty simple concept, one you see on my social networking sites today. I needed to give users the ability to upload a JPEG or PNG and crop it so it looked good. The uploading was a pretty straightforward process and I used a lot of internal APIs to grab the file from the request and save it to disk. The more difficult part was scaling the image to certain dimensions on upload (to save space) and allowing users to crop it after. 
&lt;/p&gt;
&lt;p&gt;Most of Taleo Business Edition (TBE) is written in good ol&apos; servlets and JSPs, with lots of scriptlets in their JSPs. When I saw the amount of HTML produced from Java, I laughed out loud and cringed. Soon after, I breathed a sigh of relief when I learned that any new features could be written using &lt;a href=&quot;http://freemarker.sourceforge.net/&quot;&gt;FreeMarker&lt;/a&gt; templates, which IntelliJ has excellent support for.
&lt;/p&gt;
&lt;p&gt;For image resizing on upload, I used Chris Campbell&apos;s &lt;a href=&quot;http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html&quot;&gt;Image.getScaledInstance() tutorial&lt;/a&gt;. For creating thumbnails, I used a combination of scaling, &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferedImage.html#getSubimage(int, int, int, int)&quot;&gt;getSubimage()&lt;/a&gt; and the &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/javax/imageio/package-summary.html&quot;&gt;Java Image I/O API&lt;/a&gt;. I made sure to &lt;a href=&quot;http://stackoverflow.com/questions/3204280/is-imageio-write-buffered&quot;&gt;write to BufferedOutputStream&lt;/a&gt; for scalability. For cropping images client-side, I used &lt;a href=&quot;http://jqueryui.com/demos/dialog/&quot;&gt;jQuery UI&apos;s Dialog&lt;/a&gt; and &lt;a href=&quot;http://deepliquid.com/content/Jcrop.html&quot;&gt;Jcrop&lt;/a&gt;, the jQuery image cropping plugin. Below is a screenshot of what the cropping UI looks like:
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7142/6475456591_6831fd119e_b.jpg&quot; title=&quot;Taleo&apos;s TBE: Profile Picture&quot; rel=&quot;lightbox[taleo]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7142/6475456591_6831fd119e.jpg&quot; width=&quot;500&quot; height=&quot;176&quot; alt=&quot;Taleo&apos;s TBE: Profile Picture&quot;&gt;&lt;/a&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p id=&quot;talent-cards&quot;&gt;&lt;strong&gt;Talent Cards&lt;/strong&gt;&lt;br/&gt;
Talent Cards were a whole different beast. Not only did they need to display profile pictures, they also needed to contain contact information, work history and a number of other data points. Not just for employees, but for candidates as well. 
They also needed to be rendered with tabs at the bottom that allowed you to navigate between different data sections. 
&lt;/p&gt;
&lt;p&gt;

&lt;a href=&quot;http://farm8.staticflickr.com/7032/6475456677_ae342be56e_o.png&quot; title=&quot;Taleo&apos;s TBE: Talent Card&quot; rel=&quot;lightbox[taleo]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7032/6475456677_a06514020d_m.jpg&quot; width=&quot;187&quot; height=&quot;240&quot; alt=&quot;Taleo&apos;s TBE: Talent Card&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;&gt;&lt;/a&gt;

I&apos;ll admit, most of the hard work for this feature was done by the server-side developers (Harish and &lt;a href=&quot;http://www.linkedin.com/in/vladimirbazarsky&quot;&gt;Vlad&lt;/a&gt;) on my team. Vlad built the tabbed interface and Harish built the administrative section that allows you to add/remove/sort fields, as well as show and hide certain tabs. I performed most of my magic with jQuery, its &lt;a href=&quot;http://plugins.learningjquery.com/cluetip/&quot;&gt;clueTip plugin&lt;/a&gt; and good ol&apos; CSS. I was particularly thankful for CSS3 and its border-radius, box-shadow and Justin Maxwell&apos;s tutorial on &lt;a href=&quot;http://mattsnider.com/css/css-string-truncation-with-ellipsis/&quot;&gt;CSS String Truncation with Ellipsis&lt;/a&gt;. I used &lt;a href=&quot;http://directwebremoting.org/dwr/index.html&quot;&gt;DWR&lt;/a&gt; to fetch all the data from the server using Ajax. 
&lt;/p&gt;
&lt;p&gt;Talent Cards are a slick feature in TBE 11.5 and I think they&apos;re a great way to see a lot of information about someone very quickly. If you enable them for your company, you&apos;ll be able to mouse over any employee or candidate&apos;s names and see their information.
&lt;/p&gt;
&lt;p id=&quot;org-chart&quot;&gt;&lt;strong&gt;Org Chart&lt;/strong&gt;&lt;br/&gt;
The last feature I completed in this 8-week sprint was creating an organization chart. For this, I was given a rough prototype based on &lt;a href=&quot;http://www.capricasoftware.co.uk/corp/index.php&quot;&gt;Caprica Software&apos;s&lt;/a&gt; &lt;a href=&quot;http://www.capricasoftware.co.uk/jquery/orgchart/orgchart.html&quot;&gt;JQuery/CSS Organisation Chart&lt;/a&gt;. When I received it, it had all kinds of cool CSS 3 transformations (like &lt;a href=&quot;http://css3.bradshawenterprises.com/flip/&quot;&gt;this one&lt;/a&gt;), but they only worked in Safari and Chrome. I ended up removing the transformations and adding the ability to navigate up and down the org tree with Ajax (we currently only show three levels at a time). 
&lt;/p&gt;
&lt;p&gt;
The Org Chart feature also allows you to see how many direct/indirect reports an employee has, as well as access their Talent Card by hovering over their name. It&apos;s one of my favorite features because it&apos;s so visual and because it builds upon all the other features we&apos;ve built.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://farm8.staticflickr.com/7148/6483181659_619285347d_o.png&quot; rel=&quot;lightbox[taleo]&quot; title=&quot;Taleo&apos;s TBE: Org Chart&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7148/6483181659_7434afaeee.jpg&quot; width=&quot;487&quot; height=&quot;500&quot; alt=&quot;Taleo&apos;s TBE: Org Chart&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
As you might&apos;ve guessed by now, I&apos;ve been having a lot of fun doing UI development over the last few months. While I seem to have a knack for backend Java development, I enjoy developing UIs a lot more. The smile you see on people&apos;s faces during demos is priceless. I can&apos;t help but think this kind of thing contributes greatly to my developer happiness. All these features will be in next week&apos;s release of TBE and I couldn&apos;t be happier.
&lt;/p&gt;
&lt;p&gt;If you&apos;d like to work on my team at Taleo (or even take over my current role as UI Architect), please &lt;a href=&quot;http://raibledesigns.com/contact.jsp&quot;&gt;drop me a line&lt;/a&gt;. If you live near their headquarters (Dublin, CA), it&apos;d also be great to see you at the next Silicon Valley Spring User Group meetup. I&apos;ll be speaking about &lt;a href=&quot;http://www.meetup.com/SV-SUG/events/43376082/&quot;&gt;What&apos;s New in Spring 3.1 on February 1st&lt;/a&gt;.
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/phonegap_for_hybrid_app_development</id>
        <title type="html">PhoneGap for Hybrid App Development</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/phonegap_for_hybrid_app_development"/>
        <published>2011-11-16T10:22:16-07:00</published>
        <updated>2016-11-28T18:17:50-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="devoxx" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ios" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="phonegap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="android" scheme="http://roller.apache.org/ns/tags/" />
        <category term="adobe" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This afternoon, I attended &lt;a href=&quot;http://brian.io&quot; title=&quot;Brian LeRoux&quot;&gt;Brian LeRoux&lt;/a&gt;&apos;s talk on &lt;a href=&quot;http://www.devoxx.com/display/DV11/PhoneGap+for+Hybrid+App+Development&quot;&gt;PhoneGap for Hybrid App Development&lt;/a&gt; at Devoxx. You might remember that I &lt;a href=&quot;http://raibledesigns.com/rd/entry/phonegap_to_the_rescue&quot;&gt;tried PhoneGap last week&lt;/a&gt; and really enjoyed my experience. Below are my notes from Brian&apos;s talk.
&lt;/p&gt;
&lt;p&gt;
PhoneGap is a project for creating native applications using HTML, CSS and JavaScript. PhoneGap started out as a hack. In 2007, Apple shipped the iPhone and Steve Jobs told everyone they should develop webapps. PhoneGap started in 2008 as a lofty summertime hack and gained traction as a concept at Nitobi with Android and Blackberry implementations in the fall. In 2009, people started to pay attention when PhoneGap got rejected by Apple. They added Symbian and webOS support and Sony Ericsson started contributing to the project. They got rejected because all PhoneGap-developed apps were named &quot;PhoneGap&quot;. This turned out to be good press for the project and Apple let them in shortly after.
&lt;/p&gt;
&lt;p&gt;
In 2010, IBM began tag-teaming with Nitobi and added 5 developers to the project after meeting them at OSCON. In 2011, RIM started contributing as well as Microsoft. Then Adobe bought the company, so they&apos;re obviously contributing.
&lt;/p&gt;
&lt;p&gt;PhoneGaps Goals: the web is a first class platform, so let people create installable web apps. Their second goal is to cease to exist and get browsers to adopt their model.
&lt;/p&gt;
&lt;p&gt;
PhoneGap is NOT a runtime or a compiler/transpiler. It&apos;s not an IDE or predefined framework or proprietary lockin. It&apos;s Apache, MIT and BSD licensed to guarantee it&apos;s as free as free software gets. You can do whatever you want to do with it. PhoneGap has &lt;a href=&quot;http://arstechnica.com/open-source/news/2011/10/phonegap-to-become-an-apache-project-as-adobe-acquires-nitobi.ars&quot;&gt;recently been contributed to the Apache Software Foundation&lt;/a&gt;.
&lt;/p&gt;
As far as Adobe vs. PhoneGap is concerned, the Nitobi team remains contributors to PhoneGap. Adobe is a software tools company and has Apache and WebKit contributors. PhoneGap/Build integration will be added to &lt;a href=&quot;http://www.adobe.com/products/creativecloud.html&quot;&gt;Creative Cloud&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
The biggest issues with contributing PhoneGap to Apache is renaming the project and source control. I&apos;m not sure why it needs to be renamed, but it&apos;s likely that &lt;em&gt;Apache Callback&lt;/em&gt; is out. There seems to be some consensus on Apache Cordova. Apache likes SVN and the PhoneGap community currently uses Git. They&apos;re trying to find a medium road there, but would prefer to stay on Git.
&lt;/p&gt;
&lt;p&gt;
The PhoneGap technique is colloquially called &quot;the bridge&quot;. It&apos;s a 3 step process: they instantiate a WebView, then they call JavaScript from native code, then they call native code from JavaScript. Apparently, all device APIs are available via JavaScript in a WebView.
&lt;/p&gt;
&lt;p&gt;The primary platforms supported are iOS &gt;= 3, Android &gt;= 1.5 and BlackBerry &gt;= 5.x. They also support webOS, Symbian, Samsung Bada and Windows Phone. No mobile dev platform supports as many deploy targets as PhoneGap. Primary contributors are Adobe, IBM, RIM and Microsoft. 
&lt;/p&gt;
&lt;p&gt;Documentation for PhoneGap is available at &lt;a href=&quot;http://docs.phonegap.com&quot;&gt;http://docs.phonegap.com&lt;/a&gt;. Device APIs for PhoneGap 1.0 included sensors, data and outputs, which all devices have. Examples of sensors are geolocation and camera. Data examples are the filesystem, contacts and media. Outputs are screens, speakers and the speaker jack. All PhoneGap APIs are plugins, but any native API is permitted. 
&lt;/p&gt;
&lt;p&gt;
What about security? Brian recommends looking at the &lt;a href=&quot;http://html5sec.org&quot;&gt;HTML5 Security Cheatsheet&lt;/a&gt;. PhoneGap has added a lot of security measures since they&apos;ve found the native API pretty much opens up everything. 
&lt;/p&gt;
&lt;p&gt;
PhoneGap doesn&apos;t bundle a UI framework, but they support any JavaScript framework that works in the browser. PhoneGap is just a fancy browser, so your code run in less fancy web browsers too. This means you can develop and test your app in your desktop browser and only use PhoneGap to package and distribute your app.
&lt;/p&gt;
&lt;p&gt;Competition? PhoneGap has no competition. 
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;https://build.phonegap.com/&quot;&gt;PhoneGap/Build&lt;/a&gt; is for compiling your apps in the cloud and free for open source projects. The biggest reason they did this is because they couldn&apos;t redistribute all the SDKs and it was a pain for developers to download and install SDKs in training classes.
&lt;/p&gt;
&lt;p&gt;
For mobile app development, you should have a singular goal. Do one thing really well if you want to be successful. Great UX happens iteratively. You know that the web works and has been widely successfully cross-platform. It&apos;s likely you&apos;ve already invested in the web. Start by building a mobile web client and use PhoneGap as a progressive enhancement technique. 
&lt;/p&gt;
&lt;p&gt;
Shipping and unit testing should be a daily activity. Automate everything so you can have one-click builds (test/dev/release). For web client design, constraints are your ally in the battle against complexity and &quot;clients who are not &lt;em&gt;chill&lt;/em&gt;&quot;. Phones suck and consume a lot: cpu, ram, bandwidth, battery, network... everything! Start with a benchmark of app performance and monitor that benchmark. If you have tons and tons of features, consider splitting into multiple apps.
&lt;/p&gt;
The mobile web is not WebKit! Opera is huge, Firefox is making strides and IE still happens. For layouts: use flex-box rules (anyone got a link to these?), css media queries and meta tags for viewport. You should try to develop your app without frameworks because they come with a ton of code and can effect the size of your app.
&lt;/p&gt;
&lt;p&gt;
Looks can kill: aesthetics that can hurt performance: border-radius, box-shadow and gradients can slow down your apps. Chances are, you really don&apos;t need these features. Design your app for your brand, not for the device manufacturer. An app that looks like an iPhone app on Android doesn&apos;t give a positive impression.&lt;/p&gt;
&lt;p&gt;
For JavaScript libraries, start with your problem, not a generic solution like Sencha or jQuery Mobile. &lt;a href=&quot;http://zeptojs.com/&quot;&gt;Zepto&lt;/a&gt; and its older brother &lt;a href=&quot;http://xuijs.com/&quot;&gt;XUI&lt;/a&gt; are all you need to start. &lt;a href=&quot;http://joapp.com/&quot;&gt;Jo&lt;/a&gt; is a fantastic option. &lt;a href=&quot;http://backbonejs.org/&quot;&gt;Backbone&lt;/a&gt; and &lt;a href=&quot;http://spine.github.io/&quot;&gt;Spine&lt;/a&gt; are worth watching.
&lt;/p&gt;
&lt;p&gt;
For testing, &lt;a href=&quot;http://docs.jquery.com/QUnit&quot;&gt;QUnit&lt;/a&gt; and &lt;a href=&quot;http://pivotal.github.com/jasmine/&quot;&gt;Jasmine&lt;/a&gt; are pretty popular. For deployment, concat, minify and obfuscate your JavaScript and CSS. Or you can inline everything into the markup to minimize HTTP chatter. Gmail inlines and comments all their JavaScript and then evals it.
&lt;/p&gt;
&lt;p&gt;From there, Brian recommended leveraging HTML5&apos;s &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/appcache/beginner/&quot;&gt;AppCache&lt;/a&gt; and and using RESTful JSON endpoints for legacy systems. Next, he tried to show us a demo of a photo sharing application. Unfortunately, the Demo Gods were grumpy and Brian couldn&apos;t get his computer to recognize his Android phone. He did show us the &lt;a href=&quot;http://gist.github.com/1219277&quot;&gt;client code&lt;/a&gt; and it&apos;s pretty impressive you can use 1 line of code to take a picture on a phone.
&lt;/p&gt;
&lt;p&gt;The last thing we looked at was &lt;a href=&quot;http://debug.phonegap.com&quot;&gt;debug.phonegap.com&lt;/a&gt;. This is an app that&apos;s powered by &lt;a href=&quot;http://phonegap.github.com/weinre/&quot;&gt;weinre&lt;/a&gt;. It lets you enter a line of JavaScript in your client and then remotely debug it in a tool that looks like Chrome&apos;s Web Inspector. Very cool stuff if you ask me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
I really enjoyed learning more about PhoneGap, particularly because Brain emphasized all my web development skills can be used. I don&apos;t have to learn Objective-C or Android to develop native apps and I don&apos;t even have to install an SDK if I use PhoneGap/Build. Of course, my mobile developer friends might disagree with this approach. In the meantime, I look forward to using PhoneGap to turn my mobile web clients into native apps and finding out if it&apos;s really as good as they say it is.
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/play_2_0_a_web</id>
        <title type="html">Play 2.0, A web framework for a new era</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/play_2_0_a_web"/>
        <published>2011-11-16T05:58:09-07:00</published>
        <updated>2011-11-16T12:07:33-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="akka" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This week, I&apos;m in Antwerp, Belgium for the annual &lt;a href=&quot;http://devoxx.com&quot;&gt;Devoxx&lt;/a&gt; conference. After traveling 21 hours door-to-door yesterday, I woke up and came to the conference to attend some talks on Play and PhoneGap. I just got out of the &lt;a href=&quot;http://devoxx.com/display/DV11/Play+2.0%2C+a+Web+framework+for+a+new+era&quot;&gt;session on Play 2.0&lt;/a&gt;, which was presented by &lt;a href=&quot;http://devoxx.com/display/DV11/Sadek+Drobi&quot;&gt;Sadek Drobi&lt;/a&gt; and &lt;a href=&quot;http://devoxx.com/display/DV11/Guillaume+Bort&quot;&gt;Guillaume Bort&lt;/a&gt;. Below are my notes from this presentation.&lt;/p&gt;
&lt;p&gt;The Play 2.0 beta is out! You can read more about this release &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/6d5783e28efb6931&quot;&gt;on the mailing list&lt;/a&gt;. This beta includes native support for both Scala and Java, meaning you can use both in the same project. The release also bundles &lt;a href=&quot;http://akka.io/&quot;&gt;Akka&lt;/a&gt; and &lt;a href=&quot;https://github.com/harrah/xsbt/wiki&quot;&gt;SBT&lt;/a&gt; by default.
&lt;/p&gt;
&lt;p&gt;In other news, &lt;a href=&quot;http://blog.typesafe.com/typesafe-stack-adds-play-framework&quot;&gt;Play 2.0 is now part of the Typesafe Stack&lt;/a&gt;. Typesafe is the Scala company, started by the founder of Scala (&lt;a href=&quot;http://ch.linkedin.com/in/odersky&quot;&gt;Martin Odersky&lt;/a&gt;) and the founder of Akka (&lt;a href=&quot;http://se.linkedin.com/in/jonasboner&quot;&gt;Jonas Bon&#233;r&lt;/a&gt;). Guillaume is also joining the Typesafe Advisory Board.
&lt;/p&gt;
&lt;p&gt;Sadek and Guillaume both work at &lt;a href=&quot;http://www.zenexity.com/&quot;&gt;zenexity&lt;/a&gt;, where Play is the secret weapon for the web applications they&apos;ve built for the last decade. Play was born in the real world. They kept listening to the market to see what they should add to the project. At some point, they realized they couldn&apos;t keep adding to the old model and they needed to create something new.&lt;/p&gt;
&lt;p&gt;
The web has evolved from static pages to dynamic pages (ASP, PHP). From there, we moved to structured web applications with frameworks and MVC. Then the web moved to Ajax and long-polling to more real-time, live features.  And this changes everything.
&lt;/p&gt;
&lt;p&gt;
Now we need to adapt our tools. We need to handle tremendous flows of data. Need to improve expressiveness for concurrent code. We need to pick the appropriate datastore for the problem (not only SQL). We need to integrate with rapidly-evolving client side technologies like JavaScript, CoffeeScript, and Dart. We need to use elastic deployment that allows scaling up and scaling down.&lt;/p&gt;
&lt;p&gt;zenexity wanted to integrated all of these modern web needs into Play 2.0. But they also wanted to keep Play approachable. They wanted to maintain fast turnaround so you can change your code and hit reload to see the changes. They wanted to keep it as a full stack framework with support for JSON, XML, Web Services, Jobs, etc. And they wanted to continue to use and conventions over configuration.
&lt;/p&gt;
&lt;p&gt;At this point, Guillaume did a Play 2.0 Beta demo, show us how it uses SBT and has a console so everything so it runs really fast. You can have both Scala and Java files in the same project. Play 2.0 templates are based on Scala, but you don&apos;t need to know Scala to use them. You might have to learn how to write a for loop in Scala, but it&apos;s just a subset of Scala for templates and views. SBT is used for the build system, but you don&apos;t have to learn or know SBT. All the old &lt;code&gt;play&lt;/code&gt; commands still work, they&apos;re just powered by a different system.
&lt;/p&gt;
&lt;p&gt;After the demo, Sadek took over and started discussing the key features of Play 2.0.
&lt;/p&gt;
&lt;p&gt;
To handle tremendous amounts of data, you need to do chunking of data and be able to process a stream of data, not just wait until it&apos;s finished. Java&apos;s InputStream is outdated and too low level. Its &lt;em&gt;read()&lt;/em&gt; method reads the next byte of data from the input and this method can block until input data is available.&lt;/p&gt;
&lt;p&gt;
To solve this, Play includes a reactive programming feature, which they borrowed from Haskell. It uses Iteratee/Enumerator IO and leverages inversion of control (not like dependency injection, but more like not micro-managing). The feature allows you to have control when you need it so you don&apos;t have to wait for the input stream to complete. The Enumerator is the component that sends data and the Iteratee is the component that receives data. The Iteratee does incremental processing and can tell the Enumerator when it&apos;s done. The Iteratee can also send back a continuation, where it tells the Enumerator it wants more data and how to give it. With this paradigm, you can do a lot of cool stuff without consuming resources and blocking data flow.
&lt;/p&gt;
&lt;p&gt;
Akka is an actor system that is a great model for doing concurrent code. An Actor could be both an Enumerator and an Iteratee. This vastly improves the expressiveness for concurrent code. For example, here&apos;s how you&apos;d use Akka in Play:
&lt;pre class=&quot;brush: scala&quot;&gt;
def search(keyword: String) = Action {
  AsyncResult {
    // do something with result
  }
}
&lt;/pre&gt;
&lt;p&gt;
Play does not try to abstract data access because datastores are different now. You don&apos;t want to think of everything as objects if you&apos;re using something like MongoDB or navigating a Social Graph. Play 2.0 will provide some default modules for the different datastores, but they also expect a lot of contributed modules. Anorm will be the default SQL implementation for Play Scala and &lt;a href=&quot;https://github.com/playframework/Play20/wiki/JavaEbean&quot;&gt;Ebean&lt;/a&gt; will be the default ORM implementation for Play Java. The reason they&apos;ve moved away from Hibernate is because they needed something that was more stateless.
&lt;/p&gt;
&lt;p&gt;
On the client side, there&apos;s so many technologies (LESS, CoffeeScript, DART, Backbone.js, jQuery, SASS), they didn&apos;t want to bundle any because they move too fast. Instead, there&apos;s plugins you can add that help you leverage these technologies. There&apos;s a lot of richness you can take advantage of on the client side and you need to have the tools for that.&lt;/p&gt;
&lt;p&gt;Lastly, there&apos;s a new type of deployment: container-less deployment to the cloud. Akka allows you to distribute your jobs across many servers and &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt; is an implementation of elastic deployment that has built-in support for Play.
&lt;/p&gt;
&lt;p&gt;
They&apos;ve explained what they tried to design and the results of this new, clean architecture have been suprising. Side effects include: type-safety everywhere for rock-solid applications. There&apos;s an awesome performance boost from Scala. There&apos;s easier integration with existing projects via SBT. And it only takes 10 lines of code to develop an HTTP Server that responds to web requests.
&lt;p&gt;The memory consumption is amazing: only 2MB of heap is used when a Play 2.0 app is started. Tests on Guillaume&apos;s laptop have shown that it can handle up to 40,000 requests per second, without any optimization of the JVM. Not only that, but after the requests subside, garbage collection cleans up everything and reduces the memory consumption back to 2MB.
&lt;/p&gt;
&lt;p&gt;At this point, Guillaume did another demo, showing how everything is type-safe in 2.0, including the routes file. If you mistype (or comment one out) any routes, the compiler will find it and notify you. Play 2.0 also contains a &lt;em&gt;compiled assets&lt;/em&gt; feature. This allows you to &lt;a href=&quot;https://github.com/playframework/Play20/wiki/AssetsGoogleClosureCompiler&quot;&gt;use Google&apos;s Closure Compiler&lt;/a&gt;, &lt;a href=&quot;https://github.com/playframework/Play20/wiki/AssetsCoffeeScript&quot;&gt;CoffeeScript&lt;/a&gt; and &lt;a href=&quot;https://github.com/playframework/Play20/wiki/AssetsLess&quot;&gt;LESS&lt;/a&gt;. If you put your LESS files in &lt;em&gt;app/assets/stylesheets&lt;/em&gt;, compilation errors will show up in your browser. If you put JavaScript files in &lt;em&gt;app/assets/javascripts&lt;/em&gt;, the Closure compiler will be used and compilation errors will show up in your browser.
&lt;/p&gt;
&lt;p&gt;
Play 2.0 ships with 3 different sample applications, all implemented in both Java and Scala. HelloWorld is more than just text in a browser, it includes a form that shows how validation works. Another app is computer-database. When Guillaume started it, we saw how evolutions were used to create the database schema from the browser. The Play Team has done their best to make the development process a browser-based experience rather than having to look in your console. The computer-database is a nice example of how to do CRUD and leverages Twitter&apos;s Bootstrap for its look and feel.
&lt;/p&gt;
The last sample application is zentasks. It uses Ajax and implements security so you can see how to create a login form. It uses LESS for CSS and CoffeeScript and contains features like in-place editing. If you&apos;d like to see any of these applications in action, you can stop by the Typesafe booth this week at Devoxx.
&lt;/p&gt;
&lt;p&gt;Unfortunately, there will be no migrating path for Play 1.x applications. The API seems very similar, but there are subtle changes that make this difficult. The biggest thing is how templating has changed from Groovy to Scala. To migrate from 1.2.x would be mostly a copy/paste, modify process. There are folks working on getting Groovy templates working in 2.0. The good news is zenexity has hundreds of 1.x applications in production, so 1.x will likely be maintained for many years.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
This was a great talk on what&apos;s new in Play 2.0. I especially like the native support for LESS and CoffeeScript and the emphasis on trying to keep developers using two tools: their editor and the browser. The sample apps look great, but the documentation look sparse. I doubt I&apos;ll get a chance to migrate my Play 1.2.3 app to 2.0 this month, but I hope to try migrating sometime before the end of the year.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/phonegap_to_the_rescue</id>
        <title type="html">PhoneGap to the Rescue!</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/phonegap_to_the_rescue"/>
        <published>2011-11-14T16:32:19-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="ios" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="phonegap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apple" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This is the 7th article in a series about my adventures developing a web application with HTML5, Play Scala, CoffeeScript and Jade. Previous articles can be found at:
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;Integrating Scalate and Jade with Play 1.2.3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work&quot;&gt;Trying to make CoffeeScript work with Scalate and Play&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate&quot;&gt;Integrating HTML5 Boilerplate with Scalate and Play&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and&quot;&gt;Developing with HTML5, CoffeeScript and Twitter&apos;s Bootstrap&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/play_scala_s_anorm_heroku&quot;&gt;Play Scala&apos;s Anorm, Heroku and PostgreSQL Issues&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/more_scalate_goodness_for_play&quot;&gt;More Scalate Goodness for Play&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

A few weeks ago, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and&quot;&gt;Developing a Stopwatch and Trip Meter with HTML5&lt;/a&gt;. I mentioned I&apos;d run into a major issue when I discovered HTML5 Geo&apos;s &lt;span style=&quot;font-family: monaco; courier&quot;&gt;watchPosition()&lt;/span&gt; feature didn&apos;t run in the background. From that article:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
  I tried out the trip meter that evening
    on a bike ride and noticed it said I&apos;d traveled 3 miles when I&apos;d really gone 6. I quickly figured out it was only calculating
    start point to end point and not taking into account all the turns in between. To view what was happening, I integrated my
    odometer.coffee with my map using &lt;a href=&quot;http://code.google.com/apis/maps/documentation/javascript/overlays.html#Polylines&quot; style=&quot;color: #666&quot;&gt;
    Google Maps Polylines&lt;/a&gt;. Upon finishing the integration, I discovered two things, 1) HTML5 geolocation was highly inaccurate and
    2) &lt;a href=&quot;http://stackoverflow.com/questions/7676423/is-it-possible-to-make-an-html5-trip-meter-that-tracks-distance-traveled/7681295&quot; style=&quot;color: #666&quot;&gt;geolocation doesn&apos;t run in the background&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
At the time, I opted to ignore this issue and use my app by setting Auto-Lock to Never. This worked, but if I happened to bump my phone while exercising, the app would get closed. Not to mention it really drained the battery and seemed to crash every-so-often.
&lt;/p&gt;
&lt;p&gt;Last week, I realized things were getting down to the wire and I was running out of time to finish my app&apos;s functionality for my &lt;a href=&quot;http://www.devoxx.com/display/DV11/HTML5+with+Play+Scala%2C+CoffeeScript+and+Jade&quot;&gt;Devoxx Talk&lt;/a&gt;. On Wednesday afternoon, I asked my office-mates (2 iOS developers) about developing a native app and installing it on my phone. They told me I had to be a iOS Certified Developer ($99/year) or jailbreak my iPhone to get an app on it. On Thursday, I downloaded &lt;a href=&quot;http://phonegap.com&quot;&gt;PhoneGap&lt;/a&gt; and installed &lt;a href=&quot;http://developer.apple.com/xcode/&quot;&gt;Xcode 4&lt;/a&gt;. As you can tell from the following image, PhoneGap seemed to be exactly what I was looking for. 
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://phonegap.com&quot; title=&quot;PhoneGap&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6055/6345056529_d814790e75.jpg&quot; width=&quot;500&quot; height=&quot;232&quot; alt=&quot;PhoneGap&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I spent some time going through PhoneGap&apos;s &lt;a href=&quot;http://phonegap.com/start&quot;&gt;Getting Started Guide&lt;/a&gt; and was able to get my app rendering in a short amount of time. I was able to copy the webapp&apos;s generated HTML into my PhoneGap project&apos;s &lt;em&gt;www&lt;/em&gt; directory and launch the app in the iOS Simulator. The only change I had to make was to convert my inline CoffeeScript (that used coffee-script.js for in-browser compilation) to JavaScript. It&apos;s possible the in-browser compiler works, but when things didn&apos;t work and I didn&apos;t see any error messages, it was the first thing I changed.
&lt;/p&gt;
&lt;p&gt;  
For getting the app onto a phone for testing, I opted for the easiest route and applied for an iOS Developer Account on Thursday afternoon. While waiting for approval on Friday, I briefly tried to jailbreak my phone, but encountered all kinds of version issues and gave up after 30 minutes. 24 hours after I applied for my account, Apple emailed asking for a business document to prove I was legit. I was unable to download any from the &lt;a href=&quot;http://www.sos.state.co.us/&quot;&gt;Colorado Secretary of State&lt;/a&gt; that were &lt;em&gt;e-certified&lt;/em&gt;, so expeditiously switched my application from a business account to an individual one. Apple was great in helping me out before the weekend started and I had everything I needed as we drove to our &lt;a href=&quot;http://raibledesigns.com/rd/entry/another_dream_realized_mountain_views&quot;&gt;Ski Shack&lt;/a&gt; on Friday evening. 
&lt;/p&gt;
&lt;p&gt;On Friday night, I upgraded Trish&apos;s iPhone 4 to iOS 5 to make sure I had the latest and greatest platform for my app. Saturday morning, I woke up around 8 and started trying to figure out how to get my app on her phone. It took about an hour of fumbling, grumbling and searching to figure out how to do this. All the while, the troops were getting restless, noting that they were hungry for breakfast and groaning about Daddy taking so long. At 9:30, I finally got my PhoneGap app installed on Trish&apos;s phone and we drove to breakfast at &lt;a href=&quot;http://www.tripadvisor.com/Restaurant_Review-g33431-d382122-Reviews-Sharky_s-Fraser_Colorado.html&quot;&gt;Sharkey&apos;s&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;  
I started the app when we left and checked it 5 minutes later when we arrived for breakfast. The blood drained from my face when I saw the app had drawn a straight line from our condo to the breakfast joint. We had taken several turns along the way and my new native app ignored them all. I assumed I had failed and my talk&apos;s conclusion at Devoxx would be &quot;You can&apos;t develop a reliable Fitness Tracker app with HTML5&quot;. However, after a delicious Eggs Benedict, I became determined to succeed and returned home for some more hacking.
&lt;/p&gt;
&lt;p&gt;This is when I discovered Joel Dare&apos;s &lt;a href=&quot;http://www.joeldare.com/wiki/play_an_mp3_audio_stream_in_phonegap&quot;&gt;Play an MP3 Audio Stream in PhoneGap&lt;/a&gt; tutorial and the &quot;Required background modes&quot; key. This was the knowledge that saved the day and I anxiously added the key and the values to get location updates and allow audio to run in the background. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm7.static.flickr.com/6040/6345880496_62808c5075_b.jpg&quot; title=&quot;Required Background Modes&quot; rel=&quot;lightbox[phonegap]&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6040/6345880496_62808c5075.jpg&quot; width=&quot;500&quot; height=&quot;258&quot; alt=&quot;Required Background Modes&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Saturday afternoon, I strapped on my recently purchased &lt;a href=&quot;http://gopro.com/&quot;&gt;GoPro Camera&lt;/a&gt;, shot some video for my demo at Devoxx and had a blast skiing with Abbie, Jack and Trish. I celebrated that evening with an executive workout (15 minutes each in hot tub, sauna and steam room) at the &lt;a href=&quot;http://www.fraservalleyrec.org/&quot;&gt;Fraser Rec Center&lt;/a&gt; and again on Sunday when the Broncos whooped the Kansas City Chiefs &lt;a href=&quot;http://raibledesigns.com/rd/entry/an_awesome_trip_to_amsterdam&quot;&gt;just like last year&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;Last night, I stayed up late to put the finishing touches on my Devoxx presentation. Now I&apos;m sitting at the Red Carpet Club in Chicago&apos;s O&apos;Hare getting ready to depart for Belgium. It&apos;s been a fun journey learning about HTML5, Scala, Play, CoffeeScript and Jade. If you&apos;re at Devoxx this week, I think I&apos;ve got a presentation you&apos;re really going to like. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;-)&quot; title=&quot;;-)&quot; /&gt;
  </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/more_scalate_goodness_for_play</id>
        <title type="html">More Scalate Goodness for Play</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/more_scalate_goodness_for_play"/>
        <published>2011-11-07T14:07:40-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jade" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scalate" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://scalate.fusesource.org&quot;&gt;&lt;img src=&quot;//scalate.fusesource.org/images/project-icon-160x160.png&quot; width=&quot;80&quot; height=&quot;80&quot; alt=&quot;Scalate&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;/&gt;&lt;/a&gt;
This article is the 6th in a series on about my adventures developing a web application with HTML5, Play Scala, CoffeeScript and Jade. Previous articles can be found at:
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;Integrating Scalate and Jade with Play 1.2.3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work&quot;&gt;Trying to make CoffeeScript work with Scalate and Play&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate&quot;&gt;Integrating HTML5 Boilerplate with Scalate and Play&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and&quot;&gt;Developing with HTML5, CoffeeScript and Twitter&apos;s Bootstrap&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/play_scala_s_anorm_heroku&quot;&gt;Play Scala&apos;s Anorm, Heroku and PostgreSQL Issues&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Last week, I wrote about my adventures with &lt;a href=&quot;http://scala.playframework.org/documentation/scala-0.9.1/anorm&quot;&gt;Anorm&lt;/a&gt; and mentioned I&apos;d made some improvements to Scalate Play interoperability. First of all, I&apos;ve been using a Scalate trait and ScalateTemplate class to render Jade templates in my application. I described this setup in my &lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;first article on Scalate and Play&lt;/a&gt;. 
&lt;/p&gt;
&lt;p id=&quot;default-variables&quot;&gt;&lt;strong&gt;Adding SiteMesh Features and Default Variables&lt;/strong&gt;&lt;br/&gt;  
When I started making my app look good with CSS, I started longing for a feature I&apos;ve used in SiteMesh. That is, to have a body id or class that can identify the page and allow per-page CSS rules. To do this with SiteMesh, you&apos;d have something like the following in your page:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;  
&amp;lt;body id=&quot;signup&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;  
And then read it in your decorator:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;body&lt;decorator:getProperty property=&quot;body.id&quot; writeEntireProperty=&quot;true&quot;/&gt;&amp;lt;decorator:getProperty property=&quot;body.class&quot; writeEntireProperty=&quot;true&quot;/&gt;&gt;
&lt;/pre&gt;
&lt;p&gt;As I started looking into how to do this, I came across Play Scala&apos;s &lt;a href=&quot;https://github.com/playframework/play-scala/blob/master/src/play/mvc/ScalaController.scala&quot;&gt;ScalaController&lt;/a&gt; and how it was populating Play&apos;s default variables (request, response, flash, params, etc.). Based on this newfound knowledge, I added a &lt;em&gt;populateRenderArgs()&lt;/em&gt; method to set all the default variables and my desired &lt;em&gt;bodyClass&lt;/em&gt; variable.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
def populateRenderArgs(args: (Symbol, Any)*): Map[String, Any] = {
  val renderArgs = Scope.RenderArgs.current();

  args.foreach {
    o =&gt;
      renderArgs.put(o._1.name, o._2)
  }

  renderArgs.put(&quot;session&quot;, Scope.Session.current())
  renderArgs.put(&quot;request&quot;, Http.Request.current())
  renderArgs.put(&quot;flash&quot;, Scope.Flash.current())
  renderArgs.put(&quot;params&quot;, Scope.Params.current())
  renderArgs.put(&quot;errors&quot;, validationErrors)
  renderArgs.put(&quot;config&quot;, Play.configuration)

  // CSS class to add to body
  renderArgs.put(&quot;bodyClass&quot;, Http.Request.current().action.replace(&quot;.&quot;, &quot; &quot;).toLowerCase)
  renderArgs.data.toMap
}

implicit def validationErrors:Map[String,play.data.validation.Error] = {
  import scala.collection.JavaConverters._
  Map.empty[String,play.data.validation.Error] ++ 
    Validation.errors.asScala.map( e =&gt; (e.getKey, e) )
}
&lt;/pre&gt;&lt;p&gt;After adding this method, I was able to access these values in my templates by defining them at the top:
&lt;/pre&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-@ val bodyClass: String 
-@ val params: play.mvc.Scope.Params
-@ val flash: play.mvc.Scope.Flash
&lt;/pre&gt;
&lt;p&gt;And then reading their values in my template:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
body(class=bodyClass)
...
- if (flash.get(&quot;success&quot;) != null) {
  div(class=&quot;alert-message success&quot; data-alert=&quot;alert&quot;)
    a(class=&quot;close&quot; href=&quot;#&quot;) &amp;&amp;times;
    | #{flash.get(&quot;success&quot;)}
- }
...
  fieldset
    legend Leave a comment &amp;rarr;
    div.clearfix
      label(for=&quot;author&quot;) Your name:
      input(type=&quot;text&quot; name=&quot;author&quot; class=&quot;xlarge&quot; value={params.get(&quot;author&quot;)})
    div.clearfix
      label(for=&quot;content&quot;) Your message:
      textarea(name=&quot;content&quot; class=&quot;xlarge&quot;) #{params.get(&quot;content&quot;)}
    div.actions
      button(type=&quot;submit&quot; class=&quot;btn primary&quot;) Submit your comment
      button(type=&quot;reset&quot; class=&quot;btn&quot;) Cancel
&lt;/pre&gt;  
&lt;p&gt;For a request like Home/index, the body tag is now rendered as:
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;body class=&quot;home index&quot;&gt;
&lt;/pre&gt;
This allows you to group CSS styles by Controller names as well as by method names.
&lt;/p&gt;
&lt;p&gt;
Next, I started developing forms and validation logic. I quickly discovered I needed an &lt;em&gt;action()&lt;/em&gt; method like the one defined in &lt;a href=&quot;https://github.com/playframework/play-scala/blob/master/src/play/templates/ScalaTemplate.scala&quot;&gt;ScalaTemplate&apos;s&lt;/a&gt; TemplateMagic class. 
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
def action(action: =&gt; Any) = {
  new play.mvc.results.ScalaAction(action).actionDefinition.url
}
&lt;/pre&gt;
&lt;p&gt;
Since TemplateMagic is an inner class, I determined that copying the method into my ScalateTemplate class was the easiest workaround. After doing this, I was able to import the method and use it in my templates.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-import controllers.ScalateTemplate._
...
form(method=&quot;post&quot; class=&quot;form-stacked&quot; id=&quot;commentForm&quot;
     action={action(controllers.Profile.postComment(workout._1.id()))})
&lt;/pre&gt;
&lt;p&gt;After getting the proper URL written into my form&apos;s action attribute, I encountered a new problem. The &lt;a href=&quot;http://scala.playframework.org/documentation/scala-0.9.1/guide4#aAddingvalidationa&quot;&gt;Play Scala Tutorial explains validation flow&lt;/a&gt; as follows:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
if (Validation.hasErrors) {
  show(postId)
} else {
  Comment.create(Comment(postId, author, content))
  Action(show(postId))
}
&lt;/pre&gt;
&lt;p&gt;However, when I had validation errors, I end up with the following error:
&lt;/p&gt;
&lt;pre&gt;
Could not load resource: [Timeline/postComment.jade]
&lt;/pre&gt;&lt;p&gt;To fix this, I added logic to my Scalate trait that looks for a &quot;template&quot; variable before using &lt;em&gt;Http.Request.current().action.replace(&quot;.&quot;, &quot;/&quot;)&lt;/em&gt; for the name. After making this change, I was able to use the following code to display validation errors.&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
if (Validation.hasErrors) {
  renderArgs.put(&quot;template&quot;, &quot;Timeline/show&quot;)
  show(postId)
} else {
  Comment.create(Comment(postId, author, content))
  Action(show(postId))
}
&lt;/pre&gt;
&lt;p&gt;Next, I wanted to give child pages the ability to set content in parent pages. With SiteMesh, I could use the &amp;lt;content&amp;gt; tag as follows:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;content tag=&quot;underground&quot;&gt;
  HTML goes here
&amp;lt;/content&gt;
&lt;/pre&gt;
&lt;p&gt;This HTML could then be retrieved in the decorator using the &amp;lt;decorator:getProperty&amp;gt; tag:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;decorator:getProperty property=&quot;page.underground&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;With Scalate, I found it equally easy using the &lt;em&gt;captureAttribute()&lt;/em&gt; method. For example, here&apos;s how I captured a list of an athlete&apos;s workouts for display in a sidebar.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
- captureAttribute(&quot;sidebar&quot;)
  - Option(older).filterNot(_.isEmpty).map { workouts =&gt;
    .older-workouts
      h3
        | Older workouts
        span.from from this app
      - workouts.map { workout =&gt;
        - render(&quot;workout.jade&quot;, Map(&apos;workout -&gt; workout, &apos;mode -&gt; &quot;teaser&quot;))
      - }
  - }
- }
&lt;/pre&gt;
&lt;p&gt;Then in my layout, I was able to retrieve this and display it. Below is a snippet from the layout I&apos;m using (copied from &lt;a href=&quot;http://twitter.github.com/bootstrap/examples/container-app.html&quot;&gt;Twitter&apos;s Bootstrap example&lt;/a&gt;). You can see how the sidebar is included in the .span4 at the end.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-@ val sidebar: String = &quot;&quot;
...
.container
  .content
    .page-header
      h1
        = pageHeader
        small
          = pageTagline
    .row
      .span10
        !~~ body
      .span4
        = unescape(sidebar)
  footer
&lt;/pre&gt;
&lt;p id=&quot;view-vs-render&quot;&gt;&lt;strong&gt;View vs. Render in Scalate&lt;/strong&gt;&lt;br/&gt;
In the sidebar code above, you might notice the &lt;em&gt;render()&lt;/em&gt; call. This is the &lt;a href=&quot;http://scalate.fusesource.org/documentation/user-guide.html#Render_templates&quot;&gt;Scalate version of server-side includes&lt;/a&gt;. It works well, but there&apos;s also a &lt;a href=&quot;http://scalate.fusesource.org/documentation/user-guide.html#Views&quot;&gt;&lt;em&gt;view()&lt;/em&gt; shortcut&lt;/a&gt; you can use if you want to have templates for rendering your model objects. I quickly discovered it might be difficult to use this feature in my app because my object was &lt;em&gt;Option[(models.Workout, models.Athlete, Seq[models.Comment])]&lt;/em&gt; instead of a simple object. You can read the &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/bcc6059fc08d4da0&quot;&gt;view vs. render thread&lt;/a&gt; on the Scalate Google Group if you&apos;re interested in learning more.
&lt;/p&gt;
&lt;p id=&quot;scalate-plugin&quot;&gt;&lt;strong&gt;Scalate as a Module&lt;/strong&gt;&lt;br/&gt;
The last enhancement I attempted to make was to put Scalate support into a &lt;a href=&quot;http://www.playframework.org/documentation/1.2.3/modules&quot;&gt;Play module&lt;/a&gt;. At first, I tried overriding Play&apos;s &lt;em&gt;Template&lt;/em&gt; class but &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/8d1b00ec4304f4ea&quot;&gt;ran into compilation issues&lt;/a&gt;. Then Guillaume Bort (Play&apos;s lead developer) recommended I stick with the trait approach and I was able to get everything working. I looked at the &lt;a href=&quot;https://github.com/pk11/play-scalate&quot;&gt;outdated play-scalate module&lt;/a&gt; to figure out how to add Scala support to build.xml and copied its &lt;a href=&quot;https://github.com/pk11/play-scalate/blob/master/resources/500.scaml&quot;&gt;500.scaml&lt;/a&gt; page for error reporting.&lt;/p&gt;
&lt;p&gt;In order to get line-precise error reporting working, I had to wrap a try/catch around calling Scalate&apos;s &lt;em&gt;TemplateEngine.layout()&lt;/em&gt; method. Again, most of this code was copied from the outdated play-scalate module.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
case class Template(name: String) {
  
  def render(args: (Symbol, Any)*) = {
    val argsMap = populateRenderArgs(args: _*)
    
    val buffer = new StringWriter()
    var context = new DefaultRenderContext(name, scalateEngine, new PrintWriter(buffer))
    
    try {
      val templatePath = new File(Play.applicationPath+&quot;/app/views&quot;,&quot;/&quot;+name).toString
        .replace(new File(Play.applicationPath+&quot;/app/views&quot;).toString,&quot;&quot;)
      scalateEngine.layout(templatePath + scalateType, argsMap)
    } catch {
      case ex:TemplateNotFoundException =&gt; {
        if(ex.isSourceAvailable) {
          throw ex
        }
        val element = PlayException.getInterestingStrackTraceElement(ex)
        if (element != null) {
           throw new TemplateNotFoundException(name, 
             Play.classes.getApplicationClass(element.getClassName()), element.getLineNumber());
        } else {
           throw ex
        }
      }  
      case ex:InvalidSyntaxException =&gt; handleSpecialError(context,ex)
      case ex:CompilerException =&gt; handleSpecialError(context,ex)
      case ex:Exception =&gt; handleSpecialError(context,ex)
    } finally {
      if (buffer.toString.length &gt; 0)
        throw new ScalateResult(buffer.toString,name)
    }
  }
}
...
private def handleSpecialError(context:DefaultRenderContext,ex:Exception) {
  context.attributes(&quot;javax.servlet.error.exception&quot;) = ex
  context.attributes(&quot;javax.servlet.error.message&quot;) = ex.getMessage
  try {
    scalateEngine.layout(scalateEngine.load(errorTemplate), context)
  } catch {
    case ex:Exception =&gt;
      // TODO use logging API from Play here...
      println(&quot;Caught: &quot; + ex)
      ex.printStackTrace
  }
}

private def errorTemplate:String = {
  val fullPath = new File(Play.applicationPath,&quot;/app/views/errors/500.scaml&quot;).toString 
  fullPath.replace(new File(Play.applicationPath+&quot;/app/views&quot;).toString,&quot;&quot;)
}
&lt;/pre&gt;
&lt;p&gt;Once I had this in place, error messages from Scalate are much better. Not only do I see the error in my browser, but I can click on the offending line to open it directly in TextMate.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm7.static.flickr.com/6054/6323523392_91888694bc_o.png&quot; title=&quot;Play Scalate Error Reporting&quot; rel=&quot;lightbox[scalate-goodness]&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6054/6323523392_affe4cf053.jpg&quot; width=&quot;500&quot; height=&quot;290&quot; alt=&quot;Play Scalate Error Reporting&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I&apos;ve published my &lt;a href=&quot;https://github.com/mraible/play-scalate&quot;&gt;play-scalate module on GitHub&lt;/a&gt; so others can try it out. To give it a whirl, add the following to your dependencies.yml:
&lt;/p&gt;
&lt;pre&gt;
    - upgrades -&gt; play-scalate 0.1

repositories:
    - upgrades:
        type: http
        artifact: &quot;http://static.raibledesigns.com/[module]-[revision].zip&quot;
        contains:
            - upgrades -&gt; *
&lt;/pre&gt;&lt;p&gt;Then add &lt;em&gt;with play.modules.scalate.Scalate&lt;/em&gt; to your controllers and call the &lt;em&gt;render()&lt;/em&gt; method.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
After using Scalate and Play for almost 3 months, I&apos;m really enjoying the combination. When I first integrated Scalate with a simple trait, the error messages were always in the console. Now that I&apos;ve borrowed some smarts from Play&apos;s ScalaController and play-scalate&apos;s error reporting, I feel like it&apos;s practically a built-in solution. I was easily able to integrate my desired SiteMesh features and it even allows &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/f6df5b165024407e&quot;&gt;reusable template blocks&lt;/a&gt;. In the end, it&apos;s just Scala and Scalate does a good job of allowing you to leverage that.&lt;/p&gt;
&lt;p&gt;Other thoughts:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you&apos;re writing a lot of Jade and familiar with HTML, Don Park&apos;s &lt;a href=&quot;https://github.com/donpark/html2jade&quot;&gt;html2jade&lt;/a&gt; is a great tool that comes with Scalate support.&lt;/li&gt;
&lt;li&gt;I&apos;m really enjoying writing CSS with &lt;a href=&quot;http://lesscss.org/&quot;&gt;LESS&lt;/a&gt;, particularly the ability to nest rules and have programming features. The only issue I&apos;ve seen is IntelliJ&apos;s LESS plugin only does code-completion for variables rather than CSS values.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;http://www.packtpub.com/play-framework-cookbook/book&quot;&gt;Play Framework Cookbook&lt;/a&gt; is a great reference for learning how to write modules. Not only does it explain how to create modules, it has some great real-world examples for doing bytecode enhancement, implementing message queues, using Solr and how to do production monitoring.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If this series of articles has intrigued you and you&apos;ll be at &lt;a href=&quot;http://www.devoxx.com/display/DV11/Home&quot;&gt;Devoxx&lt;/a&gt; next week, you should stop by &lt;a href=&quot;http://www.devoxx.com/display/DV11/HTML5+with+Play+Scala%2C+CoffeeScript+and+Jade&quot;&gt;my talk on Thursday afternoon&lt;/a&gt;. In addition, there&apos;s several other Play talks at Devoxx and a possible meetup on Wednesday. Check out the &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/eaef08eb36f9eb0a&quot;&gt;Devoxx, anyone?&lt;/a&gt; thread for more information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: There&apos;s one thing I forgot to mention about the Play Scalate Module. When I had Scalate integrated in my app with a trait, I only included the scalate-core and scalate-util JARs in dependencies.yml:&lt;/p&gt;
&lt;pre&gt;
- org.fusesource.scalate -&gt; scalate-core 1.5.2-scala_2.8.1:
    transitive: false
- org.fusesource.scalate -&gt; scalate-util 1.5.2-scala_2.8.1:
    transitive: false
&lt;/pre&gt;&lt;p&gt;However, when I created the play-scalate module, I allowed more dependencies.&lt;/p&gt;
&lt;pre&gt;
- org.fusesource.scalate -&gt; scalate-core 1.5.2-scala_2.8.1:
    exclude:
        - javax.servlet -&gt; *
        - com.sun.jersey -&gt; *
        - org.osgi -&gt; *
- org.fusesource.scalate -&gt; scalate-util 1.5.2-scala_2.8.1
&lt;/pre&gt;Because Scalate depends on &lt;a href=&quot;http://logback.qos.ch/&quot;&gt;Logback&lt;/a&gt;, debug messages started showing up in my console. To fix this, I created &lt;em&gt;conf/logback.xml&lt;/em&gt; in my project and filled it with the following XML.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;appender name=&quot;STDOUT&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&amp;gt;
      &amp;lt;encoder&amp;gt;
          &amp;lt;pattern&amp;gt;%msg%n&amp;lt;/pattern&amp;gt;
      &amp;lt;/encoder&amp;gt;
  &amp;lt;/appender&amp;gt;

  &amp;lt;root level=&quot;info&quot;&amp;gt;
    &amp;lt;appender-ref ref=&quot;STDOUT&quot; /&amp;gt;
  &amp;lt;/root&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This reduces the logging and allows me to increase Scalate&apos;s logging if I ever have the need.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and</id>
        <title type="html">Developing with HTML5, CoffeeScript and Twitter&apos;s Bootstrap </title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and"/>
        <published>2011-10-20T14:47:36-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="less" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="coffeescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5boilerplate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="geolocation" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://www.w3.org/html/logo/&quot;&gt;&lt;img src=&quot;//www.w3.org/html/logo/downloads/HTML5_Logo_128.png&quot; width=&quot;128&quot; height=&quot;128&quot; alt=&quot;HTML5 Logo&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;&gt;&lt;/a&gt;
This article is the fourth in a series about my adventures developing a Fitness Tracking application with HTML5, Play Scala, CoffeeScript and Jade. Previous articles can be found at:
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;Integrating Scalate and Jade with Play 1.2.3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work&quot;&gt;Trying to make CoffeeScript work with Scalate and Play&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate&quot;&gt;Integrating HTML5 Boilerplate with Scalate and Play&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;features&quot;&gt;&lt;strong&gt;Developing Features&lt;/strong&gt;&lt;br/&gt;After getting my desired infrastructure setup, I started coding like a madman. The first feature I needed was a stopwatch to track the duration of a workout, so I started writing one with CoffeeScript. After spending 20 minutes playing with dates and setTimeout, I searched and found a &lt;a href=&quot;http://www.kellishaver.com/projects/stopwatch/&quot;&gt;stopwatch jQuery plug-in&lt;/a&gt;. I added this to my app, deployed it to &lt;a href=&quot;http://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;, brought up the app on my iPhone 3G, clicked &lt;em&gt;Start&lt;/em&gt; and started riding my bike to work. 
&lt;/p&gt;
&lt;p&gt;
When I arrived, I unlocked my phone and discovered that the time had stopped. At first, I thought this was a major setback. My disappointed disappeared when I found a &lt;a href=&quot;http://proft.50megs.com/stopwatch.html&quot;&gt;Super Neat JavaScript Stopwatch&lt;/a&gt; and &lt;a href=&quot;http://www.timpelen.com/extra/sidebars/stopwatch/stopwatch.htm&quot;&gt;K&#229;re Byberg&apos;s version&lt;/a&gt; that worked just fine. This stopwatch used setTimeout, so by keeping the start time, the app on the phone would &lt;em&gt;catch up&lt;/em&gt; as soon as you unlocked it. I ported K&#229;re&apos;s script to CoffeeScript and rejoiced in my working stopwatch. 
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
# Created by K&#229;re Byberg &#169; 21.01.2005. Please acknowledge if used 
# on other domains than http://www.timpelen.com.
# Ported to CoffeeScript by Matt Raible. Also added hours support.
flagClock = 0
flagStop = 0
stopTime = 0
refresh = null
clock = null

start = (button, display) -&gt;
  clock = display
  startDate = new Date()
  startTime = startDate.getTime()
  if flagClock == 0
    $(button).html(&quot;Stop&quot;)
    flagClock = 1
    counter startTime, display
  else
    $(button).html(&quot;Start&quot;)
    flagClock = 0
    flagStop = 1

counter = (startTime) -&gt;
  currentTime = new Date()
  timeDiff = currentTime.getTime() - startTime
  timeDiff = timeDiff + stopTime  if flagStop == 1
  if flagClock == 1
    $(clock).val formatTime timeDiff, &quot;&quot;
    callback = -&gt; counter startTime
    refresh = setTimeout callback, 10
  else
    window.clearTimeout refresh
    stopTime = timeDiff

formatTime = (rawTime, roundType) -&gt;
  if roundType == &quot;round&quot;
    ds = Math.round(rawTime / 100) + &quot;&quot;
  else
    ds = Math.floor(rawTime / 100) + &quot;&quot;
  sec = Math.floor(rawTime / 1000)
  min = Math.floor(rawTime / 60000)
  hour = Math.floor(rawTime / 3600000)
  ds = ds.charAt(ds.length - 1)
  start() if hour &gt;= 24
  sec = sec - 60 * min + &quot;&quot;
  sec = prependZeroCheck sec
  min = min - 60 * hour + &quot;&quot;
  min = prependZeroCheck min
  hour = prependZeroCheck hour
  hour + &quot;:&quot; + min + &quot;:&quot; + sec + &quot;.&quot; + ds

prependZeroCheck = (time) -&gt;
  time = time + &quot;&quot; # convert from int to string
  unless time.charAt(time.length - 2) == &quot;&quot;
    time = time.charAt(time.length - 2) + time.charAt(time.length - 1)
  else
    time = 0 + time.charAt(time.length - 1)

reset = -&gt;
  flagStop = 0
  stopTime = 0
  window.clearTimeout refresh
  if flagClock == 1
    resetDate = new Date()
    resetTime = resetDate.getTime()
    counter resetTime
  else
    $(clock).val &quot;00:00:00.0&quot;

@StopWatch = {
  start: start
  reset: reset
}
&lt;/pre&gt;
&lt;p&gt;The Scalate/Jade template to render this stopwatch looks as follows:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/stopwatch.coffee&quot;)})

#display
  input(id=&quot;clock&quot; class=&quot;xlarge&quot; type=&quot;text&quot; value=&quot;00:00:00.0&quot; readonly=&quot;readonly&quot;)
#controls
  button(id=&quot;start&quot; type=&quot;button&quot; class=&quot;btn primary&quot;) Start
  button(id=&quot;reset&quot; type=&quot;button&quot; class=&quot;btn :disabled&quot;) Reset

:plain
  &amp;lt;script type=&quot;text/coffeescript&quot;&gt;
    $(document).ready -&gt;
      $(&apos;#start&apos;).click -&gt;
        StopWatch.start this, $(&apos;#clock&apos;)

      $(&apos;#reset&apos;).click -&gt;
        StopWatch.reset()
  &amp;lt;/script&gt;
&lt;/pre&gt;
&lt;p&gt;Next, I wanted to create a map that would show your location. For this, I used &lt;a href=&quot;http://merged.ca/iphone/html5-geolocation&quot;&gt;
  Merge Design&apos;s HTML 5 Geolocation Demo&lt;/a&gt; as a guide. The &lt;a href=&quot;http://dev.w3.org/geo/api/spec-source.html&quot;&gt;HTML5 Geo API&lt;/a&gt; is pretty 
  simple, containing only three methods:
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
// Gets the users current position
navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         options);
// Request repeated updates of position
watchId = navigator.geolocation.watchPosition(successCallback, errorCallback);

// Cancel the updates
navigator.geolocation.clearWatch(watchId);
&lt;/pre&gt;
&lt;p&gt;After rewriting the geolocation example in CoffeeScript, I ended up with the following code in my map.coffee script. You&apos;ll notice it uses 
  &lt;a href=&quot;http://code.google.com/apis/maps/documentation/javascript/&quot;&gt;Google Maps JavaScript API&lt;/a&gt; to show an actual map
  with a marker.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
# Geolocation with HTML 5 and Google Maps API based on example from maxheapsize: 
# http://maxheapsize.com/2009/04/11/getting-the-browsers-geolocation-with-html-5/
# This script is by Merge Database and Design, http://merged.ca/ -- if you use some, 
# all, or any of this code, please offer a return link.

map = null
mapCenter = null
geocoder = null
latlng = null
timeoutId = null

initialize = -&gt;
  if Modernizr.geolocation
    navigator.geolocation.getCurrentPosition showMap

showMap = (position) -&gt;
  latitude = position.coords.latitude
  longitude = position.coords.longitude
  mapOptions = {
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  }
  map = new google.maps.Map(document.getElementById(&quot;map&quot;), mapOptions)
  latlng = new google.maps.LatLng(latitude, longitude)
  map.setCenter(latlng)

  geocoder = new google.maps.Geocoder()
  geocoder.geocode({&apos;latLng&apos;: latlng}, addAddressToMap)

addAddressToMap = (results, status) -&gt;
  if (status == google.maps.GeocoderStatus.OK) 
    if (results[1]) 
      marker = new google.maps.Marker({
          position: latlng,
          map: map
      })
      $(&apos;#location&apos;).html(&apos;Your location: &apos; + results[0].formatted_address)
  else 
    alert &quot;Sorry, we were unable to geocode that address.&quot;

start = -&gt;
  timeoutId = setTimeout initialize, 500

reset = -&gt;
  if (timeoutId)
    clearTimeout timeoutId

@Map = {
  start: start
  reset: reset
}
&lt;/pre&gt;
&lt;p&gt;The template to show the map is a mere 20 lines of Jade:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
script(type=&quot;text/javascript&quot; src=&quot;//www.google.com/jsapi&quot;)
script(type=&quot;text/javascript&quot; src=&quot;//maps.googleapis.com/maps/api/js?sensor=false&quot;)

:css
  .demo-map {
    border: 1px solid silver;
    height: 200px;
    margin: 10px auto;
    width: 280px;
  }

#map(class=&quot;demo-map&quot;)

p(id=&quot;location&quot;)
  span(class=&quot;label success&quot;) New
  | Fetching your location with HTML 5 geolocation...

script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/map.coffee&quot;)})
:javascript
    Map.start();
&lt;/pre&gt;
&lt;p&gt;The last two features I wanted were 1) distance traveled and 2) drawing the route taken on the map. For this I learned from 
  &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/geolocation/trip_meter/&quot;&gt;A Simple Trip Meter using the Geolocation API&lt;/a&gt;.
  As I was beginning to port the JS to CoffeeScript, I thought, &quot;there&apos;s got to be a better way.&quot;  I searched and found &lt;a href=&quot;http://js2coffee.org/&quot;&gt;Js2coffee&lt;/a&gt; to do most of the conversion for me. If you know JavaScript and you&apos;re learning CoffeeScript, this is an invaluable tool. 
&lt;/p&gt;
&lt;p&gt;
I tried out the trip meter that evening
  on a bike ride and noticed it said I&apos;d traveled 3 miles when I&apos;d really gone 6. I quickly figured out it was only calculating
  start point to end point and not taking into account all the turns in between. To view what was happening, I integrated my
  odometer.coffee with my map using &lt;a href=&quot;http://code.google.com/apis/maps/documentation/javascript/overlays.html#Polylines&quot;&gt;
  Google Maps Polylines&lt;/a&gt;. Upon finishing the integration, I discovered two things, 1) HTML5 geolocation was highly inaccurate and
  2) &lt;a href=&quot;http://stackoverflow.com/questions/7676423/is-it-possible-to-make-an-html5-trip-meter-that-tracks-distance-traveled/7681295&quot;&gt;geolocation doesn&apos;t run in the background&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
I was able to solve the first problem by passing in {enableHighAccuracy: true} to navigator.geolocation.watchPosition(). Below are two screenshots showing before high accuracy and after. Both screenshots are from the same two-block walk.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a href=&quot;http://farm7.static.flickr.com/6101/6264033565_0353120a06.jpg&quot; rel=&quot;lightbox[html5geo]&quot; title=&quot;Without {enableHighAccuracy: true} by mraible, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6101/6264033565_0353120a06_m.jpg&quot; width=&quot;160&quot; height=&quot;240&quot; alt=&quot;Without {enableHighAccuracy: true}&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;
  
  &lt;a href=&quot;http://farm7.static.flickr.com/6032/6264033561_cf9a8cb311.jpg&quot; rel=&quot;lightbox[html5geo]&quot; title=&quot;With {enableHighAccuracy: true} by mraible, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6032/6264033561_cf9a8cb311_m.jpg&quot; width=&quot;160&quot; height=&quot;240&quot; alt=&quot;With {enableHighAccuracy: true}&quot; style=&quot;margin-left: 30px; border: 1px solid silver&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
The second
issue is a slight show-stopper. &lt;a href=&quot;http://www.phonegap.com/&quot;&gt;PhoneGap&lt;/a&gt; might be able to solve the problem, but I&apos;m currently using a
workaround &amp;rarr; turning off auto-lock and keeping Safari in the foreground. 
&lt;/p&gt;
&lt;p id=&quot;style&quot;&gt;&lt;strong&gt;Making it look good&lt;/strong&gt;&lt;br/&gt;After I got all my desired features developed, I moved onto making the app look good. I started by using &lt;a href=&quot;http://sass-lang.com/&quot;&gt;SASS&lt;/a&gt; for my CSS and installed &lt;a href=&quot;http://www.playframework.org/modules/sass&quot;&gt;Play&apos;s SASS module&lt;/a&gt;. I then switched to &lt;a href=&quot;http://lesscss.org/&quot;&gt;LESS&lt;/a&gt; when I discovered and added &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Twitter&apos;s Bootstrap&lt;/a&gt; to my project. At first I used &lt;a href=&quot;http://www.playframework.org/modules/less-0.3/home&quot;&gt;Play&apos;s LESS module&lt;/a&gt; (version 0.3), but ran into &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/4d76688608105dd/f886dc32c724b7cd&quot;&gt;compilation issues&lt;/a&gt;. I then tried &lt;a href=&quot;https://github.com/greenlaw110/play-greenscript&quot;&gt;Play&apos;s GreenScript module&lt;/a&gt;, but gave up on it when I found it was incompatible with the &lt;a href=&quot;http://www.playframework.org/modules/coffee&quot;&gt;CoffeeScript module&lt;/a&gt;. Switching back to the LESS module and using the &quot;0.3.compatibility&quot; version solved all remaining issues.
&lt;/p&gt;
&lt;p&gt;You might remember that &lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate&quot;&gt;I integrated HTML5 Boilerplate&lt;/a&gt; and wondering why I have both Bootstrap and Boilerplate in my project. At this point, I don&apos;t think Boilerplate is needed, but I&apos;ve kept it just in case it&apos;s doing something for HTML5 cross-browser compatibility. I&apos;ve renamed its style.css to style.less and added the following so it has access to Bootstrap&apos;s variables.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
/* Variables from Bootstrap */
@import &quot;libs/variables.less&quot;;
&lt;/pre&gt;
&lt;p&gt;Then I made my app look a lot better with &lt;a href=&quot;http://twitter.github.com/bootstrap/examples/container-app.html&quot;&gt;layouts&lt;/em&gt;, &lt;a href=&quot;http://twitter.github.com/bootstrap/#forms&quot;&gt;stylish forms&lt;/a&gt;, a &lt;a href=&quot;http://twitter.github.com/bootstrap/#navigation&quot;&gt;fixed topbar&lt;/a&gt; and &lt;a href=&quot;http://twitter.github.com/bootstrap/#alerts&quot;&gt;alerts&lt;/a&gt;. For example, here&apos;s the CoffeeScript I wrote to display geolocation errors:
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
geolocationError = (error) -&gt;
  msg = &apos;Unable to locate position. &apos;
  switch error.code
    when error.TIMEOUT then msg += &apos;Timeout.&apos;
    when error.POSITION_UNAVAILABLE then msg += &apos;Position unavailable.&apos;
    when error.PERMISSION_DENIED then msg += &apos;Please turn on location services.&apos;
    when error.UNKNOWN_ERROR then msg += error.code
  $(&apos;.alert-message&apos;).remove()
  alert = $(&apos;&amp;lt;div class=&quot;alert-message error fade in&quot; data-alert=&quot;alert&quot;&amp;gt;&apos;)
  alert.html(&apos;&amp;lt;a class=&quot;close&quot; href=&quot;#&quot;&amp;gt;&amp;times;&amp;lt;/a&amp;gt;&apos; + msg);
  alert.insertBefore($(&apos;.span10&apos;))
&lt;/pre&gt;
&lt;p&gt;Then I set about styling up the app so it looked good on a smartphone with &lt;a href=&quot;http://thinkvitamin.com/design/getting-started-and-gotchas-of-css-media-queries/&quot;&gt;CSS3 Media Queries&lt;/a&gt;. Below is the LESS code I used to hide elements and squish the widths for smaller devices.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
@media all and (max-device-width: 480px) {
  /* hide scrollbar on mobile */
  html { overflow-y:hidden }
  /* hide sidebar on mobile */
  .home .span4, .home .page-header, .topbar form {
    display: none
  }
  .home .container {
    width: 320px;
  } 
  .about {
    .container, .span10 {
      width: 280px;
    }
    .span10 {
      padding-top: 0px;
    }
  }
&lt;/pre&gt;
&lt;p id=&quot;tools&quot;&gt;&lt;strong&gt;Tools&lt;/strong&gt;&lt;br/&gt;
In the process of developing a stopwatch, odometer, displaying routes and making everything look good, I used a number of tools. I started out primarily with &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; and its bundles for &lt;a href=&quot;https://github.com/appden/less.tmbundle&quot;&gt;LESS&lt;/a&gt;, &lt;a href=&quot;https://github.com/jashkenas/coffee-script-tmbundle&quot;&gt;CoffeeScript&lt;/a&gt; and &lt;a href=&quot;https://github.com/miksago/jade-tmbundle&quot;&gt;Jade&lt;/a&gt;. When I started writing more Scala, I installed the &lt;a href=&quot;https://github.com/mads379/scala.tmbundle&quot;&gt;Scala TextMate Bundle&lt;/a&gt;. When I needed some debugging, I switched to &lt;a href=&quot;http://www.jetbrains.com/idea/&quot;&gt;IntelliJ&lt;/a&gt; and installed its Scala plugin. CoffeeScript, LESS and HAML plugins (for Jade) were already installed by default. I also used James Ward&apos;s &lt;a href=&quot;http://www.jamesward.com/2011/07/28/setup-play-framework-with-scala-in-intellij&quot;&gt;Setup Play Framework with Scala in IntelliJ&lt;/a&gt;.
&lt;/p&gt;
&lt;p id=&quot;issues&quot;&gt;
  &lt;strong&gt;Issues&lt;/strong&gt;&lt;br/&gt;
  I think it&apos;s obvious that my biggest issue so far is the fact that a webapp can&apos;t multitask in the background like a native app can. Beyond that, there&apos;s accuracy issues with HTML5&apos;s geolocation that I haven&apos;t seen in native apps. 
&lt;/p&gt;
&lt;p&gt;
I also ran into a caching issue when calling getCurrentPosition(). It only worked the first time and I had to refresh my browser to get it to work again. Strangely enough, this only happened on my desktop (in Safari and Firefox) and worked fine on my iPhone. Unfortunately, it looks like &lt;a href=&quot;https://github.com/phonegap/phonegap-iphone/issues/197&quot;&gt;PhoneGap has issues&lt;/a&gt; similar to this.
&lt;/p&gt;
&lt;p&gt;My workaround for no webapp multitasking is turning off auto-lock and leaving the browser in the foreground while I exercise. The downside to this is it &lt;em&gt;really&lt;/em&gt; drains the battery quickly (~ 3 hours). I constantly have to charge my phone if I&apos;m testing it throughout the day. The testing is a real pain too. I have to deploy to Heroku (which is easy enough), then go on a walk or bike ride. If something&apos;s broke, I have to return home, tweak some things, redeploy and go again. Also, there&apos;s been a few times where Safari crashes halfway through and I lose all the tracking data. This happens with native apps too, but seemingly not as often.  
&lt;/p&gt;
&lt;p&gt;
  If you&apos;d like to try the app on your mobile phone and see if you experience these issues, checkout &lt;a href=&quot;http://play-more.com&quot;&gt;play-more.com&lt;/a&gt;.
&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;Going forward, there&apos;s still more HTML5 features I&apos;d like to use. In particular, I&apos;d like to play music while the fitness tracker is running. I&apos;d love it if cloud music services (e.g. Pandora or Spotify) had an API I could use to play music in a webapp. &lt;a href=&quot;http://soundcloud.com/&quot;&gt;Soundcloud&lt;/a&gt; might be an option, but I&apos;ve also thought of just uploading some MP3s and playing them with the &amp;lt;audio&amp;gt; tag. 
&lt;/p&gt;
&lt;p&gt;I&apos;ve really enjoyed developing with all these technologies and haven&apos;t experienced much frustration so far. The majority has come from integrating Scalate into Play, but I&apos;ve resolved most problems. Next, I&apos;ll talk about how I&apos;ve improved Play&apos;s Scalate support and my experience working with &lt;a href=&quot;http://scala.playframework.org/documentation/scala-0.9.1/anorm&quot;&gt;Anorm&lt;/a&gt;. 
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate</id>
        <title type="html">Integrating HTML5 Boilerplate with Scalate and Play</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate"/>
        <published>2011-09-28T08:49:35-06:00</published>
        <updated>2012-06-25T22:46:30-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="scalate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5boilerplate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="coffeescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">HTML5 Boilerplate is a project that provides a number of basic files to help you build an HTML5 application. At its core, it&apos;s an HTML template that puts CSS at the top, JavaScript at the bottom, installs Chrome Frame for IE6 users and leverages Modernizr for legacy browser support. It also includes jQuery with the download. One of the major benefits of HTML5 Boilerplate is it ships with a build system (powered by Ant) that concatenates and minimizes CSS and JS for maximum performance. From &lt;a href=&quot;http://html5boilerplate.com/&quot;&gt;html5boilerplate.com&lt;/a&gt;:
&lt;/p&gt;
&lt;p class=&quot;smokey&quot;&gt;
Boilerplate is not a framework, nor does it prescribe any philosophy of development, it&apos;s just got some tricks to get your project off the ground quickly and right-footed.
&lt;/p&gt;
&lt;p&gt;
&lt;p&gt;
I like the idea of its build system to minify and gzip, but I&apos;d probably only use it if I was working on a project that uses Ant. Since I&apos;m using it in a Play project, the whole Ant build system doesn&apos;t help me. Besides, I prefer something like &lt;a href=&quot;http://code.google.com/p/wro4j/&quot;&gt;wro4j&lt;/a&gt;. Wro4j allows you to specify a group of files and then it compiles, minimizes and gzips them all on-the-fly. As far as I know, Play doesn&apos;t have any support for Servlet Filters, so using wro4j in Play is not trivial.
&lt;/p&gt;
&lt;p&gt;
The good news is Play has a &lt;a href=&quot;http://www.playframework.org/modules/greenscript&quot;&gt;GreenScript module&lt;/a&gt; that contains much of the wro4j functionality. However, since I&apos;m using &lt;a href=&quot;http://scalate.fusesource.org/presentations/scalate#(1)&quot;&gt;Scalate&lt;/a&gt; in my project, this goodness is unavailable to me. In the future, the Scalate Team is considering adding &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/2904bbdc8ca9dd46&quot;&gt;better wro4j, JavaScript and CSS integration&lt;/a&gt;. In the meantime, I&apos;m going to pretend I don&apos;t care about concatenation and minimization and trundle along without this feature.
&lt;/p&gt;
&lt;p&gt;To add HTML5 Boilerplate to my Play project, I performed the following steps:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Downloaded the &lt;a href=&quot;http://github.com/paulirish/html5-boilerplate/zipball/v2.0&quot;&gt;2.0 Zipball&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Copied all the static files to my project. Below are the commands I used (where &lt;em&gt;$boilerplate-download&lt;/em&gt; is the expanded download directory and &lt;em&gt;~/dev/play-more&lt;/em&gt; is my project):
&lt;pre style=&quot;margin-top: 5px&quot;&gt;
cd $boilerplate-download
cp 404.html ~/dev/play-more/app/views/errors/404.html
cp *.png ~/dev/play-more/public/.
cp crossdomain.xml ~/dev/play-more/public/.
cp -r css ~/dev/play-more/public/stylesheets/.
cp favicon.ico ~/dev/play-more/public/.
cp humans.txt ~/dev/play-more/public/.
cp -r js/libs ~/dev/play-more/public/javascripts/.
cp robots.txt ~/dev/play-more/public/.
&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;Copied the index.html to &lt;em&gt;~/dev/play-more/app/templates/layouts/default.jade&lt;/em&gt; and modified it to use &lt;a href=&quot;http://scalate.fusesource.org/documentation/jade.html&quot;&gt;Jade syntax&lt;/a&gt;. Since I downloaded the comments-heavy version, I modified many of them to be hidden in the final output.
&lt;pre class=&quot;brush: xml&quot;&gt;
-@ val body: String 
-@ var title: String = &quot;Play More&quot;
-@ var header: String = &quot;&quot;
-@ var footer: String = &quot;&quot;
!!! 5
/ paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ 
&amp;lt;!--&amp;#91;if lt IE 7&amp;#93;&amp;gt; &amp;lt;html class=&quot;no-js ie6 oldie&quot; lang=&quot;en&quot;&amp;gt; &amp;lt;!&amp;#91;endif&amp;#93;--&amp;gt;
&amp;lt;!--&amp;#91;if IE 7&amp;#93;&amp;gt;    &amp;lt;html class=&quot;no-js ie7 oldie&quot; lang=&quot;en&quot;&amp;gt; &amp;lt;!&amp;#91;endif&amp;#93;--&amp;gt;
&amp;lt;!--&amp;#91;if IE 8&amp;#93;&amp;gt;    &amp;lt;html class=&quot;no-js ie8 oldie&quot; lang=&quot;en&quot;&amp;gt; &amp;lt;!&amp;#91;endif&amp;#93;--&amp;gt;
-# Consider adding an manifest.appcache: h5bp.com/d/Offline 
&amp;lt;!--&amp;#91;if gt IE 8&amp;#93;&amp;gt;&amp;lt;!--&amp;gt; &amp;lt;html class=&quot;no-js&quot; lang=&quot;en&quot;&amp;gt; &amp;lt;!--&amp;lt;!&amp;#91;endif&amp;#93;--&amp;gt;
head
  meta(charset=&quot;utf-8&quot;)

  -# Use the .htaccess and remove these lines to avoid edge case issues. More info: h5bp.com/b/378 
  meta(http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge,chrome=1&quot;)

  title=title
  meta(name=&quot;description&quot; content=&quot;&quot;)
  meta(name=&quot;author&quot; content=&quot;Matt Raible ~ matt@raibledesigns.com&quot;)

  -# Mobile viewport optimized: j.mp/bplateviewport 
  meta(name=&quot;viewport&quot; content=&quot;width=device-width,initial-scale=1&quot;)

  -# Place favicon.ico and apple-touch-icon.png in the root directory: mathiasbynens.be/notes/touch-icons

  -# CSS: implied media=all
  link(rel=&quot;stylesheet&quot; href={uri(&quot;/public/stylesheets/style.css&quot;)})
  -# end CSS

  -# More ideas for your &amp;lt;head&amp;gt; here: h5bp.com/d/head-Tips 
  -#
    All JavaScript at the bottom, except for Modernizr / Respond.
    Modernizr enables HTML5 elements &amp;amp; feature detects; Respond is a polyfill for min/max-width CSS3 Media Queries
    For optimal performance, use a custom Modernizr build: www.modernizr.com/download/ 

  script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/libs/modernizr-2.0.6.min.js&quot;)})
body
  #container
    header = header
    #main(role=&quot;main&quot;)
      != body
    footer = footer

  -# JavaScript at the bottom for fast page loading 
  
  / Grab Google CDN&apos;s jQuery, with a protocol relative URL; fall back to local if offline 
  script(type=&quot;text/javascript&quot; src=&quot;//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js&quot;)
  :javascript
    window.jQuery || document.write(&apos;&amp;lt;script src={uri(&quot;/public/javascripts/libs/jquery-1.6.2.min.js&quot;)}&amp;gt;&amp;lt;\/script&amp;gt;&apos;)

  -# Change UA-XXXXX-X to be your site&apos;s ID 
  :javascript
    window._gaq = &amp;#91;&amp;#91;&apos;_setAccount&apos;,&apos;UA-25859875-1&apos;&amp;#93;,&amp;#91;&apos;_trackPageview&apos;&amp;#93;,&amp;#91;&apos;_trackPageLoadTime&apos;&amp;#93;&amp;#93;;
    Modernizr.load({
      load: (&apos;https:&apos; == location.protocol ? &apos;//ssl&apos; : &apos;//www&apos;) + &apos;.google-analytics.com/ga.js&apos;
    });

  -# Prompt IE 6 users to install Chrome Frame. Remove this if you want to support IE 6. 
  -# http://chromium.org/developers/how-tos/chrome-frame-getting-started 
  /&amp;#91;if lt IE 7&amp;#93;
    script(src=&quot;//ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js&quot;)
    :javascript
      window.attachEvent(&apos;onload&apos;,function(){CFInstall.check({mode:&apos;overlay&apos;})})
        
!= &quot;&amp;lt;/html&amp;gt;&quot;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Next, I had to add support for layouts to my &lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;homegrown Scalate support&lt;/a&gt;. I did this by specifying a &lt;code&gt;layoutStrategy&lt;/code&gt; when initializing the TemplateEngine. From &lt;em&gt;play-more/app/controllers/ScalateTemplate.scala&lt;/em&gt;:
&lt;pre class=&quot;brush: scala&quot;&gt;
engine.classLoader = Play.classloader
engine.layoutStrategy = new DefaultLayoutStrategy(engine, 
  Play.getFile(&quot;/app/templates/layouts/default&quot; + scalateType).getAbsolutePath)
engine
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s it! Now I have HTML5 Boilerplate integrated into my Play/Scalate/Jade application. To set the title and header in my &lt;em&gt;index.jade&lt;/em&gt;, I simply added the following lines at the top:&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
- attributes(&quot;title&quot;) = &quot;Counting&quot;
- attributes(&quot;header&quot;) = &quot;HTML5 Rocks!&quot;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;CoffeeScript Tip&lt;/strong&gt;&lt;br/&gt;
Yesterday, I mentioned that I was &lt;a href=&quot;http://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work&quot;&gt;having issues getting CoffeeScript to work with Scalate&lt;/a&gt; and that I was going to try and get the in-browser compiler working. First of all, reverting to Scalate 1.4.1 didn&apos;t work because there is no CoffeeScript support in 1.4.1. So I stayed with 1.5.2 and used PandaWood&apos;s &lt;a href=&quot;http://forgivingworm.wordpress.com/2010/09/27/running-coffeescript-in-browser/&quot;&gt;Running CoffeeScript In-Browser Tutorial&lt;/a&gt;. I copied &lt;a href=&quot;http://github.com/jashkenas/coffee-script/raw/master/extras/coffee-script.js&quot;&gt;coffee-script.js&lt;/a&gt; to &lt;em&gt;~/dev/play-more/public/javascripts/libs&lt;/em&gt; and added a reference to it in my default.jade layout:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-# JavaScript at the bottom for fast page loading 
script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/libs/coffee-script.js&quot;)})
&lt;/pre&gt;
&lt;p&gt;Then I was able to write CoffeeScript in a .jade template using the following syntax:&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
:plain
  &amp;lt;script type=&quot;text/coffeescript&quot;&amp;gt;
    alert &quot;hello world&quot;
  &amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
If you&apos;ve integrated HTML5 Boilerplate into your Play application, I&apos;d love to hear about it. Now that I have all the infrastructure in place (Jade, CoffeeScript, HTML5 Boilerplate), I&apos;m looking forward to getting some development done. Who knows, maybe I&apos;ll even come up with my own &lt;a href=&quot;http://www.zeroturnaround.com/blog/play-framework-unfeatures-that-irk-my-inner-geek/&quot;&gt;Play Un-Features That Really Irk My Inner Geek&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work</id>
        <title type="html">Trying to make CoffeeScript work with Scalate and Play</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work"/>
        <published>2011-09-27T13:59:18-06:00</published>
        <updated>2012-11-29T01:26:02-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="coffeescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scalate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">A few weeks ago, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;integrating Scalate with Play&lt;/a&gt;. 
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;The next steps in my Play Scala adventure will be trying to get the &lt;a href=&quot;http://www.playframework.org/modules/coffee&quot; style=&quot;color: #666&quot;&gt;CoffeeScript module&lt;/a&gt; to work. I also hope to integrate &lt;a href=&quot;http://html5boilerplate.com/&quot; style=&quot;color: #666&quot;&gt;HTML5 Boilerplate&lt;/a&gt; with Jade and &lt;a href=&quot;http://scalate.fusesource.org/documentation/user-guide.html#layouts&quot; style=&quot;color: #666&quot;&gt;Scalate Layouts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since my last writing, the Scalate Team has created a new branch for Scala 2.8.x (that&apos;s compatible with Play) and &lt;a href=&quot;http://scalate.fusesource.org/blog/releases/release-1.5.2.html&quot;&gt;released 1.5.2&lt;/a&gt;. To upgrade my Play application to use this version, I changed my dependencies.yml to have the following:
&lt;/p&gt;
&lt;pre&gt;
  - org.fusesource.scalate -&gt; scalate-core 1.5.2-scala_2.8.1:
      transitive: false
  - org.fusesource.scalate -&gt; scalate-util 1.5.2-scala_2.8.1:
      transitive: false
&lt;/pre&gt;
&lt;p&gt;Unfortunately, this release breaks Scalate&apos;s CoffeeScript support because it &lt;a href=&quot;http://groups.google.com/group/scalate/msg/519b8ce04336b57e&quot;&gt;wraps the code with illegal comments&lt;/a&gt;. This has been fixed in the latest snapshot, but no new release has been cut. However, even if it did work, it&apos;s not quite what I&apos;m looking for. The 1.5.2 release allows for compiling inline CoffeeScript on-the-fly, but I&apos;d rather store my .coffee files external to the page.
&lt;/p&gt;
&lt;p&gt;To try and figure out how to do this, I sent a message to the Scalate Google Group asking &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/868e5c1afb6d38ac&quot;&gt;Does Scalate allow for referencing (and compiling) CoffeeScript files like the plugin for Play?&lt;/a&gt; My email prompted the Scalate Team to do some modifications that seemed to do exactly what I was looking for.
  &lt;/p&gt;
&lt;div class=&quot;quote&quot; style=&quot;margin-left: 0; margin-bottom: 10px&quot;&gt;
FWIW I&apos;ve just checked in a couple of coffeescript examples. To run it, grab the code &amp;amp; do a local build... 
&lt;br/&gt;&lt;br/&gt;
&lt;a href=&quot;http://scalate.fusesource.org/source.html&quot;&gt;http://scalate.fusesource.org/source.html&lt;/a&gt;&lt;br/&gt;
&lt;a href=&quot;http://scalate.fusesource.org/building.html&quot;&gt;http://scalate.fusesource.org/building.html&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
then run this...
&lt;br/&gt;
&lt;pre style=&quot;margin-top: 10px&quot;&gt;
cd samples/scalate-example 
mvn jetty:run
&lt;/pre&gt;
then open &lt;a href=&quot;http://localhost:8080/coffee/index&quot;&gt;http://localhost:8080/coffee/index&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
there are 2 sample jade files which use embedded coffee or a separate  coffee file (using the .js extension in the &amp;lt;script src attribute&amp;gt; 
&lt;br/&gt;&lt;br/&gt;
&lt;a href=&quot;https://github.com/scalate/scalate/tree/master/samples/scalate-example/src/main/webapp/coffee&quot;&gt;https://github.com/scalate/scalate/tree/master/samples/scalate-exampl...&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
e.g. here&apos;s a jade file references a separate .js file for a coffee script which gets converted to .js on the server... 
&lt;br/&gt;&lt;br/&gt;
&lt;a href=&quot;https://github.com/scalate/scalate/blob/master/samples/scalate-example/src/main/webapp/coffee/external.jade&quot;&gt;https://github.com/scalate/scalate/blob/master/samples/scalate-exampl...&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;To try out the improved CoffeeScript support, I checked out the source and fumbled with Git branches for a bit before I got latest version of Scalate to build. Unfortunately, it didn&apos;t work because Play doesn&apos;t know how to process the .js and .css files.&lt;/p&gt;
&lt;pre&gt;
@67o8fflce 
Application.foo.js action not found 
Action not found 
Action Application.foo.js could not be found. Error raised is 
Controller controllers.Application.foo not found 
play.exceptions.ActionNotFoundException: Action Application.foo.js not 
found 
        at play.mvc.ActionInvoker.getActionMethod(ActionInvoker.java: 
588) 
        at play.mvc.ActionInvoker.resolve(ActionInvoker.java:85) 
        at Invocation.HTTP Request(Play!) 
Caused by: java.lang.Exception: Controller controllers.Application.foo 
not found 
        ... 3 more 
08:20:21,133 ERROR ~
&lt;/pre&gt;
&lt;p&gt;Based on this error, I assumed I needed a Controller to do receive the .js and .css requests and compile them accordingly with Scalate. I changed my Jade template to have the following:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
script(src=&quot;/assets/foo.js&quot; type=&quot;text/javascript&quot;) 
&lt;/pre&gt;
&lt;p&gt;
Then I added a new route to my Play application: 
&lt;/p&gt;
&lt;pre&gt;
  GET     /                           Application.index
  GET     /assets/{template}          ScalateResource.process
&lt;/pre&gt;
&lt;p&gt;
My ScalateResource.scala class is as follows: 
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
package controllers 

import play.mvc._ 

object ScalateResource extends Controller { 

  def process(args: (Symbol, Any)*) = { 
    var template = params.get(&quot;template&quot;) 
    // replace .js with .coffee 
    template = template.replace(&quot;.js&quot;, &quot;.coffee&quot;) 
    // replace .css with .scss 
    template = template.replace(&quot;.css&quot;, &quot;.scss&quot;) 
    ScalateTemplate(template).render(); 
  } 
} 
&lt;/pre&gt;
&lt;p&gt;
Unfortunately, when I tried to access http://localhost:9000/assets/foo.js, I received the following error: 
&lt;/p&gt;
&lt;p class=&quot;smokey&quot; style=&quot;background: #ffd; border: 1px solid silver&quot;&gt;
TemplateException occured : Not a template file extension (md | markdown | ssp | scaml | mustache | jade), you requested: coffee 
&lt;/p&gt;
&lt;p&gt;At this point, I still haven&apos;t &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/868e5c1afb6d38ac&quot;&gt;figured out how to solve this&lt;/a&gt;. I can only assume that the reason this works in the example application is because it uses a TemplateEngineFilter that&apos;s mapped to /*.
&lt;/p&gt;
&lt;p&gt;As I see it, I have a few choices if I want to continue using CoffeeScript and Scalate in my application:
&lt;ol&gt;
  &lt;li&gt;Revert to an older build of Scalate that uses the in-browser CoffeeScript compiler.&lt;/li&gt;
  &lt;li&gt;Try to get a new version released that fixes the comment bug and use inline CoffeeScript.&lt;/li&gt;
  &lt;li&gt;Keep trying to figure out how to get external files compiled by Scalate.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Obviously, I&apos;d like to do #3 the most, but with the lack of responses from the Scalate group, this seems like the most challenging. Since #1 is the easiest (and I can complete without anyone&apos;s help), I&apos;ll be going that route for now. With any luck, the 2nd and third solutions will surface as options before my talk in November.&lt;/p&gt;
&lt;p id=&quot;update&quot;&gt;&lt;strong&gt;Update Oct 4, 2011:&lt;/strong&gt; I was able to get external CoffeeScript files working! It was rather simple actually. I just tried the &lt;a href=&quot;https://github.com/robfig/play-coffee&quot;&gt;Play CoffeeScript module&lt;/a&gt; again, using Scalate&apos;s &lt;code&gt;{uri(&quot;/path&quot;)}&lt;/code&gt; helper. For example, in a Jade template:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala; toolbar: false&quot;&gt;
script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/script.coffee&quot;)})
&lt;/pre&gt;
&lt;p&gt;This compiles the CoffeeScript file on the server and returns JavaScript. &lt;em&gt;Sweet!&lt;/em&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/how_do_you_get_started</id>
        <title type="html">How do you get started in programming?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/how_do_you_get_started"/>
        <published>2011-07-28T11:12:09-06:00</published>
        <updated>2011-07-28T17:16:14-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="groovy" scheme="http://roller.apache.org/ns/tags/" />
        <category term="programming" scheme="http://roller.apache.org/ns/tags/" />
        <category term="opensource" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jvm" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">I recently received the email below from someone asking how he might get started in programming. I think this is a popular topic, especially given the current economic situation in the US (&lt;a href=&quot;http://www.huffingtonpost.com/david-honig/coming-to-terms-with-the-_b_908181.html&quot;&gt;unemployment is high, but not in the tech industry&lt;/a&gt;). For that reason, I figured I&apos;d post my response here and allow others to chime in with their advice.
&lt;/p&gt;
&lt;p class=&quot;quote&quot; style=&quot;color: #666; background: #eee; border: 1px solid silver; border-left: 5px solid silver&quot;&gt;
I read about you on LinkedIn, forgive my intrusion. Since you seem like an expert in the field of designing websites I wanted to know your thoughts on switching into this field late in life. I am 41 and looking to make the move from an unrelated field (finance) to programming.  So far I have learned HTML, CSS and some Javascript.   I have taken classes on C and Java.   I have made some basic Android phone apps. 
&lt;br/&gt;&lt;br/&gt;
What languages do you think I should focus on?  What is the fastest way to get up to speed to make a career of it?  Classes?  Take a entry level job?  Study on my own?
 &lt;br/&gt;&lt;br/&gt;
Thanks for any insights&#8230;.
&lt;/p&gt;
&lt;p&gt;My reply:&lt;/p&gt;
&lt;p class=&quot;quote&quot; style=&quot;color: #666; background: #ffd&quot;&gt;
It&apos;s interesting that you&apos;re switching from finance to programming. I did the same thing early on in my career, but I was fortunate enough to do it in college (I have degrees in Russian, International Business and Finance) and therefore able to audit some CS classes before I graduated.
&lt;br/&gt;&lt;br/&gt;
I think the most valuable skills these days are front-end skills (HTML, CSS and JavaScript). If you can combine those skills with the ability to design websites, you&apos;ll go along way. I&apos;ve taken a different approach where I have excellent front-end skills, but also know a lot about the backend. 
&lt;br/&gt;&lt;br/&gt;
While it helps to have a Java background these days, the real sweat spot is the JVM and the containers that run on it like Tomcat and Jetty. A lot of Java developers are learning Groovy and Scala, but unfortunately a lot of their documentation/books are targeted towards Java developers.
&lt;br/&gt;&lt;br/&gt;
The fastest way to get up-to-speed on it is to start your own project (if you can&apos;t get a company to hire you to do it). I&apos;d suggest creating a webapp that solves a problem that you have, makes your life easier, etc. If you open source it and build a community around it, that&apos;s just as good as working for a company as far as experience goes. Combine this with studying on your own and you can likely come up to speed very quickly.
&lt;/p&gt;
&lt;p&gt;As a programmer, what advice do you have for someone looking to switch careers, or get into our industry fresh out of college? </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/integrating_oauth_with_appfuse_and</id>
        <title type="html">Integrating OAuth with AppFuse and its REST API</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/integrating_oauth_with_appfuse_and"/>
        <published>2011-07-05T10:56:48-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rest" scheme="http://roller.apache.org/ns/tags/" />
        <category term="oauth" scheme="http://roller.apache.org/ns/tags/" />
        <category term="enunciate" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">One of the new features in &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_1_released&quot;&gt;AppFuse 2.1&lt;/a&gt; is an &lt;a href=&quot;http://issues.appfuse.org/browse/APF-897&quot;&gt;appfuse-ws&lt;/a&gt; archetype. This archetype leverages &lt;a href=&quot;http://enunciate.codehaus.org/&quot;&gt;Enunciate&lt;/a&gt; and &lt;a href=&quot;http://cxf.apache.org/&quot;&gt;CXF&lt;/a&gt; to create a project with a REST API and generated HTML documentation. Enunciate is a very useful tool, allowing you to develop web services with JAX-RS and JAX-WS annotations and have all types of client libraries generated. For me, it seems very useful for developing the backend of &lt;abbr title=&quot;Service Oriented Front End Applications&quot;&gt;SOFEA&lt;/abbr&gt; (a.k.a. modern) applications. &lt;/p&gt;
&lt;p&gt;Back in March, &lt;a href=&quot;http://www.java.net/blogs/stoicflame/&quot;&gt;Ryan Heaton&lt;/a&gt; published a nice article on &lt;a href=&quot;http://docs.codehaus.org/display/ENUNCIATE/Securing+Web+Services&quot;&gt;Securing Web Services&lt;/a&gt; in an Enunciate application. I decided to take his tutorial a step further and not only secure my web services, but also to integrate with  OAuth 2. In this tutorial, I&apos;ll show you how to create a new application with AppFuse WS, secure it, add OAuth support, and then use a client app to authenticate and retrieve data.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#create-appfuse-ws&quot;&gt;Create a New AppFuse WS Project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#integrate-oauth&quot;&gt;Integrate Spring Security and OAuth&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#oauth-client&quot;&gt;Authenticate and Retrieve Data with Client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;create-appfuse-ws&quot;&gt;
&lt;strong&gt;Create a New AppFuse WS Project&lt;/strong&gt;&lt;br/&gt;
To begin, I visited the &lt;a href=&quot;http://static.appfuse.org/archetypes.html&quot;&gt;Create AppFuse Archetypes&lt;/a&gt; page and created a new application using the &quot;Web Services Only&quot; option in the &lt;em&gt;Web Framework&lt;/em&gt; dropdown. Below is the command I used to create the &quot;appfuse-oauth&quot; project.
&lt;/p&gt;
&lt;pre&gt;
mvn archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes \
-DarchetypeArtifactId=appfuse-ws-archetype -DarchetypeVersion=2.1.0 \
-DgroupId=org.appfuse.example -DartifactId=appfuse-oauth 
&lt;/pre&gt;
&lt;p&gt;After doing this, I started the app using &lt;strong&gt;mvn jetty:run&lt;/strong&gt; and confirmed it started OK. At this point, I was able to view the generated documentation for the application at &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt;. The screenshot below shows what the app looks like at this point.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm7.static.flickr.com/6004/5891597325_7f9829e158.jpg&quot; title=&quot;AppFuse WS Homepage&quot; rel=&quot;lightbox[appfuse-oauth]&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6004/5891597325_7f9829e158_m.jpg&quot; width=&quot;240&quot; height=&quot;198&quot; alt=&quot;AppFuse WS Homepage&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;font-weight: italic; color: #666; margin-left: 10px&quot;&gt;
NOTE: You might notice the REST endpoint of /{username}. This is &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1246&quot; style=&quot;color: #666&quot;&gt;a bug&lt;/a&gt; in AppFuse 2.1.0 and has been fixed in SVN. It does not affect this tutorial.&lt;/p&gt;
&lt;p id=&quot;integrate-oauth&quot;&gt;
&lt;strong&gt;Integrate Spring Security and OAuth&lt;/strong&gt;&lt;br/&gt;
I originally tried to integrate Spring Security with Enunciate&apos;s &lt;a href=&quot;http://docs.codehaus.org/display/ENUNCIATE/Securing+Web+Services&quot;&gt;Securing Web Services Tutorial&lt;/a&gt;. However, it only secures endpoints and doesn&apos;t do enough filtering for OAuth support, so I ended up using a custom web.xml. I put this file in &lt;em&gt;src/main/resources&lt;/em&gt; and loaded it in my &lt;em&gt;enunciate.xml&lt;/em&gt; file. I also upgraded Spring Security and imported my security.xml file.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
  &amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
  &amp;lt;enunciate xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
             xsi:noNamespaceSchemaLocation=&quot;http://enunciate.codehaus.org/schemas/enunciate-1.22.xsd&quot;&amp;gt;
      ...
      &amp;lt;webapp mergeWebXML=&quot;src/main/resources/web.xml&quot;/&amp;gt;
      &amp;lt;modules&amp;gt;
      ...
          &amp;lt;spring-app disabled=&quot;false&quot; springVersion=&quot;3.0.5.RELEASE&quot;&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/applicationContext-resources.xml&quot;/&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/applicationContext-dao.xml&quot;/&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/applicationContext-service.xml&quot;/&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/applicationContext.xml&quot;/&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/security.xml&quot;/&amp;gt;
          &amp;lt;/spring-app&amp;gt;
      &amp;lt;/modules&amp;gt;
  &amp;lt;/enunciate&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Then I created &lt;em&gt;src/main/resources/web.xml&lt;/em&gt; with a filter for Spring Security and a DispatcherServlet for OAuth support.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;web-app xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;
         version=&quot;3.0&quot;&amp;gt;

    &amp;lt;filter&amp;gt;
        &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
        &amp;lt;filter-class&amp;gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&amp;gt;
        &amp;lt;init-param&amp;gt;
            &amp;lt;param-name&amp;gt;targetBeanName&amp;lt;/param-name&amp;gt;
            &amp;lt;param-value&amp;gt;springSecurityFilterChain&amp;lt;/param-value&amp;gt;
        &amp;lt;/init-param&amp;gt;
    &amp;lt;/filter&amp;gt;

    &amp;lt;filter-mapping&amp;gt;
        &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
    &amp;lt;/filter-mapping&amp;gt;

    &amp;lt;servlet&amp;gt;
        &amp;lt;servlet-name&amp;gt;appfuse-oauth&amp;lt;/servlet-name&amp;gt;
        &amp;lt;servlet-class&amp;gt;org.springframework.web.servlet.DispatcherServlet&amp;lt;/servlet-class&amp;gt;
        &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;
    &amp;lt;/servlet&amp;gt;

    &amp;lt;servlet-mapping&amp;gt;
        &amp;lt;servlet-name&amp;gt;appfuse-oauth&amp;lt;/servlet-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;/oauth/*&amp;lt;/url-pattern&amp;gt;
    &amp;lt;/servlet-mapping&amp;gt;
&amp;lt;/web-app&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I created a &lt;em&gt;src/main/resources/security.xml&lt;/em&gt; and used it to secure my API, specify a login page, supply the users and integrate OAuth (see the last 4 beans below). &lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/security&quot;
             xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
             xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
             xmlns:oauth=&quot;http://www.springframework.org/schema/security/oauth2&quot;
             xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
                           http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd&quot;&amp;gt;

    &amp;lt;http auto-config=&quot;true&quot;&amp;gt;
        &amp;lt;intercept-url pattern=&quot;/api/**&quot; access=&quot;ROLE_USER&quot;/&amp;gt;
        &amp;lt;intercept-url pattern=&quot;/oauth/**&quot; access=&quot;ROLE_USER&quot;/&amp;gt;
        &amp;lt;intercept-url pattern=&quot;/**&quot; access=&quot;IS_AUTHENTICATED_ANONYMOUSLY&quot;/&amp;gt;
        &amp;lt;form-login login-page=&quot;/login.jsp&quot; authentication-failure-url=&quot;/login.jsp?error=true&quot;
                    login-processing-url=&quot;/j_security_check&quot;/&amp;gt;
    &amp;lt;/http&amp;gt;

    &amp;lt;authentication-manager&amp;gt;
        &amp;lt;authentication-provider&amp;gt;
            &amp;lt;user-service&amp;gt;
                &amp;lt;user name=&quot;admin&quot; password=&quot;admin&quot; authorities=&quot;ROLE_USER,ROLE_ADMIN&quot;/&amp;gt;
                &amp;lt;user name=&quot;user&quot; password=&quot;user&quot; authorities=&quot;ROLE_USER&quot;/&amp;gt;
            &amp;lt;/user-service&amp;gt;
        &amp;lt;/authentication-provider&amp;gt;
    &amp;lt;/authentication-manager&amp;gt;

    &amp;lt;!--hook up the spring security filter chain--&amp;gt;
    &amp;lt;beans:alias name=&quot;springSecurityFilterChain&quot; alias=&quot;securityFilter&quot;/&amp;gt;

    &amp;lt;beans:bean id=&quot;tokenServices&quot;
                class=&quot;org.springframework.security.oauth2.provider.token.InMemoryOAuth2ProviderTokenServices&quot;&amp;gt;
        &amp;lt;beans:property name=&quot;supportRefreshToken&quot; value=&quot;true&quot;/&amp;gt;
    &amp;lt;/beans:bean&amp;gt;

    &amp;lt;oauth:provider client-details-service-ref=&quot;clientDetails&quot; token-services-ref=&quot;tokenServices&quot;&amp;gt;
        &amp;lt;oauth:verification-code user-approval-page=&quot;/oauth/confirm_access&quot;/&amp;gt;
    &amp;lt;/oauth:provider&amp;gt;

    &amp;lt;oauth:client-details-service id=&quot;clientDetails&quot;&amp;gt;
        &amp;lt;!--&amp;lt;oauth:client clientId=&quot;my-trusted-client&quot; authorizedGrantTypes=&quot;password,authorization_code,refresh_token&quot;/&amp;gt;
        &amp;lt;oauth:client clientId=&quot;my-trusted-client-with-secret&quot;
                      authorizedGrantTypes=&quot;password,authorization_code,refresh_token&quot; secret=&quot;somesecret&quot;/&amp;gt;
        &amp;lt;oauth:client clientId=&quot;my-less-trusted-client&quot; authorizedGrantTypes=&quot;authorization_code&quot;/&amp;gt;--&amp;gt;
        &amp;lt;oauth:client clientId=&quot;ajax-login&quot; authorizedGrantTypes=&quot;authorization_code&quot;/&amp;gt;
    &amp;lt;/oauth:client-details-service&amp;gt;
&amp;lt;/beans:beans&amp;gt;
&lt;/pre&gt;
&lt;p&gt;I used the &lt;a href=&quot;http://static.springsource.org/spring-security/oauth/tutorial.html&quot;&gt;OAuth for Spring Security sample apps&lt;/a&gt; to figure this out. In this example, I used authorizedGrantTypes=&quot;authorization_code&quot;, but you can see from the commented &amp;lt;oauth:client&gt; elements above that there&apos;s a few different options. You should also note that the clientId is hard-coded to &quot;ajax-login&quot;, signifying I only want to allow a single application to authenticate.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;At this point, I&apos;d like to give a shoutout to Ryan Heaton for creating &lt;em&gt;both&lt;/em&gt; Enunciate and Spring Security&apos;s OAuth support. Nice work Ryan!&lt;/p&gt;
&lt;p&gt;At this point, I needed to do a number of additional tasks to finish integrating oauth. The first was to modify the Jetty Plugin&apos;s configuration to 1) run on port 9000, 2) load my custom files and 3) allow jetty:run to recognize Enunciate&apos;s generated files. Below is the final configuration in my pom.xml.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;maven-jetty-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;6.1.26&amp;lt;/version&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;connectors&amp;gt;
            &amp;lt;connector implementation=&quot;org.mortbay.jetty.nio.SelectChannelConnector&quot;&amp;gt;
                &amp;lt;port&amp;gt;9000&amp;lt;/port&amp;gt;
                &amp;lt;maxIdleTime&amp;gt;60000&amp;lt;/maxIdleTime&amp;gt;
            &amp;lt;/connector&amp;gt;
        &amp;lt;/connectors&amp;gt;
        &amp;lt;webAppConfig&amp;gt;
            &amp;lt;baseResource implementation=&quot;org.mortbay.resource.ResourceCollection&quot;&amp;gt;
                &amp;lt;resourcesAsCSV&amp;gt;
                    ${basedir}/src/main/webapp,
                    ${project.build.directory}/${project.build.finalName}
                &amp;lt;/resourcesAsCSV&amp;gt;
            &amp;lt;/baseResource&amp;gt;
            &amp;lt;contextPath&amp;gt;/appfuse-oauth&amp;lt;/contextPath&amp;gt;
        &amp;lt;/webAppConfig&amp;gt;
        &amp;lt;webXml&amp;gt;${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml&amp;lt;/webXml&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I added the necessary OAuth dependencies for Spring Security to my pom.xml. Since the latest release is a milestone release, I had to add Spring&apos;s milestone repo too. 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;repository&amp;gt;
    &amp;lt;id&amp;gt;spring-milestone&amp;lt;/id&amp;gt;
    &amp;lt;url&amp;gt;http://s3.amazonaws.com/maven.springframework.org/milestone&amp;lt;/url&amp;gt;
&amp;lt;/repository&amp;gt;
...
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.security&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-security-taglibs&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;${spring.version}&amp;lt;/version&amp;gt;
    &amp;lt;exclusions&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-web&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-support&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
    &amp;lt;/exclusions&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.security.oauth&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-security-oauth&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0.0.M3&amp;lt;/version&amp;gt;
    &amp;lt;exclusions&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-beans&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-context&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-core&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
    &amp;lt;/exclusions&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-webmvc&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;${spring.version}&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;javax.servlet&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;servlet-api&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.5&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;javax.servlet&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;jstl&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.1.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;taglibs&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;standard&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.1.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Since I named my DispatcherServlet &quot;appfuse-oauth&quot; in web.xml, I created a &lt;em&gt;src/main/webapp/WEB-INF/appfuse-oauth-servlet.xml&lt;/em&gt; to configure Spring MVC. I had to create the &lt;em&gt;src/main/webapp/WEB-INF&lt;/em&gt; directory. &lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
       xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd&quot;&amp;gt;

    &amp;lt;!-- Scans the classpath of this application for @Components to deploy as beans --&amp;gt;
    &amp;lt;context:component-scan base-package=&quot;org.appfuse.examples.webapp&quot;/&amp;gt;

    &amp;lt;!-- Configures the @Controller programming model --&amp;gt;
    &amp;lt;mvc:annotation-driven/&amp;gt;

    &amp;lt;!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory --&amp;gt;
    &amp;lt;bean class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&amp;gt;
        &amp;lt;property name=&quot;viewClass&quot; value=&quot;org.springframework.web.servlet.view.JstlView&quot;/&amp;gt;
        &amp;lt;property name=&quot;prefix&quot; value=&quot;/&quot;/&amp;gt;
        &amp;lt;property name=&quot;suffix&quot; value=&quot;.jsp&quot;/&amp;gt;
    &amp;lt;/bean&amp;gt;
&amp;lt;/beans&amp;gt;
&lt;/pre&gt;
&lt;p&gt;In order to show the OAuth confirmation page, I needed to create &lt;em&gt;src/main/java/org/appfuse/examples/webapp/AccessConfirmationController.java&lt;/em&gt; and map it to /oauth/confirm_access. I copied this from one of the sample projects and modified to use Spring&apos;s annotations.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.webapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.ClientAuthenticationToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.verification.ClientAuthenticationCache;
import org.springframework.security.oauth2.provider.verification.DefaultClientAuthenticationCache;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.TreeMap;

/**
 * Controller for retrieving the model for and displaying the confirmation page
 * for access to a protected resource.
 *
 * @author Ryan Heaton
 */
@Controller
@RequestMapping(&quot;/confirm_access&quot;)
public class AccessConfirmationController {

    private ClientAuthenticationCache authenticationCache = new DefaultClientAuthenticationCache();
    @Autowired
    private ClientDetailsService clientDetailsService;

    @RequestMapping(method = RequestMethod.GET)
    protected ModelAndView confirm(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ClientAuthenticationToken clientAuth = authenticationCache.getAuthentication(request, response);
        if (clientAuth == null) {
            throw new IllegalStateException(&quot;No client authentication request to authorize.&quot;);
        }

        TreeMap&amp;lt;String, Object&amp;gt; model = new TreeMap&amp;lt;String, Object&amp;gt;();
        ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
        model.put(&quot;auth_request&quot;, clientAuth);
        model.put(&quot;client&quot;, client);

        return new ModelAndView(&quot;access_confirmation&quot;, model);
    }
}
&lt;/pre&gt;
&lt;p&gt;This controller delegates to &lt;em&gt;src/main/webapp/access_confirmation.jsp&lt;/em&gt;. I created this file and filled it with code to display Accept and Deny buttons.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;%@ page import=&quot;org.springframework.security.core.AuthenticationException&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;org.springframework.security.oauth2.provider.verification.BasicUserApprovalFilter&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;org.springframework.security.oauth2.provider.verification.VerificationCodeFilter&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;org.springframework.security.web.WebAttributes&quot; %&amp;gt;
&amp;lt;%@ taglib prefix=&quot;authz&quot; uri=&quot;http://www.springframework.org/security/tags&quot; %&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Confirm Access&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; media=&quot;all&quot;
          href=&quot;http://demo.appfuse.org/appfuse-struts/styles/simplicity/theme.css&quot;/&amp;gt;
    &amp;lt;style type=&quot;text/css&quot;&amp;gt;
        h1 {
            margin-left: -300px;
            margin-top: 50px
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

&amp;lt;h1&amp;gt;Confirm Access&amp;lt;/h1&amp;gt;

&amp;lt;div id=&quot;content&quot;&amp;gt;

    &amp;lt;% if (session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null &amp;amp;&amp;amp; 
                 !(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof UnapprovedClientAuthenticationException)) { %&amp;gt;
    &amp;lt;div class=&quot;error&quot;&amp;gt;
        &amp;lt;h2&amp;gt;Woops!&amp;lt;/h2&amp;gt;

        &amp;lt;p&amp;gt;Access could not be granted.
            (&amp;lt;%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %&amp;gt;)&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;% } %&amp;gt;
    &amp;lt;c:remove scope=&quot;session&quot; var=&quot;SPRING_SECURITY_LAST_EXCEPTION&quot;/&amp;gt;

    &amp;lt;authz:authorize ifAnyGranted=&quot;ROLE_USER,ROLE_ADMIN&quot;&amp;gt;
        &amp;lt;h2&amp;gt;Please Confirm&amp;lt;/h2&amp;gt;

        &amp;lt;p&amp;gt;You hereby authorize &quot;&amp;lt;c:out value=&quot;${client.clientId}&quot; escapeXml=&quot;true&quot;/&amp;gt;&quot; to access your protected resources.&amp;lt;/p&amp;gt;

        &amp;lt;form id=&quot;confirmationForm&quot; name=&quot;confirmationForm&quot;
              action=&quot;&amp;lt;%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%&amp;gt;&quot; method=&quot;POST&quot;&amp;gt;
            &amp;lt;input name=&quot;&amp;lt;%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%&amp;gt;&quot;
                   value=&quot;&amp;lt;%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%&amp;gt;&quot; type=&quot;hidden&quot;/&amp;gt;
            &amp;lt;label&amp;gt;&amp;lt;input name=&quot;authorize&quot; value=&quot;Authorize&quot; type=&quot;submit&quot;&amp;gt;&amp;lt;/label&amp;gt;
        &amp;lt;/form&amp;gt;
        &amp;lt;form id=&quot;denialForm&quot; name=&quot;denialForm&quot;
              action=&quot;&amp;lt;%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%&amp;gt;&quot; method=&quot;POST&quot;&amp;gt;
            &amp;lt;input name=&quot;&amp;lt;%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%&amp;gt;&quot;
                   value=&quot;not_&amp;lt;%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%&amp;gt;&quot; type=&quot;hidden&quot;/&amp;gt;
            &amp;lt;label&amp;gt;&amp;lt;input name=&quot;deny&quot; value=&quot;Deny&quot; type=&quot;submit&quot;&amp;gt;&amp;lt;/label&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/authz:authorize&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Finally, I needed to create &lt;em&gt;src/main/webapp/login.jsp&lt;/em&gt; to allow users to login.&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;%@ page language=&quot;java&quot; pageEncoding=&quot;UTF-8&quot; contentType=&quot;text/html;charset=utf-8&quot; %&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; prefix=&quot;fmt&quot; %&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&amp;gt;

&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Login&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; media=&quot;all&quot;
          href=&quot;http://demo.appfuse.org/appfuse-struts/styles/simplicity/theme.css&quot;/&amp;gt;
    &amp;lt;style type=&quot;text/css&quot;&amp;gt;
        h1 {
            margin-left: -300px;
            margin-top: 50px
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Login&amp;lt;/h1&amp;gt;

&amp;lt;form method=&quot;post&quot; id=&quot;loginForm&quot; action=&quot;&amp;lt;c:url value=&apos;/j_security_check&apos;/&amp;gt;&quot;&amp;gt;
    &amp;lt;fieldset style=&quot;padding-bottom: 0&quot;&amp;gt;
        &amp;lt;ul&amp;gt;
            &amp;lt;c:if test=&quot;${param.error != null}&quot;&amp;gt;
                &amp;lt;li class=&quot;error&quot;&amp;gt;
                    ${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}
                &amp;lt;/li&amp;gt;
            &amp;lt;/c:if&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;label for=&quot;j_username&quot; class=&quot;required desc&quot;&amp;gt;
                    Username &amp;lt;span class=&quot;req&quot;&amp;gt;*&amp;lt;/span&amp;gt;
                &amp;lt;/label&amp;gt;
                &amp;lt;input type=&quot;text&quot; class=&quot;text medium&quot; name=&quot;j_username&quot;
                       id=&quot;j_username&quot; tabindex=&quot;1&quot;/&amp;gt;
            &amp;lt;/li&amp;gt;

            &amp;lt;li&amp;gt;
                &amp;lt;label for=&quot;j_password&quot; class=&quot;required desc&quot;&amp;gt;
                    Password &amp;lt;span class=&quot;req&quot;&amp;gt;*&amp;lt;/span&amp;gt;
                &amp;lt;/label&amp;gt;
                &amp;lt;input type=&quot;password&quot; class=&quot;text medium&quot; name=&quot;j_password&quot;
                       id=&quot;j_password&quot; tabindex=&quot;2&quot;/&amp;gt;
            &amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;input type=&quot;submit&quot; class=&quot;button&quot; name=&quot;login&quot; value=&quot;Login&quot;
                       tabindex=&quot;3&quot;/&amp;gt;
            &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/fieldset&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;All the changes described in the above section are necessary to implement OAuth if you create a project with AppFuse WS 2.1. It may seem like a lot of code, but I was able to copy/paste and get it all working in an app in under 5 minutes. Hopefully you can do the same. I&apos;m also considering adding it by default to the next version of AppFuse. Now let&apos;s look at integrating OAuth into a client to authenticate and retrieve data from this application.
&lt;/p&gt;
&lt;p id=&quot;oauth-client&quot;&gt;&lt;strong&gt;Authenticate and Retrieve Data with Client&lt;/strong&gt;&lt;br/&gt;
I originally thought my &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_oauth_with_gwt&quot;&gt;GWT OAuth&lt;/a&gt; application would provide a nice client. However, after 30 minutes of trying to get GWT 1.7.1 and the GWT Maven plugin (1.1) working with my 64-bit Java 6 JDK on OS X, I gave up. So I opted to use the &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;Ajax Login&lt;/a&gt; application I&apos;ve been using in my recent security tutorials.
&lt;/p&gt;
&lt;p&gt;In this example, I used OAuth2RestTemplate from Spring Security OAuth. While this works, and works well, I&apos;d still like to get things working with GWT (or jQuery) to demonstrate how to do it from a pure client-side perspective.
&lt;/p&gt;
&lt;p&gt;To begin, I got the latest source of Ajax Login from GitHub (as of this morning) and made some changes. First of all, I added the Spring Security OAuth dependencies to pom.xml:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;repository&amp;gt;
    &amp;lt;id&amp;gt;spring-milestone&amp;lt;/id&amp;gt;
    &amp;lt;url&amp;gt;http://s3.amazonaws.com/maven.springframework.org/milestone&amp;lt;/url&amp;gt;
&amp;lt;/repository&amp;gt;
...
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.security.oauth&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-security-oauth&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0.0.M3&amp;lt;/version&amp;gt;
    &amp;lt;exclusions&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-beans&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-context&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-core&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
    &amp;lt;/exclusions&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Then I modified &lt;em&gt;src/main/webapp/WEB-INF/security.xml&lt;/em&gt; and added an OAuth Token Service and defined the location of the OAuth server.&lt;/p&gt; 
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/security&quot;
             xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
             xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
             xmlns:oauth=&quot;http://www.springframework.org/schema/security/oauth2&quot;
             xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
              http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
              http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd&quot;&amp;gt;

...
    &amp;lt;oauth:client token-services-ref=&quot;oauth2TokenServices&quot;/&amp;gt;

    &amp;lt;beans:bean id=&quot;oauth2TokenServices&quot;
                class=&quot;org.springframework.security.oauth2.consumer.token.InMemoryOAuth2ClientTokenServices&quot;/&amp;gt;

    &amp;lt;oauth:resource id=&quot;appfuse&quot; type=&quot;authorization_code&quot; clientId=&quot;ajax-login&quot;
                    accessTokenUri=&quot;http://localhost:9000/appfuse-oauth/oauth/authorize&quot;
                    userAuthorizationUri=&quot;http://localhost:9000/appfuse-oauth/oauth/user/authorize&quot;/&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I created a Controller that uses OAuth2RestTemplate to make the request and get the data from the AppFuse OAuth application&apos;s API. I created &lt;em&gt;src/main/java/org/appfuse/examples/webapp/oauth/UsersApiController.java&lt;/em&gt; and filled it with the following code:&lt;/p&gt;
&lt;pre class=&quot;brush: java; auto-links: false&quot;&gt;
package org.appfuse.examples.webapp.oauth;

import org.appfuse.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.consumer.*;
import org.springframework.security.oauth2.consumer.token.OAuth2ClientTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;

@RequestMapping(&quot;/appfuse/users&quot;)
@Controller
public class UsersApiController {

    private OAuth2RestTemplate apiRestTemplate;
    @Autowired
    private OAuth2ClientTokenServices tokenServices;

    private static final String REMOTE_DATA_URL = &quot;http://localhost:9000/appfuse-oauth/api/users&quot;;

    @Autowired
    public UsersApiController(OAuth2ProtectedResourceDetails resourceDetails) {
        this.apiRestTemplate = new OAuth2RestTemplate(resourceDetails);
    }

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List&amp;lt;User&amp;gt; getUsers() {
        try {
            List users = apiRestTemplate.getForObject(REMOTE_DATA_URL, List.class);
            return new ArrayList&amp;lt;User&amp;gt;(users);
        } catch (InvalidTokenException badToken) {
            //we&apos;ve got a bad token, probably because it&apos;s expired.
            OAuth2ProtectedResourceDetails resource = apiRestTemplate.getResource();
            OAuth2SecurityContext context = OAuth2SecurityContextHolder.getContext();
            if (context != null) {
                // this one is kind of a hack for this application
                // the problem is that the sparklr photos page doesn&apos;t remove the &apos;code=&apos; request parameter.
                ((OAuth2SecurityContextImpl) context).setVerificationCode(null);
            }
            //clear any stored access tokens...
            tokenServices.removeToken(SecurityContextHolder.getContext().getAuthentication(), resource);
            //go get a new access token...
            throw new OAuth2AccessTokenRequiredException(resource);
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;At this point, I thought everything would work and I spent quite some time banging my head against the wall when it didn&apos;t. As I was composing an email to the Enunciate users mailing list, I realized the issue. It appeared to be working, but from the server side, and the redirect back to the client was not happening. The Ajax Login app uses UrlRewriteFilter (for pretty URLs) to redirect from /app/* to /$1 and this redirect was losing the code parameter in the URL. 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;rule&amp;gt;
    &amp;lt;from&amp;gt;/app/**&amp;lt;/from&amp;gt;
    &amp;lt;to last=&quot;true&quot; type=&quot;redirect&quot;&amp;gt;%{context-path}/$1&amp;lt;/to&amp;gt;
&amp;lt;/rule&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To fix this, I added use-query-string=&quot;true&quot; to the root element in &lt;em&gt;src/main/webapp/WEB-INF/urlrewrite.xml&lt;/em&gt;:
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;urlrewrite default-match-type=&quot;wildcard&quot; use-query-string=&quot;true&quot;&gt;
&lt;/pre&gt;
&lt;p&gt;After making all these changes, I ran &lt;strong&gt;mvn jetty:run&lt;/strong&gt; on both apps and opened &lt;a href=&quot;http://localhost:8080/appfuse/users&quot;&gt;http://localhost:8080/appfuse/users&lt;/a&gt; in my browser. It all worked and a smile crept across my face. I&apos;ve checked in the client changes into &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;ajax-login on GitHub&lt;/a&gt; and the appfuse-oauth example into &lt;a href=&quot;http://code.google.com/p/appfuse-demos/&quot;&gt;AppFuse Demos on Google Code&lt;/a&gt;. If you&apos;d like to see this example in action, I&apos;d encourage you to checkout both projects and let me know if you find any issues.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/java_web_application_security_part1</id>
        <title type="html">Java Web Application Security - Part II: Spring Security Login Demo</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/java_web_application_security_part1"/>
        <published>2011-05-13T09:20:51-06:00</published>
        <updated>2015-07-07T01:36:15-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="presentation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ujug" scheme="http://roller.apache.org/ns/tags/" />
        <category term="web" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last week, I wrote &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part&quot;&gt;a tutorial on how to implement Security in Java EE 6&lt;/a&gt;. This week, I&apos;d like to show you how to implement the same features using &lt;a href=&quot;http://static.springsource.org/spring-security/site/&quot;&gt;Spring Security&lt;/a&gt;. Before I begin, I&apos;d like to explain my reason for writing this article.
&lt;/p&gt;
&lt;p&gt;Last month, I presented a talk on Java Web Application Security at the &lt;a href=&quot;http://ujug.org&quot;&gt;Utah JUG&lt;/a&gt; (UJUG). As part of that presentation, I did a number of demos about how to implement security with Java EE 6, Spring Security and Apache Shiro. I told the audience that I would post the presentation and was planning on recording screencasts of the various demos so the online version of the presentation would make more sense. 
&lt;/p&gt;
&lt;p&gt;
Today, I&apos;ve finished the second screencast showing how to implement security with Spring Security. Below is the presentation (with the screencast embedded on slide 16) as well as a step-by-step tutorial.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/o4wwswiZck6bKS&quot; width=&quot;510&quot; height=&quot;420&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen&gt; &lt;/iframe&gt;
&lt;br/&gt;
&lt;div style=&quot;font-size: .9em; text-align: left&quot;&gt;* You can also &lt;a href=&quot;http://www.youtube.com/watch?v=K5Hf-2bKYu8&quot;&gt;watch the screencast on YouTube&lt;/a&gt; or &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/Java_Web_Application_Security_UJUG2011.pdf&quot;&gt;download the presentation PDF&lt;/a&gt;.&lt;/div&gt;
&lt;/p&gt;

&lt;p id=&quot;springsecurity-login-tutorial&quot;&gt;&lt;strong&gt;Spring Security Login Tutorial&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#download-run&quot;&gt;Download and Run the Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#secure-basic&quot;&gt;Implement Basic Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ssl&quot;&gt;Force SSL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#form-authentication&quot;&gt;Implement Form-based Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#remember-me&quot;&gt;Add Remember Me&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#jdbc&quot;&gt;Store Users in a Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;download-run&quot;&gt;&lt;strong&gt;Download and Run the Application&lt;/strong&gt;&lt;br/&gt;

To begin, &lt;a href=&quot;http://static.raibledesigns.com/downloads/ajax-login-springsecurity-tutorial-1.0.zip&quot;&gt;download the application&lt;/a&gt; you&apos;ll be implementing security in. This app is a stripped-down version of the Ajax Login application I wrote for my article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery&quot;&gt;Implementing Ajax Authentication using jQuery, Spring Security and HTTPS&lt;/a&gt;. You&apos;ll need Java 6 and Maven installed to run the app. Run it using &lt;strong&gt;mvn jetty:run&lt;/strong&gt; and open &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt; in your browser. You&apos;ll see it&apos;s a simple CRUD application for users and there&apos;s no login required to add or delete users.&lt;/p&gt;

&lt;p id=&quot;secure-basic&quot;&gt;&lt;strong&gt;Implement Basic Authentication&lt;/strong&gt;&lt;br/&gt;
  
The first step is to protect the list screen so people have to login to view users. To do this, you&apos;ll need to create a Spring context file that contains Spring Security&apos;s configuration. Create &lt;em&gt;src/main/webapp/WEB-INF/security.xml&lt;/em&gt; and populate it with the contents below:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
  &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
  &amp;lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/security&quot;
               xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
               xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
               xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd&quot;&amp;gt;

      &amp;lt;!-- New in Spring Security 3.1 --&amp;gt;
      &amp;lt;!-- &amp;lt;http pattern=&quot;/css/**&quot; security=&quot;none&quot;/&amp;gt; --&amp;gt;

      &amp;lt;http auto-config=&quot;true&quot;&amp;gt;
          &amp;lt;intercept-url pattern=&quot;/app/users&quot; access=&quot;ROLE_USER,ROLE_ADMIN&quot;/&amp;gt;
          &amp;lt;http-basic/&amp;gt;
      &amp;lt;/http&amp;gt;

      &amp;lt;authentication-manager alias=&quot;authenticationManager&quot;&amp;gt;
          &amp;lt;authentication-provider&amp;gt;
              &amp;lt;password-encoder hash=&quot;sha&quot;/&amp;gt;
              &amp;lt;user-service&amp;gt;
                  &amp;lt;user name=&quot;user&quot; password=&quot;12dea96fec20593566ab75692c9949596833adc9&quot; authorities=&quot;ROLE_USER&quot;/&amp;gt;
                  &amp;lt;user name=&quot;admin&quot; password=&quot;d033e22ae348aeb5660fc2140aec35850c4da997&quot; authorities=&quot;ROLE_ADMIN&quot;/&amp;gt;
              &amp;lt;/user-service&amp;gt;
          &amp;lt;/authentication-provider&amp;gt;
      &amp;lt;/authentication-manager&amp;gt;

      &amp;lt;!-- Override userSecurityAdvice bean in appfuse-service to allow any role to update a user. --&amp;gt;
      &amp;lt;beans:bean id=&quot;userSecurityAdvice&quot; class=&quot;org.appfuse.examples.webapp.security.UserSecurityAdvice&quot;/&amp;gt;
  &amp;lt;/beans:beans&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
The last bean, userSecurityAdvice, is an aspect that&apos;s needed to override some behavior in &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;. You won&apos;t need this normally when implementing Spring Security. 
&lt;/p&gt;
&lt;p&gt;Next, open &lt;em&gt;src/main/webapp/WEB-INF/web.xml&lt;/em&gt; and add Spring&apos;s DelegatingFilterProxy:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;filter&amp;gt;
    &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;filter-class&amp;gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&amp;gt;
    &amp;lt;init-param&amp;gt;
        &amp;lt;param-name&amp;gt;targetBeanName&amp;lt;/param-name&amp;gt;
        &amp;lt;param-value&amp;gt;springSecurityFilterChain&amp;lt;/param-value&amp;gt;
    &amp;lt;/init-param&amp;gt;
&amp;lt;/filter&amp;gt;
&lt;/pre&gt;
&lt;p&gt;And add its filter-mapping just after the rewriteFilter in the filter-mappings section (order is important!):
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;rewriteFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
    &amp;lt;dispatcher&amp;gt;REQUEST&amp;lt;/dispatcher&amp;gt;
    &amp;lt;dispatcher&amp;gt;FORWARD&amp;lt;/dispatcher&amp;gt;
    &amp;lt;dispatcher&amp;gt;INCLUDE&amp;lt;/dispatcher&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
You don&apos;t need to add any dependencies in your pom.xml is because this project depends on AppFuse, which already contains these dependencies.
&lt;/p&gt;
&lt;p&gt;
At this point, if you restart Jetty (Ctrl+C and jetty:run again), you should be prompted to login when you click on the &quot;Users&quot; tab. Enter admin/admin to login. Spring Security is a bit easier to configure than Java EE 6 out-of-the-box, mostly because it doesn&apos;t require you to configure your container.
&lt;/p&gt;
&lt;p&gt;After logging in, you can try to logout by clicking the &quot;Logout&quot; link in the top-right corner. This calls a LogoutController with the following code that logs the user out.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public void logout(HttpServletResponse response) throws ServletException, IOException {
    request.getSession().invalidate();
    response.sendRedirect(request.getContextPath()); 
}
&lt;/pre&gt; 
&lt;p class=&quot;quote&quot; style=&quot;font-style: italic; color: #666&quot;&gt;NOTE: Spring Security has a way to configure &quot;logout&quot; to match a URL and get rid of a class like LogoutController. Since it was already in the project, I don&apos;t cover that in this tutorial.&lt;/p&gt;
&lt;p&gt;You&apos;ll notice that clicking this link doesn&apos;t log you out, even though the session is invalidated. The only way to logout with basic authentication is to close the browser. In order to get the ability to logout, as well as to have more control over the look-and-feel of the login, you can implement form-based authentication.
  Before you implement form-based authentication, I&apos;d like to show you how easy it is to force SSL with Spring Security.
&lt;/p&gt;
&lt;p id=&quot;ssl&quot;&gt;&lt;strong&gt;Force SSL&lt;/strong&gt;&lt;br/&gt;
Spring Security allows you to switch between secure (https) and non-secure (http) protocols using a simple &lt;em&gt;requires-channel&lt;/em&gt; attribute on the &amp;lt;intercept-url&amp;gt; element. Possible values are &quot;http&quot;, &quot;https&quot; and &quot;any&quot;. Add &lt;em&gt;requires-channel=&quot;https&quot;&lt;/em&gt; to your security.xml file:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;intercept-url pattern=&quot;/app/users&quot; access=&quot;ROLE_USER,ROLE_ADMIN&quot; requires-channel=&quot;https&quot;/&amp;gt;
&lt;/pre&gt;
&lt;p&gt;In order for this to work, you have to configure Jetty to listen on an SSL port. Add the following just after the jetty-maven-plugin&apos;s &amp;lt;/webAppConfig&amp;gt; element in your pom.xml:
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;connectors&amp;gt;
    &amp;lt;connector implementation=&quot;org.eclipse.jetty.server.nio.SelectChannelConnector&quot;&amp;gt;
        &amp;lt;forwarded&amp;gt;true&amp;lt;/forwarded&amp;gt;
        &amp;lt;port&amp;gt;8080&amp;lt;/port&amp;gt;
    &amp;lt;/connector&amp;gt;
    &amp;lt;connector implementation=&quot;org.eclipse.jetty.server.ssl.SslSelectChannelConnector&quot;&amp;gt;
        &amp;lt;forwarded&amp;gt;true&amp;lt;/forwarded&amp;gt;
        &amp;lt;port&amp;gt;8443&amp;lt;/port&amp;gt;
        &amp;lt;maxIdleTime&amp;gt;60000&amp;lt;/maxIdleTime&amp;gt;
        &amp;lt;keystore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/keystore&amp;gt;
        &amp;lt;password&amp;gt;appfuse&amp;lt;/password&amp;gt;
        &amp;lt;keyPassword&amp;gt;appfuse&amp;lt;/keyPassword&amp;gt;
    &amp;lt;/connector&amp;gt;
&amp;lt;/connectors&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The keystore must be generated for Jetty to start successfully, so add the keytool-maven-plugin just above the jetty-maven-plugin in pom.xml.
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;keytool-maven-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;
    &amp;lt;executions&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;
            &amp;lt;id&amp;gt;clean&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;clean&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;
            &amp;lt;id&amp;gt;genkey&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;genkey&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
    &amp;lt;/executions&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;keystore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/keystore&amp;gt;
        &amp;lt;dname&amp;gt;cn=localhost&amp;lt;/dname&amp;gt;
        &amp;lt;keypass&amp;gt;appfuse&amp;lt;/keypass&amp;gt;
        &amp;lt;storepass&amp;gt;appfuse&amp;lt;/storepass&amp;gt;
        &amp;lt;alias&amp;gt;appfuse&amp;lt;/alias&amp;gt;
        &amp;lt;keyalg&amp;gt;RSA&amp;lt;/keyalg&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now if you restart Jetty, go to &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt; and click on the &quot;Users&quot; tab, you&apos;ll be prompted to accept the Untrusted Certificate and then redirected to https://localhost:8443/users after logging in. This is an 
  improvement on Java EE&apos;s user-data-constraint for two reasons:
  &lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;You can switch between http and https protocols. With Java EE, you can only force https. You have to write a custom filter to switch back to http.&lt;/li&gt;
    &lt;li&gt;Redirecting to https actually works. With Java EE (on Jetty at least), a 403 is returned instead of redirecting the request.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;Now let&apos;s look at how to have more control over the look-and-feel of the login screen, as well as how to make logout work with form-based authentication.&lt;/p&gt;
&lt;p id=&quot;form-authentication&quot;&gt;&lt;strong&gt;Implement Form-based Authentication&lt;/strong&gt;&lt;br/&gt;
To change from basic to form-based authentication, you simply have to add a &amp;lt;form-login&amp;gt; element in security.xml&apos;s &amp;lt;http&amp;gt; element:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;http auto-config=&quot;true&quot;&amp;gt;
    &amp;lt;intercept-url pattern=&quot;/app/users&quot; access=&quot;ROLE_USER,ROLE_ADMIN&quot; requires-channel=&quot;https&quot;/&amp;gt;
    &amp;lt;form-login login-page=&quot;/login&quot; authentication-failure-url=&quot;/login?error=true&quot;
                login-processing-url=&quot;/j_security_check&quot;/&amp;gt;
    &amp;lt;http-basic/&amp;gt;
&amp;lt;/http&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
  You can leave the &amp;lt;http-basic&amp;gt; element since Spring Security is smart enough to serve up the form for browsers and use Basic Authentication for clients such as web services.
  
  The login.jsp page (that /login forwards to) already exists in the project, in the &lt;em&gt;src/main/webapp&lt;/em&gt; directory. The forwarding is done by the &lt;a href=&quot;http://www.tuckey.org/urlrewrite/&quot;&gt;UrlRewriteFilter&lt;/a&gt; with the following configuration in &lt;em&gt;src/main/webapp/WEB-INF/urlrewrite.xml&lt;/em&gt;. 
  
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;rule&amp;gt;
    &amp;lt;from&amp;gt;/login&amp;lt;/from&amp;gt;
    &amp;lt;to&amp;gt;/login.jsp&amp;lt;/to&amp;gt;
&amp;lt;/rule&amp;gt;
&lt;/pre&gt;  
&lt;p&gt;
  This JSP has 3 important elements: 1) a form that submits to &quot;/j_security_check&quot;, 2) an input element named &quot;j_username&quot; and 3) an input element named &quot;j_password&quot;. If you restart Jetty, you&apos;ll now be prompted to login with this JSP instead of the basic authentication dialog.
&lt;/p&gt;
&lt;p id=&quot;remember-me&quot;&gt;&lt;strong&gt;Add Remember Me&lt;/strong&gt;&lt;br/&gt;
Remember Me is a feature you see in many web applications today. It&apos;s usually a checkbox on the login form that allows you to auto-login the next time you visit a site. This feature doesn&apos;t exist in Java EE security, but it does exist in Spring Security. To enable it, add the following just below &amp;lt;form-login&amp;gt; in security.xml:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;remember-me user-service-ref=&quot;userDao&quot; key=&quot;e37f4b31-0c45-11dd-bd0b-0800200c9a66&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;Next, open &lt;em&gt;src/main/webapp/login.jsp&lt;/em&gt; and change the name of the &quot;remember me&quot; checkbox to be &lt;strong&gt;_spring_security_remember_me&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;input type=&quot;checkbox&quot; name=&quot;_spring_security_remember_me&quot; id=&quot;rememberMe&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;After making these changes, you should be able to restart Jetty, go to &lt;a href=&quot;http://localhost:8080/users&quot;&gt;http://localhost:8080/users&lt;/a&gt;, enter admin/adminjdbc, check the Remember Me checkbox and login. Then close your browser, and repeat the process. This time, you won&apos;t be prompted to login. For more information on this feature, see &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.0.x/reference/remember-me.html&quot;&gt;Spring Security&apos;s Remember Me documentation&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
While storing usernames and passwords in a file is convenient for demos, it&apos;s not very real-world-ish. The next section shows you how to configure Spring Security to use a database for its user store.
&lt;/p&gt;
&lt;p id=&quot;jdbc&quot;&gt;&lt;strong&gt;Store Users in a Database&lt;/strong&gt;&lt;br/&gt;
To store your users in a database instead of file, you&apos;ll need to add a &lt;em&gt;user-service-ref&lt;/em&gt; attribute to the &amp;lt;authentication-provider&amp;gt; element. You can also delete the &amp;lt;user-service&amp;gt; element. 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;authentication-manager alias=&quot;authenticationManager&quot;&amp;gt;
    &amp;lt;authentication-provider user-service-ref=&quot;userDao&quot;&amp;gt;
        &amp;lt;password-encoder hash=&quot;sha&quot;/&amp;gt;
    &amp;lt;/authentication-provider&amp;gt;
&amp;lt;/authentication-manager&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The &quot;userDao&quot; bean is provided by AppFuse and its &lt;a href=&quot;http://static.appfuse.org/appfuse-data/appfuse-hibernate/xref/org/appfuse/dao/hibernate/UserDaoHibernate.html&quot;&gt;UserDaoHibernate.java&lt;/a&gt; class. This 
  class implements Spring Security&apos;s &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/core/userdetails/UserDetailsService.html&quot;&gt;UserDetailsService&lt;/a&gt; interface.
  With Java EE, I had to configure a database connection and make sure the JDBC Driver was in my container&apos;s classpath. With Spring Security, you can talk to the database you already have configured in your application.
&lt;/p&gt;
&lt;div class=&quot;quote&quot; style=&quot;margin-left: 0; color: #666&quot;&gt;Of course, you could do this with Java EE too. One thing I neglected to show in my last tutorial was that 1) the app uses H2 and 2) I had to configure Java EE&apos;s database to be MySQL. This was because when I tried to access my H2 instance, I got an error about two threads trying to access it at once.
&lt;pre style=&quot;margin-top: 10px; margin-bottom: 0&quot;&gt;
2011-05-13 08:47:29.081:WARN::UserRealm Java EE Login could not connect to database; will try later
org.h2.jdbc.JdbcSQLException: Database may be already in use: &quot;Locked by another process&quot;. 
        Possible solutions: close all other connection(s); use the server mode [90020-154]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:327)
	at org.h2.message.DbException.get(DbException.java:167)
	at org.h2.message.DbException.get(DbException.java:144)
	at org.h2.store.FileLock.getExceptionAlreadyInUse(FileLock.java:443)
	at org.h2.store.FileLock.lockFile(FileLock.java:338)
	at org.h2.store.FileLock.lock(FileLock.java:134)
	at org.h2.engine.Database.open(Database.java:535)
	at org.h2.engine.Database.openDatabase(Database.java:218)
&lt;/pre&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;!-- Figure out exact error from H2 --&gt;
&lt;p&gt;The password for the &quot;admin&quot; user is configured in &lt;em&gt;src/test/resources/sample-data.xml&lt;/em&gt; and it&apos;s loaded by &lt;a href=&quot;http://dbunit.sourceforge.net/&quot;&gt;DbUnit&lt;/a&gt; before the application starts. 
  You can view your pom.xml and the dbunit-maven-plugin&apos;s configuration if you&apos;re interested in learning how this is done. The password is currently configured to &quot;adminjdbc&quot;, but you can reset it by
  &lt;a href=&quot;http://darrenfauth.com/generators/sha1&quot;&gt;generating a new password&lt;/a&gt; and modifying sample-data.xml.
&lt;/p&gt;
&lt;p&gt;Now if you restart Jetty, you should be able to login with admin/adminjdbc and view the list of users.
&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
In this tutorial, you learned how to implement authentication using Spring Security 3.0.5. In addition to the basic XML configuration, Spring Security also provides a AOP support and annotations you can use to secure methods. It also has many more features than standard Java EE Security. In my opinion, it&apos;s the most mature security framework we have in Java today. Currently, I think its &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity.html&quot;&gt;reference documentation&lt;/a&gt; is the best place to learn more.
&lt;/p&gt;
There are a few limitations I found with Spring Security:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;The authentication mechanism (file, database, ldap, etc.) is contained in the WAR&lt;/li&gt;
&lt;li&gt;Securing methods only works on Spring beans&lt;/li&gt;
&lt;li&gt;Remember Me doesn&apos;t work in my screencast (because I forgot to rename the checkbox in login.jsp)&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;&lt;p&gt;Of course, you can configure Spring to load its configuration from outside the WAR (e.g. a file or JNDI), but it&apos;s not as easy as including the configuration in your app.
  &lt;/p&gt;
  &lt;p&gt;In the next couple weeks, I&apos;ll post &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part2&quot;&gt;Part III&lt;/a&gt; of this series, where I&apos;ll show you how to implement this same set of features using Apache Shiro. In the meantime, please let me know if you have any questions.
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 5px; color: #666&quot;&gt;
I created the screencasts with &lt;a href=&quot;http://www.techsmith.com/camtasia/&quot; style=&quot;color: #666&quot;&gt;Camtasia&lt;/a&gt;. For small screens, and embedding in the presentation, I created it at 50% and used the SmartFocus feature to zoom in and out during the demo. For larger screens, I published &lt;a href=&quot;http://www.youtube.com/watch?v=poc5dyImbig&quot; style=&quot;color: #666&quot;&gt;another screencast at 100%, in HD&lt;/a&gt;. If you have a preference for which screencast is better, I&apos;d love to hear about it.&lt;/p&gt;
&lt;p&gt;</content>
    </entry>
</feed>

