<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://raibledesigns.com/roller-ui/styles/rss.xsl" media="screen"?><rss version="2.0" 
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:atom="http://www.w3.org/2005/Atom" >
<channel>
  <title>Raible Designs</title>
  <link>https://raibledesigns.com/rd/</link>
      <atom:link rel="self" type="application/rss+xml" href="https://raibledesigns.com/rd/feed/entries/rss?cat=%2FThe+Web" />
    <description>Raible Designs is an Enterprise Open Source Consulting company. We specialize in UI and Full Stack Architectures using HTML5, CSS, JavaScript and Java. We love HTML5, Angular, Bootstrap, Spring Boot, and especially JHipster.</description>
  <language>en-us</language>
  <copyright>Copyright 2026</copyright>
  <lastBuildDate>Mon, 30 Mar 2026 03:31:45 -0600</lastBuildDate>
  <generator>Apache Roller (incubating) 5.0.3 (1388864191739:dave)</generator>
        <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/getting_started_with_angular_cli</guid>
    <title>Getting Started + Testing with Angular CLI and Angular 2 (RC5)</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/getting_started_with_angular_cli</link>
        <pubDate>Tue, 23 Aug 2016 17:18:41 -0600</pubDate>
    <category>The Web</category>
    <category>npm</category>
    <category>jasmine</category>
    <category>angular2</category>
    <category>karma</category>
    <category>javascript</category>
    <category>typescript</category>
    <category>node</category>
    <category>angular-cli</category>
    <category>protractor</category>
    <category>asciidoctor</category>
    <atom: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;</atom:summary>        <description>&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;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/testing_angular_2_0_rc1</guid>
    <title>Testing Angular 2.0 RC1 Applications</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/testing_angular_2_0_rc1</link>
        <pubDate>Mon, 6 Jun 2016 09:57:13 -0600</pubDate>
    <category>The Web</category>
    <category>protractor</category>
    <category>asciidoctor</category>
    <category>npm</category>
    <category>angular2</category>
    <category>javascript</category>
    <category>karma</category>
    <category>git</category>
    <category>jasmine</category>
    <category>node</category>
    <atom: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;</atom:summary>        <description>&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;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1</guid>
    <title>Getting Started with Angular 2.0 RC1</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1</link>
        <pubDate>Fri, 3 Jun 2016 07:16:18 -0600</pubDate>
    <category>The Web</category>
    <category>asciidoctor</category>
    <category>npm</category>
    <category>javascript</category>
    <category>node</category>
    <category>git</category>
    <category>typescript</category>
    <category>angular2</category>
    <atom: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;</atom:summary>        <description>&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;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/testing_angular_2_applications</guid>
    <title>Testing Angular 2 Applications</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/testing_angular_2_applications</link>
        <pubDate>Tue, 29 Mar 2016 08:08:58 -0600</pubDate>
    <category>The Web</category>
    <category>node</category>
    <category>javascript</category>
    <category>angular2</category>
    <category>karma</category>
    <category>npm</category>
    <category>jasmine</category>
    <category>protractor</category>
    <category>asciidoctor</category>
    <category>git</category>
    <atom:summary type="html">&lt;p class=&quot;alert alert-info&quot;&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_0_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;This article is the second in a series about learning &lt;a href=&quot;https://angular.io/&quot;&gt;Angular 2&lt;/a&gt;. It describes
    how to test a simple Angular 2 application. In a previous article, &lt;a
        href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting Started with Angular 2&lt;/a&gt;, I
    showed how to develop a simple search and edit feature.
    In this tutorial, I did my best to keep the tests similar to last year&apos;s &lt;a
        href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;
        Testing AngularJS Applications&lt;/a&gt; so you can compare &lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/tree/testing&quot;&gt;
        the code between AngularJS&lt;/a&gt; and Angular 2.
&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;https://angular.github.io/protractor/&quot;&gt;Protractor&lt;/a&gt; for
                integration testing. Angular&apos;s documentation has a good &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; using
                git 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
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&quot;&gt;Getting
                Started with Angular 2&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;</atom:summary>        <description>&lt;p class=&quot;alert alert-info&quot;&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_0_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;This article is the second in a series about learning &lt;a href=&quot;https://angular.io/&quot;&gt;Angular 2&lt;/a&gt;. It describes
    how to test a simple Angular 2 application. In a previous article, &lt;a
        href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting Started with Angular 2&lt;/a&gt;, I
    showed how to develop a simple search and edit feature.
    In this tutorial, I did my best to keep the tests similar to last year&apos;s &lt;a
        href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;
        Testing AngularJS Applications&lt;/a&gt; so you can compare &lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/tree/testing&quot;&gt;
        the code between AngularJS&lt;/a&gt; and Angular 2.
&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;https://angular.github.io/protractor/&quot;&gt;Protractor&lt;/a&gt; for
                integration testing. Angular&apos;s documentation has a good &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; using
                git 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
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&quot;&gt;Getting
                Started with Angular 2&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/shared/services/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;pre class=&quot;brush: js&quot;&gt;
import {it, describe, expect, inject, fakeAsync, beforeEachProviders, tick} from &apos;angular2/testing&apos;;
import {MockBackend} from &apos;angular2/http/testing&apos;;
import {provide} from &apos;angular2/core&apos;;
import &apos;rxjs/add/operator/map&apos;;
import {Http, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions} from &apos;angular2/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 class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you run &lt;code&gt;npm test&lt;/code&gt;, you&apos;ll get a failed build from a number of unused imports. You can fix
                those 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;div class=&quot;paragraph&quot;&gt;
            &lt;p&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&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;pre class=&quot;brush: js&quot;&gt;
it(&apos;should retrieve all search results&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&gt; {
    var res:Response;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;shared/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;/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;10 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;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:Response;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;shared/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:Response;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;shared/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 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/shared/services/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;pre class=&quot;brush: js&quot;&gt;
import {provide} from &apos;angular2/core&apos;;
import {SpyObject} from &apos;angular2/testing_internal&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;

  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) {
    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;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;In this same directory, create &lt;code&gt;routes.ts&lt;/code&gt; to mock Angular&apos;s &lt;code&gt;Router&lt;/code&gt;, &lt;code&gt;RouteParams&lt;/code&gt;
                and &lt;code&gt;RouterProvider&lt;/code&gt;.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {provide} from &apos;angular2/core&apos;;
import {
  ComponentInstruction,
  Router,
  RouteParams
} from &apos;angular2/router&apos;;
import {ResolvedInstruction} from &apos;angular2/src/router/instruction&apos;;
import {SpyObject} from &apos;angular2/testing_internal&apos;;

export class MockRouteParams extends SpyObject {
  private ROUTE_PARAMS = {};

  constructor() { super(RouteParams); }

  set(key: string, value: string) {
    this.ROUTE_PARAMS[key] = value;
  }

  get(key: string) {
    return this.ROUTE_PARAMS[key];
  }
}

export class MockRouter extends SpyObject {
  constructor() { super(Router); }
  isRouteActive(s) { return true; }
  generate(s) {
    return new ResolvedInstruction(new ComponentInstruction(&apos;detail&apos;, [], null, null, true, &apos;0&apos;), null, {});
  }
}

export class MockRouterProvider {
  mockRouter: MockRouter = new MockRouter();
  mockRouteParams: MockRouteParams = new MockRouteParams();

  setRouteParam(key: string, value: any) {
    this.mockRouteParams.set(key, value);
  }

  getProviders(): Array&amp;lt;any&gt; {
    return [
      provide(Router, {useValue: this.mockRouter}),
      provide(RouteParams, {useValue: this.mockRouteParams}),
    ];
  }
}
&lt;/pre&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;pre class=&quot;brush: js&quot;&gt;
import {
  it,
  describe,
  expect,
  injectAsync,
  beforeEachProviders,
  TestComponentBuilder,
} from &apos;angular2/testing&apos;;

import {MockRouterProvider} from &apos;../../shared/services/mocks/routes&apos;;
import {MockSearchService} from &apos;../../shared/services/mocks/search.service&apos;;

import {SearchComponent} from &apos;./search.component&apos;;

export function main() {
  describe(&apos;Search component&apos;, () =&gt; {
    var mockSearchService:MockSearchService;
    var mockRouterProvider:MockRouterProvider;

    beforeEachProviders(() =&gt; {
      mockSearchService = new MockSearchService();
      mockRouterProvider = new MockRouterProvider();

      return [
        mockSearchService.getProviders(), mockRouterProvider.getProviders()
      ];
    });
  });
}
&lt;/pre&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;pre class=&quot;brush: js&quot;&gt;
it(&apos;should search when a term is set and search() is called&apos;, injectAsync([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;, injectAsync([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
  mockRouterProvider.setRouteParam(&apos;term&apos;, &apos;peyton&apos;);
  return tcb.createAsync(SearchComponent).then((fixture) =&gt; {
    fixture.detectChanges();
    expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;peyton&apos;);
  });
}));
&lt;/pre&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;pre class=&quot;brush: js&quot;&gt;
import {
  it,
  describe,
  expect,
  injectAsync,
  beforeEachProviders,
  TestComponentBuilder,
} from &apos;angular2/testing&apos;;

import {MockRouterProvider} from &apos;../../shared/services/mocks/routes&apos;;
import {MockSearchService} from &apos;../../shared/services/mocks/search.service&apos;;

import {EditComponent} from &apos;./edit.component&apos;;

export function main() {
  describe(&apos;Edit component&apos;, () =&gt; {
    var mockSearchService:MockSearchService;
    var mockRouterProvider:MockRouterProvider;

    beforeEachProviders(() =&gt; {
      mockSearchService = new MockSearchService();
      mockRouterProvider = new MockRouterProvider();

      return [
        mockSearchService.getProviders(), mockRouterProvider.getProviders()
      ];
    });

    it(&apos;should fetch a single record&apos;, injectAsync([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
      mockRouterProvider.setRouteParam(&apos;id&apos;, &apos;1&apos;);
      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 class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should see &quot;&lt;span style=&quot;color: green&quot;&gt;? 20 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://farm2.staticflickr.com/1614/25706918470_49c4b4fd22_c.jpg&quot;
                   title=&quot;Protractor nav test error&quot; rel=&quot;lightbox[testing-angular2-applications]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/25706918470/in/album-72157666324227025/&quot;&gt;&lt;img
                    src=&quot;https://farm2.staticflickr.com/1614/25706918470_49c4b4fd22_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;pre class=&quot;brush: js&quot;&gt;
it(&apos;should have correct nav text for About&apos;, () =&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 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;pre class=&quot;brush: js&quot;&gt;
it(&apos;should have correct nav text for About&apos;, () =&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;, () =&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 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.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;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 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.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;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;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Run &lt;code&gt;npm run 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;/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://farm2.staticflickr.com/1558/25706917370_e88e4a6d0f_c.jpg&quot;
                       title=&quot;Protractor success&quot; rel=&quot;lightbox[testing-angular2-applications]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25706917370/in/album-72157666324227025/&quot;&gt;&lt;img
                        src=&quot;https://farm2.staticflickr.com/1558/25706917370_e88e4a6d0f_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;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%2FTESTING.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 TESTING.adoc&lt;/code&gt; to produce a &lt;code&gt;TESTING.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 testing Angular 2 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://farm2.staticflickr.com/1547/25706915830_f764f63717_c.jpg&quot; title=&quot;Test Coverage&quot;
                   rel=&quot;lightbox[testing-angular2-applications]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/25706915830/in/album-72157666324227025/&quot;&gt;&lt;img
                    src=&quot;https://farm2.staticflickr.com/1547/25706915830_f764f63717_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;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/getting_started_with_angular_2</guid>
    <title>Getting Started with Angular 2</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/getting_started_with_angular_2</link>
        <pubDate>Wed, 23 Mar 2016 09:23:53 -0600</pubDate>
    <category>The Web</category>
    <category>javascript</category>
    <category>angular2</category>
    <category>typescript</category>
    <category>node</category>
    <category>git</category>
    <category>asciidoctor</category>
    <category>npm</category>
    <atom: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;</atom:summary>        <description>&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;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/how_to_implement_a_smart</guid>
    <title>How to Implement a Smart Chunking Bootstrap Carousel with AngularJS</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/how_to_implement_a_smart</link>
        <pubDate>Tue, 15 Mar 2016 09:47:30 -0600</pubDate>
    <category>The Web</category>
    <category>bootstrap</category>
    <category>angularjs</category>
    <category>javascript</category>
    <category>ui-bootstrap</category>
    <category>carousel</category>
    <category>css</category>
    <category>responsive</category>
    <atom: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;</atom:summary>        <description>&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;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/angular_summit_2015</guid>
    <title>Angular Summit 2015</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/angular_summit_2015</link>
        <pubDate>Thu, 1 Oct 2015 10:29:31 -0600</pubDate>
    <category>The Web</category>
    <category>aurelia</category>
    <category>javascript</category>
    <category>bootstrap</category>
    <category>angular2</category>
    <category>angularjs</category>
    <category>jhipster</category>
    <category>spring-boot</category>
    <category>java</category>
    <category>meteor</category>
    <category>es6</category>
            <description>&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;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/best_practices_for_using_foundation1</guid>
    <title>Best Practices for using Foundation with AngularJS Revisited</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/best_practices_for_using_foundation1</link>
        <pubDate>Thu, 19 Feb 2015 09:38:42 -0700</pubDate>
    <category>The Web</category>
    <category>foundation</category>
    <category>css</category>
    <category>javascript</category>
    <category>foundationforapps</category>
    <category>angularjs</category>
            <description>&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;.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/how_to_reactjs_and_tooling</guid>
    <title>How to ReactJS and Tooling is Awesome at HTML5 Denver</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/how_to_reactjs_and_tooling</link>
        <pubDate>Tue, 17 Feb 2015 10:16:08 -0700</pubDate>
    <category>The Web</category>
    <category>webpack</category>
    <category>denver</category>
    <category>thingmonk</category>
    <category>npm</category>
    <category>tooling</category>
    <category>html5denver</category>
    <category>willklein</category>
    <category>html5</category>
    <category>react</category>
            <description>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!</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/best_practices_for_using_foundation</guid>
    <title>Best Practices for using Foundation with AngularJS</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/best_practices_for_using_foundation</link>
        <pubDate>Thu, 5 Feb 2015 09:21:50 -0700</pubDate>
    <category>The Web</category>
    <category>foundation</category>
    <category>css</category>
    <category>javascript</category>
    <category>angularjs</category>
    <atom:summary type="html">&lt;p&gt;
&lt;a href=&quot;http://www.htmlxprs.com/post/46/what-you-need-to-know-about-zurb-foundation-for-apps&quot;&gt;&lt;img src=&quot;/repository/images/foundation-for-apps-angularjs.jpg&quot; width=&quot;200&quot; alt=&quot;What You Need To Know About Zurb Foundation for Apps&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
I was recently tasked with doing some research to figure out the best way to use &lt;a
    href=&quot;http://foundation.zurb.com/&quot;&gt;Foundation&lt;/a&gt; with &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. Goals for
    this research included:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Identify use cases of Foundation for Sites vs Foundation for Apps and recommend when to use each.&lt;/li&gt;
    &lt;li&gt;Look at pros and cons of using AngularJS with Foundation for Sites.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&apos;m writing this blog post to get feedback from you, fellow web developers, on your experience with Foundation. Have
    you tried using Foundation for Sites with AngularJS? If so, did you experience any pain?&lt;/p&gt;
&lt;p&gt;From what I can tell, it looks like Foundation for Apps (FA) was created because folks had issues making AngularJS
    and Foundation 5 play nicely together. &lt;a href=&quot;http://zurb.com/article/1312/the-next-foundation&quot;&gt;The Next
        Foundation&lt;/a&gt; explains why FA was created. Reddit&apos;s web_design zone has quite a few &lt;a
        href=&quot;http://www.reddit.com/r/web_design/comments/26btc4/zurb_the_next_foundation_foundation_built_with/&quot;&gt;comments
        related to this article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;From there, I found a few ZURB blog posts that describe FA&apos;s three main advantages over Foundation for Sites
    (FS):&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;&lt;a href=&quot;http://zurb.com/article/1333/foundation-a-new-grid&quot;&gt;A New Grid&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://zurb.com/article/1340/foundation-for-apps-motion-ui-is-the-new-&quot;&gt;Motion UI&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://zurb.com/article/1345/design-amazing-single-page-apps-with-the-&quot;&gt;AngularJS Integration&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;http://foundation.zurb.com/forum/posts/21134-foundation-5-and-foundation-apps-differences&quot;&gt;This thread
    on the Foundation forums&lt;/a&gt; seems to indicate that FA would be good for developing applications while FS would be
    good for an intranet built on WordPress (since it&apos;s more of a website than a webapp). &lt;/p&gt;</atom:summary>        <description>&lt;p&gt;
&lt;a href=&quot;http://www.htmlxprs.com/post/46/what-you-need-to-know-about-zurb-foundation-for-apps&quot;&gt;&lt;img src=&quot;/repository/images/foundation-for-apps-angularjs.jpg&quot; width=&quot;200&quot; alt=&quot;What You Need To Know About Zurb Foundation for Apps&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
I was recently tasked with doing some research to figure out the best way to use &lt;a
    href=&quot;http://foundation.zurb.com/&quot;&gt;Foundation&lt;/a&gt; with &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. Goals for
    this research included:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Identify use cases of Foundation for Sites vs Foundation for Apps and recommend when to use each.&lt;/li&gt;
    &lt;li&gt;Look at pros and cons of using AngularJS with Foundation for Sites.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&apos;m writing this blog post to get feedback from you, fellow web developers, on your experience with Foundation. Have
    you tried using Foundation for Sites with AngularJS? If so, did you experience any pain?&lt;/p&gt;
&lt;p&gt;From what I can tell, it looks like Foundation for Apps (FA) was created because folks had issues making AngularJS
    and Foundation 5 play nicely together. &lt;a href=&quot;http://zurb.com/article/1312/the-next-foundation&quot;&gt;The Next
        Foundation&lt;/a&gt; explains why FA was created. Reddit&apos;s web_design zone has quite a few &lt;a
        href=&quot;http://www.reddit.com/r/web_design/comments/26btc4/zurb_the_next_foundation_foundation_built_with/&quot;&gt;comments
        related to this article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;From there, I found a few ZURB blog posts that describe FA&apos;s three main advantages over Foundation for Sites
    (FS):&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;&lt;a href=&quot;http://zurb.com/article/1333/foundation-a-new-grid&quot;&gt;A New Grid&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://zurb.com/article/1340/foundation-for-apps-motion-ui-is-the-new-&quot;&gt;Motion UI&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://zurb.com/article/1345/design-amazing-single-page-apps-with-the-&quot;&gt;AngularJS Integration&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;http://foundation.zurb.com/forum/posts/21134-foundation-5-and-foundation-apps-differences&quot;&gt;This thread
    on the Foundation forums&lt;/a&gt; seems to indicate that FA would be good for developing applications while FS would be
    good for an intranet built on WordPress (since it&apos;s more of a website than a webapp). &lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
    Foundation for apps is for making responsive web apps vs responsive web sites. The difference is in the structure
    of an app. They usually take up the screen and instead of the page scrolling, content in the app scrolls. Apps
    employ stateful views, so a view can change without reloading the app, creating a better user experience.
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;http://foundation.zurb.com/forum/posts/16594-foundation-for-sites-to-foundation-for-apps-preparation&quot;&gt;This
    other thread&lt;/a&gt; backs up that notion.&lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
    &lt;p style=&quot;margin-top: 0&quot;&gt;Foundation for Apps will be an additional version of Foundation - not a replacement.&lt;/p&gt;

    &lt;p&gt;This means Foundation for Sites will continue on along side the new Apps version. Ink will be Foundation for
        Emails.&lt;/p&gt;

    &lt;p&gt;Having said that, choosing which one to use simply depends on the type of site you need to build. The Apps grid
        is better suited to make web apps or apps that can be wrapped for native.&lt;/p&gt;

    &lt;p&gt;The syntax is different (on purpose) and we are taking great care to make it easy to get.&lt;/p&gt;

    &lt;p style=&quot;margin-bottom: 0&quot;&gt;You should continue using Foundation for sites as long as you need traditional scrolling websites. We don&apos;t
        expect people to convert an existing site to an app unless they need to re-do their site anyways.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also found &lt;a href=&quot;https://github.com/pineconellc/angular-foundation&quot;&gt;Angular Foundation&lt;/a&gt; which is a &quot;a port of
    the AngularUI team&apos;s excellent angular-bootstrap project for use in the Foundation framework&quot;.&lt;/p&gt;

&lt;p&gt;The problem I see with using Angular Foundation over Foundation for Apps is that it&apos;s maintained by folks that aren&apos;t
    developing Foundation. It&apos;s more of a &quot;here&apos;s some Bootstrap widgets as Foundation widgets&quot;.&lt;/p&gt;

&lt;p&gt;It could also come down to IE support. Angular Foundation &lt;a href=&quot;http://foundation.zurb.com/docs/compatibility.html&quot;&gt;supports IE9+&lt;/a&gt; while Foundation for Apps is IE10+. From &lt;a
    href=&quot;http://www.htmlxprs.com/post/46/what-you-need-to-know-about-zurb-foundation-for-apps&quot;&gt;What You Need To Know
    About Zurb Foundation for Apps&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
    &lt;p style=&quot;margin-top: 0&quot;&gt;Foundation for Apps works with all of the modern browsers including Internet Explorer 10. It doesn&apos;t support IE9
        and other older browsers because of the new CSS3 features and issues of AngularJS in older browsers. Below is
        the snapshot of the compatibility list grabbed from Zurb website.&lt;/p&gt;

    &lt;p style=&quot;margin-bottom: 0&quot;&gt;&lt;img src=&quot;//raibledesigns.com/repository/images/compatibility-foundation-for-apps.jpg&quot; alt=&quot;Foundation for Apps compatibility&quot; width=&quot;100%&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I wrote this article in order to publish my research findings about Foundation for Apps vs. Foundation for Sites. It
seems like the natural thing to do is to use FA for any webapps we create and FS for any websites (e.g. WordPress sites). Do you agree with these findings?
Any other &lt;em&gt;recommended practices&lt;/em&gt; for integrating Foundation with AngularJS?
&lt;/p&gt;
&lt;p class=&quot;alert alert-info&quot;&gt;&lt;strong&gt;Sidenote&lt;/strong&gt;: For the project I&apos;m working on, we haven&apos;t chose a backend framework yet. We&apos;ve chosen &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt; as our deployment
    platform, so there&apos;s a plethora of languages and frameworks to choose from.
    It&apos;s too bad &lt;a href=&quot;https://github.com/jhipster/generator-jhipster/issues/1039&quot;&gt;JHipster doesn&apos;t support
    Foundation&lt;/a&gt;. I could probably sell the team on Java + Spring Boot pretty easily if it did.&lt;/p&gt;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/the_art_of_angularjs_in</guid>
    <title>The Art of AngularJS in 2015</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/the_art_of_angularjs_in</link>
        <pubDate>Wed, 4 Feb 2015 09:14:57 -0700</pubDate>
    <category>The Web</category>
    <category>denver</category>
    <category>angularjs</category>
    <category>grunt</category>
    <category>dosug</category>
    <category>http2</category>
    <category>coffeescript</category>
    <category>protractor</category>
    <category>javascript</category>
    <category>html5</category>
    <category>jasmine</category>
    <atom:summary type="html">&lt;p&gt;I&apos;ve been tracking statistics on jobs and skills for JavaScript MVC frameworks ever since I &lt;a href=&quot;//raibledesigns.com/rd/entry/devoxx_france_a_great_conference&quot;&gt;Compared JVM Web Frameworks at Devoxx
    France in 2013&lt;/a&gt;. At that time, Backbone was the dominant framework.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm8.staticflickr.com/7452/16255644670_e426fb455f_c.jpg&quot; title=&quot;2013 Dice Jobs for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16255644670&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7452/16255644670_e426fb455f_m.jpg&quot; width=&quot;240&quot; alt=&quot;2013 Dice Jobs for JavaScript MVC Frameworks&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;

    &lt;a href=&quot;https://farm8.staticflickr.com/7411/16255384478_67712c17dd_c.jpg&quot; title=&quot;2013 LinkedIn Skills for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16255384478&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7411/16255384478_67712c17dd_m.jpg&quot; width=&quot;240&quot; alt=&quot;2013 LinkedIn Skills 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;Last year, I updated those statistics for a &lt;a href=&quot;//raibledesigns.com/rd/entry/the_art_of_angularjs&quot;&gt;presentation
    on AngularJS&lt;/a&gt; at Denver&apos;s Derailed. Angular had a similar amount of jobs as Backbone and a lot of people added it
    to their LinkedIn profiles. I found that Ember had grown around 300%, Backbone 200% and Angular 1000%!&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm8.staticflickr.com/7381/16256817639_b1ea05213a_c.jpg&quot; title=&quot;2014 Dice Jobs for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16256817639&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7381/16256817639_b1ea05213a_m.jpg&quot; width=&quot;240&quot; alt=&quot;2014 Dice Jobs for JavaScript MVC Frameworks&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm8.staticflickr.com/7381/16443061465_e89eda261c_c.jpg&quot; title=&quot;2014 LinkedIn Skills for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16443061465&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7381/16443061465_e89eda261c_m.jpg&quot; width=&quot;240&quot; alt=&quot;2014 LinkedIn Skills 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;Before presenting on AngularJS at &lt;a href=&quot;http://www.meetup.com/DOSUG1/events/219099019/&quot;&gt;last night&apos;s Denver Open
    Source Users Group&lt;/a&gt;, I updated these statistics once again. The charts below show how the number of jobs for
    Angular has doubled in the last year, while jobs for Ember and Backbone have fallen slightly. As far as skills,
    developers learning Ember and Backbone has increased 200%, while skilled Angular folks has risen 400%.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm9.staticflickr.com/8637/16443001655_fb8593e2f3_c.jpg&quot; title=&quot;2015 Dice Jobs for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16443001655&quot;&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8637/16443001655_fb8593e2f3_m.jpg&quot; width=&quot;240&quot; alt=&quot;2015 Dice Jobs for JavaScript MVC Frameworks&quot; style=&quot;border: 1px solid black;&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm9.staticflickr.com/8628/16257092107_c4a9735b65_c.jpg&quot; title=&quot;2015 LinkedIn Skills for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16257092107&quot;&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8628/16257092107_c4a9735b65_m.jpg&quot; width=&quot;240&quot; alt=&quot;2015 LinkedIn Skills 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;Yes, AngularJS has experienced &lt;em&gt;huge&lt;/em&gt; growth in the last couple of years. You might even say it&apos;s the &lt;em&gt;Struts
    of the JavaScript world&lt;/em&gt;.
&lt;/p&gt;&lt;p&gt;For the presentation I delivered last night, I made a number of improvements over last year&apos;s. I added a live coding
    demo based on my &lt;a href=&quot;&quot;&gt;Getting Started with AngularJS&lt;/a&gt; tutorial. I used IntelliJ&apos;s &lt;a href=&quot;https://www.jetbrains.com/idea/help/live-templates.html&quot;&gt;live templates&lt;/a&gt; to make it look easy. However,
    since the audience was quiet, and some were falling asleep, I skipped over the &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;testing&lt;/a&gt; demo.&lt;/p&gt;
</atom:summary>        <description>&lt;p&gt;I&apos;ve been tracking statistics on jobs and skills for JavaScript MVC frameworks ever since I &lt;a href=&quot;//raibledesigns.com/rd/entry/devoxx_france_a_great_conference&quot;&gt;Compared JVM Web Frameworks at Devoxx
    France in 2013&lt;/a&gt;. At that time, Backbone was the dominant framework.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm8.staticflickr.com/7452/16255644670_e426fb455f_c.jpg&quot; title=&quot;2013 Dice Jobs for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16255644670&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7452/16255644670_e426fb455f.jpg&quot; width=&quot;500&quot; alt=&quot;2013 Dice Jobs for JavaScript MVC Frameworks&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://farm8.staticflickr.com/7411/16255384478_67712c17dd_c.jpg&quot; title=&quot;2013 LinkedIn Skills for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16255384478&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7411/16255384478_67712c17dd.jpg&quot; width=&quot;500&quot; alt=&quot;2013 LinkedIn Skills for JavaScript MVC Frameworks&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Last year, I updated those statistics for a &lt;a href=&quot;//raibledesigns.com/rd/entry/the_art_of_angularjs&quot;&gt;presentation
    on AngularJS&lt;/a&gt; at Denver&apos;s Derailed. Angular had a similar amount of jobs as Backbone and a lot of people added it
    to their LinkedIn profiles. I found that Ember had grown around 300%, Backbone 200% and Angular 1000%!&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm8.staticflickr.com/7381/16256817639_b1ea05213a_c.jpg&quot; title=&quot;2014 Dice Jobs for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16256817639&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7381/16256817639_b1ea05213a.jpg&quot; width=&quot;500&quot; alt=&quot;2014 Dice Jobs for JavaScript MVC Frameworks&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://farm8.staticflickr.com/7381/16443061465_e89eda261c_c.jpg&quot; title=&quot;2014 LinkedIn Skills for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16443061465&quot;&gt;&lt;img src=&quot;https://farm8.staticflickr.com/7381/16443061465_e89eda261c.jpg&quot; width=&quot;500&quot; alt=&quot;2014 LinkedIn Skills for JavaScript MVC Frameworks&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Before presenting on AngularJS at &lt;a href=&quot;http://www.meetup.com/DOSUG1/events/219099019/&quot;&gt;last night&apos;s Denver Open
    Source Users Group&lt;/a&gt;, I updated these statistics once again. The charts below show how the number of jobs for
    Angular has doubled in the last year, while jobs for Ember and Backbone have fallen slightly. As far as skills,
    developers learning Ember and Backbone has increased 200%, while skilled Angular folks has risen 400%.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm9.staticflickr.com/8637/16443001655_fb8593e2f3_c.jpg&quot; title=&quot;2015 Dice Jobs for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16443001655&quot;&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8637/16443001655_fb8593e2f3.jpg&quot; width=&quot;500&quot; alt=&quot;2015 Dice Jobs for JavaScript MVC Frameworks&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://farm9.staticflickr.com/8628/16257092107_c4a9735b65_c.jpg&quot; title=&quot;2015 LinkedIn Skills for JavaScript MVC Frameworks by Matt Raible, on Flickr&quot; rel=&quot;lightbox[artofangular2015]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/16257092107&quot;&gt;&lt;img src=&quot;https://farm9.staticflickr.com/8628/16257092107_c4a9735b65.jpg&quot; width=&quot;500&quot; alt=&quot;2015 LinkedIn Skills for JavaScript MVC Frameworks&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Yes, AngularJS has experienced &lt;em&gt;huge&lt;/em&gt; growth in the last couple of years. You might even say it&apos;s the &lt;em&gt;Struts
    of the JavaScript world&lt;/em&gt;. I like to say that &lt;a href=&quot;http://www.infoq.com/news/2013/04/struts1-eol&quot;&gt;Struts 1.x was the &apos;Killer App&apos; for J2EE&lt;/a&gt; back in the day.
&lt;/p&gt;&lt;p&gt;For the presentation I delivered last night, I made a number of improvements over last year&apos;s. I added a live coding
    demo based on my &lt;a href=&quot;&quot;&gt;Getting Started with AngularJS&lt;/a&gt; tutorial. I used IntelliJ&apos;s &lt;a href=&quot;https://www.jetbrains.com/idea/help/live-templates.html&quot;&gt;live templates&lt;/a&gt; to make it look easy. However,
    since the audience was quiet, and some were falling asleep, I skipped over the &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;testing&lt;/a&gt; demo.&lt;/p&gt;
&lt;p&gt;I added a few slides on &lt;a href=&quot;http://foundation.zurb.com/apps/&quot;&gt;Foundation for Apps&lt;/a&gt; (FA). We&apos;ve selected
    AngularJS and Foundation on my current project, and I&apos;ve been researching how to integrate the two lately. FA is one
    solution I&apos;ve found, &lt;a href=&quot;http://pineconellc.github.io/angular-foundation/&quot;&gt;Angular Foundation&lt;/a&gt; is
    another. If you know of others, please let me know.&lt;/p&gt;
&lt;p&gt;For Java developers getting started with Angular, I recommended &lt;a href=&quot;http://jhipster.github.io/&quot;&gt;JHipster&lt;/a&gt;. I
    talked about its foundational frameworks and project options when creating your project. I included screenshots of
    its slick metrics UI and code generation features.&lt;/p&gt;
&lt;p&gt;I also added a slide for Dave Syer&apos;s excellent five-part series on Spring and AngularJS:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;http://spring.io/blog/2015/01/12/spring-and-angular-js-a-secure-single-page-application&quot;&gt;Spring and
        Angular JS: A Secure Single Page Application&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://spring.io/blog/2015/01/12/the-login-page-angular-js-and-spring-security-part-ii&quot;&gt;The Login Page:
        Angular JS and Spring Security Part II&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://spring.io/blog/2015/01/20/the-resource-server-angular-js-and-spring-security-part-iii&quot;&gt;The
        Resource Server: Angular JS and Spring Security Part III&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://spring.io/blog/2015/01/28/the-api-gateway-pattern-angular-js-and-spring-security-part-iv&quot;&gt;The
        API Gateway Pattern: Angular JS and Spring Security Part IV&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v&quot;&gt;SSO with
        OAuth2: Angular JS and Spring Security Part V&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, I added a number of slides on &lt;a href=&quot;http://www.infoq.com/news/2014/10/angular-2-atscript&quot;&gt;Angular 2.0&lt;/a&gt;.
    I encouraged folks to checkout &lt;a href=&quot;http://12factor.net/&quot;&gt;The Twelve-Factor App&lt;/a&gt; and James Ward&apos;s
    &lt;a href=&quot;http://www.jamesward.com/2014/12/03/java-doesnt-suck-youre-just-using-it-wrong&quot;&gt;Java Doesn&#8217;t Suck &#8211; You&#8217;re Just Using it Wrong&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;The discussion with the audience was great, particularly around HTTP/2 and minification/concatenation of assets.
    Thanks to all who attended, I really enjoyed having the opportunity to share what I&apos;ve learned.&lt;/p&gt;
&lt;p&gt;You can click through my presentation below, download it from &lt;a href=&quot;//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-art-of-angularjs-in-2015&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/44244006&quot; width=&quot;512&quot; height=&quot;325&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;/div&gt;
&lt;p&gt;If you live in Denver, there&apos;s a number of interesting meetups happening in the next couple months. &lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Monday, February 16: &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/events/220053261/&quot;&gt;Introduction to ReactJS&lt;/a&gt; at HTML5 Denver&lt;/li&gt;
    &lt;li&gt;Tuesday &amp;amp; Wednesday, March 3rd and 4th: &lt;a href=&quot;http://thingmonk.com/&quot;&gt;ThingMonk&lt;/a&gt; at &lt;a href=&quot;http://www.drinkmilehighspirits.com/&quot;&gt;&lt;em&gt;a distillery!&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Wednesday, April 8: I&apos;ll be speaking about JHipster at Denver Java User Group&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;m also looking for speakers to teach programming to kids at &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/&quot;&gt;Devoxx4Kids Denver&lt;/a&gt;.
    Let me know if you have a fun topic you&apos;d like to present.&lt;/p&gt;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/testing_angularjs_applications</guid>
    <title>Testing AngularJS Applications</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/testing_angularjs_applications</link>
        <pubDate>Mon, 2 Feb 2015 10:11:56 -0700</pubDate>
    <category>The Web</category>
    <category>angularjs</category>
    <category>karma</category>
    <category>javascript</category>
    <category>node</category>
    <category>jasmine</category>
    <category>git</category>
    <category>protractor</category>
    <category>npm</category>
    <atom:summary type="html">&lt;p&gt;This article is the second in a series about learning &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. It describes
    how to test a simple AngularJS application. In a previous article, &lt;a
            href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting Started with AngularJS&lt;/a&gt;, I
    showed how to develop a simple search and edit feature.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll learn&lt;/h3&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://angular.github.io/protractor/#/&quot;&gt;Protractor&lt;/a&gt; for integration testing. Angular&apos;s documentation
    has a
    good &lt;a href=&quot;https://docs.angularjs.org/guide/unit-testing&quot;&gt;developer&apos;s 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;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;h3&gt;What you&apos;ll need&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;About 15-30 minutes&lt;/li&gt;
    &lt;li&gt;A favorite text editor or IDE. We 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.&lt;/li&gt;
&lt;/ul&gt;</atom:summary>        <description>&lt;p&gt;This article is the second in a series about learning &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. It describes
    how to test a simple AngularJS application. In a previous article, &lt;a
            href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting Started with AngularJS&lt;/a&gt;, I
    showed how to develop a simple search and edit feature.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll learn&lt;/h3&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://angular.github.io/protractor/#/&quot;&gt;Protractor&lt;/a&gt; for integration testing. Angular&apos;s documentation
    has a
    good &lt;a href=&quot;https://docs.angularjs.org/guide/unit-testing&quot;&gt;developer&apos;s 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;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;h3&gt;What you&apos;ll need&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;About 15-30 minutes&lt;/li&gt;
    &lt;li&gt;A favorite text editor or IDE. We 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.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Get the tutorial project&lt;/h3&gt;
&lt;p&gt;Clone the angular-tutorial repository using &lt;a href=&quot;http://git-scm.com/&quot;&gt;git&lt;/a&gt; and
    install the dependencies. &lt;/p&gt;

&lt;pre&gt;
git clone https://github.com/mraible/angular-tutorial.git
cd angular-tutorial
npm install&lt;/pre&gt;
    &lt;p&gt;If you haven&apos;t completed the &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting
        Started with AngularJS&lt;/a&gt; tutorial, you should peruse it so you
        understand how this application works. You can also simply start the app with &amp;quot;npm start&amp;quot; and view it
        in your browser at &lt;a href=&quot;http://localhost:8000/app/&quot;&gt;http://localhost:8000/app/&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Test the SearchController&lt;/h3&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;app/search/search_test.js&lt;/code&gt; and populate it with the basic test infrastructure. This code
            sets up a mock &lt;code&gt;SearchService&lt;/code&gt; that has the first function we want to test:
            &lt;code&gt;query(term)&lt;/code&gt;. It uses &lt;code&gt;$provide&lt;/code&gt; to override the default &lt;code&gt;SearchService&lt;/code&gt;.
            &lt;a href=&quot;http://stackoverflow.com/questions/20828179/angular-unit-test-controllers-mocking-service-inside-controller&quot;&gt;Angular
                unit-test controllers - mocking service inside controller&lt;/a&gt; was a useful question on Stack Overflow
            for figuring out how to mock services in controllers.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;&apos;use strict&apos;;

describe(&apos;myApp.search module&apos;, function() {
    var mockSearchService;

    beforeEach(module(&apos;myApp.search&apos;, function($provide) {
        mockSearchService = {query: function(term) {}};
        $provide.value(&quot;SearchService&quot;, mockSearchService);
    }));
});&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;karma.conf.js&lt;/code&gt; (in the root directory) to add the search implementation and test.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;files : [
  ...
  &apos;app/components/**/*.js&apos;,
  &apos;app/search/search.js&apos;,
  &apos;app/search/search_test.js&apos;,
  &apos;app/view*/**/*.js&apos;
],&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add the first test to &lt;code&gt;search_test.js&lt;/code&gt;. This test verifies that setting a search term and
            executing the &lt;code&gt;search()&lt;/code&gt; function will call the service and return results. You can see the results returned
            from the service are mocked with
            &lt;code&gt;deferred.resolve()&lt;/code&gt;. The &lt;code&gt;deferred.resolve()&lt;/code&gt; call is &lt;a
                    href=&quot;http://entwicklertagebuch.com/blog/2013/10/how-to-handle-angularjs-promises-in-jasmine-unit-tests/&quot;&gt;how
                to handle promises in unit tests&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;search by term&apos;, function() {
    var scope, rootScope, controller, deferred;

	// setup the controller with dependencies.
    beforeEach(inject(function($rootScope, $controller, $q) {
        rootScope = $rootScope;
        scope = $rootScope.$new();
        controller = $controller(&apos;SearchController&apos;, {$scope: scope, SearchService: mockSearchService });
        deferred = $q.defer();
    }));

    it(&apos;should search when a term is set and search() is called&apos;, function() {
        spyOn(mockSearchService, &apos;query&apos;).andReturn(deferred.promise);
        scope.term = &apos;M&apos;;
        scope.search();
        deferred.resolve({data: {name: &quot;Peyton Manning&quot;}});
        rootScope.$apply();
        expect(scope.searchResults).toEqual({name: &quot;Peyton Manning&quot;});
    });
});&lt;/pre&gt;
        &lt;p&gt;Related: &lt;a href=&quot;http://angular-tips.com/blog/2014/03/introduction-to-unit-test-spies/&quot;&gt;Introduction to
            Unit Test: Spies&lt;/a&gt; is a good introduction to using spies in unit tests.&lt;br/&gt;&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Run the following command on the command line to start the Karma test runner. You can leave this process
            running and new tests will be run automatically. You can change the mocked data and expectation to see your
            test fail.&lt;/p&gt;
        &lt;pre&gt;npm test&lt;/pre&gt;
        &lt;div class=&quot;alert alert-success&quot;&gt;&lt;strong&gt;Running Tests from IntelliJ IDEA&lt;/strong&gt;&lt;br&gt;
            See &lt;a href=&quot;https://www.jetbrains.com/idea/help/running-unit-tests-on-karma.html&quot;&gt;Running Unit Tests on
                Karma&lt;/a&gt; to learn how to run your tests from IntelliJ IDEA.
        &lt;/div&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add a test to verify a search occurs automatically when the term is in the URL. Notice how the code structure had
            to change a bit to handle &lt;code&gt;$routeParams&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;search by term automatically&apos;, function() {
    var scope, rootScope, controller, location, deferred;

    beforeEach(inject(function($rootScope, $controller, $q) {
        rootScope = $rootScope;
        scope = $rootScope.$new();

        // in this case, expectations need to be setup before controller is initialized
        var routeParams = {&quot;term&quot;: &quot;peyton&quot;};
        deferred = $q.defer();
        spyOn(mockSearchService, &apos;query&apos;).andReturn(deferred.promise);
        deferred.resolve({data: {name: &quot;Peyton Manning&quot;}});

        controller = $controller(&apos;SearchController&apos;, {$scope: scope, $routeParams: routeParams, SearchService: mockSearchService });
    }));

    it(&apos;should search automatically when a term is on the URL&apos;, function() {
        rootScope.$apply();
        expect(scope.searchResults).toEqual({name: &quot;Peyton Manning&quot;});
    });
});&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add a test to verify the &lt;code&gt;EditController&lt;/code&gt; works as expected.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;edit person&apos;, function() {
    var scope, rootScope, controller, location, deferred;

    beforeEach(inject(function($rootScope, $controller, $q) {
        rootScope = $rootScope;
        scope = $rootScope.$new();

        // expectations need to be setup before controller is initialized
        var routeParams = {&quot;id&quot;: &quot;1&quot;};
        deferred = $q.defer();
        spyOn(mockSearchService, &apos;fetch&apos;).andReturn(deferred.promise);
        deferred.resolve({data: {name: &quot;Peyton Manning&quot;, address: {street: &quot;12345 Blake Street&quot;, city: &quot;Denver&quot;}}});

        controller = $controller(&apos;EditController&apos;, {$scope: scope, $routeParams: routeParams, SearchService: mockSearchService });
    }));

    it(&apos;should fetch a single record&apos;, function() {
        rootScope.$apply();
        expect(scope.person.name).toBe(&quot;Peyton Manning&quot;);
        expect(scope.person.address.street).toBe(&quot;12345 Blake Street&quot;);
    });
});&lt;/pre&gt;
&lt;p&gt;After adding this test, you&apos;ll likely see the following error in your terminal.&lt;/p&gt;
&lt;pre class=&quot;brush: shell&quot;&gt;
Chrome 40.0.2214 (Mac OS X 10.10.2) myApp.search module edit person should fetch a single record FAILED
	fetch() method does not exist
	TypeError: Cannot read property &apos;name&apos; of undefined
&lt;/pre&gt;
        &lt;p&gt;This happens because the &lt;code&gt;mockSearchService&lt;/code&gt; does not have a fetch method defined. Modify the &lt;code&gt;beforeEach()&lt;/code&gt; on line 7 to add this function.&lt;/p&gt;
    &lt;pre class=&quot;brush: js&quot;&gt;mockSearchService = {query: function(term) {}, fetch: function(id) {}};&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Extra Credit&lt;/h3&gt;
&lt;p&gt;Create a test for saving a person. &lt;a
        href=&quot;http://stackoverflow.com/questions/13664144/how-to-unit-test-angularjs-controller-with-location-service&quot;&gt;Here&apos;s
    a question on Stack Overflow&lt;/a&gt; that might help you verify the location after a save has been performed.&lt;/p&gt;
&lt;h3&gt;Test the Search Feature&lt;/h3&gt;
&lt;p&gt;To test if the application works end-to-end, you can write scenarios with &lt;a
        href=&quot;http://angular.github.io/protractor/&quot;&gt;Protractor&lt;/a&gt;. These are also known as integration tests, as 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 command in one terminal
    window:&lt;/p&gt;
&lt;pre&gt;npm start&lt;/pre&gt;
&lt;p&gt;Then in another window, run the following to execute the tests:&lt;/p&gt;
&lt;pre&gt;npm run protractor&lt;/pre&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Write your first integration test to verify you can navigate to /search and enter a search term to see
            results. Add the following to &lt;code&gt;e2e-tests/scenarios.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;search&apos;, function() {
  var searchTerm = element(by.model(&apos;term&apos;));
  var searchButton = element(by.id(&apos;search&apos;));

  beforeEach(function() {
    browser.get(&apos;index.html#/search&apos;);
  });

  it(&apos;should allow searching at /search&apos;, function() {
    searchTerm.sendKeys(&quot;M&quot;);
    searchButton.click().then(function() {
      expect(element.all(by.repeater(&apos;person in searchResults&apos;)).count()).toEqual(3);
    });
  });
});&lt;/pre&gt;
        &lt;p&gt;The &amp;quot;searchTerm&amp;quot; variable represents the input field. The &lt;code&gt;by.model()&lt;/code&gt; syntax binds to
            the &amp;quot;ng-model&amp;quot; attribute you defined in the HTML. &lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Run &amp;quot;npm run protractor&amp;quot;. This should fail with the following error.&lt;/p&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;[launcher] Running 1 instances of WebDriver
Selenium standalone server started at http://172.16.6.39:64230/wd/hub
...F
Failures:
  1) my app search should allow searching at /search
   Message:
     NoSuchElementError: No element found using locator: By.id(&quot;search&quot;)&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;To fix, you need to add an &amp;quot;id&amp;quot; attribute to the Search button in
            &lt;code&gt;app/search/index.html&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;form ng-submit=&quot;search()&quot;&amp;gt;
    &amp;lt;input type=&quot;search&quot; name=&quot;search&quot; ng-model=&quot;term&quot;&amp;gt;
    &amp;lt;button id=&quot;search&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Run the tests again using &amp;quot;npm run protractor&amp;quot;. They should all pass this time.&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Write another test to verify editing a user displays their information.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;edit person&apos;, function() {
  var name = element(by.model(&apos;person.name&apos;));
  var street = element(by.model(&apos;person.address.street&apos;));
  var city = element(by.model(&apos;person.address.city&apos;));

  beforeEach(function() {
    browser.get(&apos;index.html#/edit/1&apos;);
  });

  it(&apos;should allow viewing a person&apos;, function() {
    // getText() doesn&apos;t work with input elements, see the following for more information:
    // https://github.com/angular/protractor/blob/master/docs/faq.md#the-result-of-gettext-from-an-input-element-is-always-empty
    expect(name.getAttribute(&apos;value&apos;)).toEqual(&quot;Peyton Manning&quot;);
    expect(street.getAttribute(&apos;value&apos;)).toEqual(&quot;1234 Main Street&quot;);
    expect(city.getAttribute(&apos;value&apos;)).toEqual(&quot;Greenwood Village&quot;);
  });
});&lt;/pre&gt;
        &lt;p&gt;Verify it works with &amp;quot;npm run protractor&amp;quot;.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Finally, write a test to verify you can save a person and their details are updated. Figuring out how to
            verify the URL after it changed was assisted by &lt;a href=&quot;https://github.com/angular/protractor/issues/610&quot;&gt;this
                issue&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;save person&apos;, function() {
  var name = element(by.model(&apos;person.name&apos;));
  var save = element(by.id(&apos;save&apos;));

  beforeEach(function() {
    browser.get(&apos;index.html#/edit/1&apos;);
  });

  it(&apos;should allow updating a name&apos;, function() {
    name.sendKeys(&quot; Updated&quot;);
    save.click().then(function() {
      // verify url set back to search results
      browser.driver.wait(function() {
        return browser.driver.getCurrentUrl().then(function(url) {
          expect(url).toContain(&apos;/search/Peyton%20Manning%20Updated&apos;);
          return url;
        });
      });
      // verify one element matched this change
      expect(element.all(by.repeater(&apos;person in searchResults&apos;)).count()).toEqual(1);
    });
  });
});&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;When you run this test with &amp;quot;npm run protractor&amp;quot;, it should fail because there&apos;s no element with
            &lt;code&gt;id=&amp;quot;save&amp;quot;&lt;/code&gt; in &lt;code&gt;app/search/edit.html&lt;/code&gt;. Add it to the Save button in this
            file and try again. You should see something similar to the following:&lt;/p&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;Finished in 4.478 seconds
6 tests, 9 assertions, 0 failures&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&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/angular-tutorial/&quot;&gt;https://github.com/mraible/angular-tutorial&lt;/a&gt; on the &lt;strong&gt;testing&lt;/strong&gt; branch.
&lt;/p&gt;
&lt;p&gt;There are two commits that make the changes for the two main steps in this tutorial:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/mraible/angular-tutorial/commit/f3551cc84bd2f0fb35b4dd9ac38cbf6f417c106f&quot;&gt;Unit Tests&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/mraible/angular-tutorial/commit/12e37eb7ad8cde82c6ec92c8562700a7fb6952cc&quot;&gt;Integration Tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on testing AngularJS applications. The first couple AngularJS applications
I developed didn&apos;t have tests and required a lot of manual testing to verify their quality. After learning that testing AngularJS
apps is pretty easy, I now do it on all my projects. Hopefully this tutorial motivates you to do do the same.
&lt;/p&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/getting_started_with_angularjs</guid>
    <title>Getting Started with AngularJS</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/getting_started_with_angularjs</link>
        <pubDate>Thu, 29 Jan 2015 11:12:38 -0700</pubDate>
    <category>The Web</category>
    <category>npm</category>
    <category>javascript</category>
    <category>git</category>
    <category>angularjs</category>
    <category>node</category>
    <atom:summary type="html">&lt;p&gt;I was hired by my current client in November to help them choose a technology stack for developing modern web applications.
   In our first sprint, we decided to look at JavaScript MVC frameworks. I suggested &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;, &lt;a href=&quot;http://emberjs.com/&quot;&gt;Ember.js&lt;/a&gt; and &lt;a href=&quot;http://facebook.github.io/react/&quot;&gt;React&lt;/a&gt;. Since
    most of the team was new to JavaScript MVC, I decided to create a tutorial for them. I tried to make it easy so they
    could learn how to write a simple web application with AngularJS. I thought others could benefit from this article as well,
so I asked (and received) permission from my client to publish it here.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll build&lt;/h3&gt;
&lt;p&gt;You&apos;ll build a simple web application with AngularJS. You&apos;ll also add search and edit features with mock data.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll need&lt;/h3&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.&lt;/li&gt;
&lt;/ul&gt;</atom:summary>        <description>&lt;p&gt;I was hired by my current client in November to help them choose a technology stack for developing modern web applications.
   In our first sprint, we decided to look at JavaScript MVC frameworks. I suggested &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;, &lt;a href=&quot;http://emberjs.com/&quot;&gt;Ember.js&lt;/a&gt; and &lt;a href=&quot;http://facebook.github.io/react/&quot;&gt;React&lt;/a&gt;. Since
    most of the team was new to JavaScript MVC, I decided to create a tutorial for them. I tried to make it easy so they
    could learn how to write a simple web application with AngularJS. I thought others could benefit from this article as well,
so I asked (and received) permission from my client to publish it here.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll build&lt;/h3&gt;
&lt;p&gt;You&apos;ll build a simple web application with AngularJS. You&apos;ll also add search and edit features with mock data.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll need&lt;/h3&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.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Create a simple web application&lt;/h3&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Clone the angular-seed repository using &lt;a href=&quot;http://git-scm.com/&quot;&gt;git&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;
git clone https://github.com/angular/angular-seed.git angular-tutorial
cd angular-tutorial&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;There are two kinds of dependencies in this project: tools and angular framework code. The tools help manage
            and test the application.&lt;/p&gt;
        &lt;ul&gt;
            &lt;li&gt;To get the tools that depend upon via &lt;code&gt;npm&lt;/code&gt;, the &lt;a href=&quot;https://www.npmjs.org/&quot;&gt;node
                package manager&lt;/a&gt;.
            &lt;/li&gt;
            &lt;li&gt;To get the angular code via &lt;code&gt;bower&lt;/code&gt;, a &lt;a href=&quot;http://bower.io/&quot;&gt;client-side code package
                manager&lt;/a&gt;.
            &lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;The project has preconfigured &lt;code&gt;npm&lt;/code&gt;&amp;nbsp;to automatically run &lt;code&gt;bower&lt;/code&gt;&amp;nbsp;so you can
            simply do:&lt;/p&gt;
        &lt;pre&gt;npm install&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Run the application&lt;/h3&gt;
&lt;p&gt;The project is configured with a simple development web server. The simplest way to start this server is:&lt;/p&gt;
&lt;pre&gt;npm start&lt;/pre&gt;
&lt;p&gt;Now browse to the app at &lt;code&gt;&lt;a href=&quot;http://localhost:8000/app/index.html&quot;&gt;http://localhost:8000/app/&lt;/a&gt;&lt;/code&gt;.
&lt;/p&gt;
&lt;h3&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 directory you cloned angular-seed to.&lt;/p&gt;
&lt;h3&gt;The Basics&lt;/h3&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Create an &lt;code&gt;app/search/index.html&lt;/code&gt; file with the following HTML:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;form ng-submit=&quot;search()&quot;&amp;gt;
    &amp;lt;input type=&quot;search&quot; name=&quot;search&quot; ng-model=&quot;term&quot;&amp;gt;
    &amp;lt;button&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;app/search/search.js&lt;/code&gt; and define the routes (URLs) and controller for the search feature.
        &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
angular.module(&apos;myApp.search&apos;, [&apos;ngRoute&apos;])

    .config([&apos;$routeProvider&apos;, function ($routeProvider) {
        $routeProvider
            .when(&apos;/search&apos;, {
                templateUrl: &apos;search/index.html&apos;,
                controller: &apos;SearchController&apos;
            })
    }])

    .controller(&apos;SearchController&apos;, function () {
        console.log(&quot;In Search Controller...&quot;);
    });
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/app.js&lt;/code&gt; and add the &amp;quot;myApp.search&amp;quot; module you created above.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;angular.module(&apos;myApp&apos;, [
  &apos;ngRoute&apos;,
  &apos;myApp.view1&apos;,
  &apos;myApp.view2&apos;,
  &apos;myApp.version&apos;,
  &apos;myApp.search&apos;
])&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/index.html&lt;/code&gt; and add a link to the search.js file.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;script src=&quot;view2/view2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;search/search.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;components/version/version.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Refresh your browser and navigate to &lt;a href=&quot;http://localhost:8000/app/#/search&quot;&gt;http://localhost:8000/app/#/search.&lt;/a&gt;
            You should see an input field and search button. You should also see a log message printed in your browser&apos;s
            console.
            In Chrome, you can view the console using View &amp;gt; Developer &amp;gt; JavaScript Console. You can make it
            easier to navigate to this page by adding a menu item in &lt;code&gt;app/index.html&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;ul class=&quot;menu&quot;&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#/view1&quot;&amp;gt;view1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#/view2&quot;&amp;gt;view2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#/search&quot;&amp;gt;search&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This section has shown you how to add a new controller and view to a basic AngularJS application. This was fairly simple to create.
    The next section shows you how to create a fake backend API. &lt;/p&gt;
&lt;h3&gt;The Backend&lt;/h3&gt;
&lt;p&gt;To get search results, you&apos;re going to create a SearchService
    that makes HTTP requests. These HTTP requests will be handled by a mock backend using some of Angular&apos;s built-in
    mocking tools. The backend implementation was created using &lt;a
        href=&quot;http://www.jeremyzerr.com/angularjs-backend-less-development-using-httpbackend-mock&quot;&gt;AngularJS Backend-less Development Using a $httpBackend Mock&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Add a &lt;code&gt;SearchService&lt;/code&gt; to &lt;code&gt;app/search/search.js&lt;/code&gt;. This is done in Angular using its &lt;a
            href=&quot;https://docs.angularjs.org/guide/providers#factory-recipe&quot;&gt;Factory Recipe&lt;/a&gt;. Make sure to remove the
            semicolon from the &lt;code&gt;.controller&lt;/code&gt; code block.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;.controller(&apos;SearchController&apos;, function () {
    console.log(&quot;In Search Controller...&quot;);
})

.factory(&apos;SearchService&apos;, function ($http) {
    var service = {
        query: function (term) {
            return $http.get(&apos;/search/&apos; + term);
        }
    };
    return service;
});
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Inject the &lt;code&gt;SearchService&lt;/code&gt; into the &lt;code&gt;SearchController&lt;/code&gt; and use it to get results from
            the backend. The form in &lt;code&gt;app/search/index.html&lt;/code&gt; calls the &lt;code&gt;search()&lt;/code&gt; function. The
            &amp;quot;term&amp;quot; is defined by the input field in this page with
            &lt;span style=&quot;color: rgb(0,0,255);&quot;&gt;ng-model=&lt;/span&gt;&lt;span
                style=&quot;color: rgb(0,128,0);&quot;&gt;&amp;quot;term&amp;quot;.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;.controller(&apos;SearchController&apos;, function ($scope, SearchService) {
    $scope.search = function () {
        console.log(&quot;Search term is: &quot; + $scope.term);
        SearchService.query($scope.term).then(function (response) {
            $scope.searchResults = response.data;
        });
    };
})&lt;/pre&gt;
        &lt;p&gt;If you try to search for a &quot;foo&quot; term now, you&apos;ll see the following error in your console.&lt;/p&gt;
        &lt;pre&gt;Search term is: foo&lt;br/&gt;GET &lt;a href=&quot;http://localhost:8000/search/foo&quot;&gt;http://localhost:8000/search/foo&lt;/a&gt; 404 (Not Found)&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;app/search/mock-api.js&lt;/code&gt; for the fake backend. Populate it with the following JavaScript.
        &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;// We will be using backend-less development
// $http uses $httpBackend to make its calls to the server
// $resource uses $http, so it uses $httpBackend too
// We will mock $httpBackend, capturing routes and returning data
angular.module(&apos;myApp&apos;)
    .service(&apos;ServerDataModel&apos;, function ServerDataModel() {
        this.data = [
            {
                id: 1,
                name: &quot;Peyton Manning&quot;,
                phone: &quot;(303) 567-8910&quot;,
                address: {
                    street: &quot;1234 Main Street&quot;,
                    city: &quot;Greenwood Village&quot;,
                    state: &quot;CO&quot;,
                    zip: &quot;80111&quot;
                }
            },
            {
                id: 2,
                name: &quot;Demaryius Thomas&quot;,
                phone: &quot;(720) 213-9876&quot;,
                address: {
                    street: &quot;5555 Marion Street&quot;,
                    city: &quot;Denver&quot;,
                    state: &quot;CO&quot;,
                    zip: &quot;80202&quot;
                }
            },
            {
                id: 3,
                name: &quot;Von Miller&quot;,
                phone: &quot;(917) 323-2333&quot;,
                address: {
                    street: &quot;14 Mountain Way&quot;,
                    city: &quot;Vail&quot;,
                    state: &quot;CO&quot;,
                    zip: &quot;81657&quot;
                }
            }
        ];

        this.getData = function () {
            return this.data;
        };

        this.search = function (term) {
            if (term == &quot;&quot; || term == &quot;*&quot;) {
                return this.getData();
            }
            // find the name that matches the term
            var list = $.grep(this.getData(), function (element, index) {
                term = term.toLowerCase();
                return (element.name.toLowerCase().match(term));
            });

            if (list.length === 0) {
                return [];
            } else {
                return list;
            }
        };
    })
    .run(function ($httpBackend, ServerDataModel) {

        $httpBackend.whenGET(/\/search\/\w+/).respond(function (method, url, data) {
            // parse the matching URL to pull out the term (/search/:term)
            var term = url.split(&apos;/&apos;)[2];

            var results = ServerDataModel.search(term);

            return [200, results, {Location: &apos;/search/&apos; + term}];
        });

        $httpBackend.whenGET(/search\/index.html/).passThrough();
        $httpBackend.whenGET(/view/).passThrough();
    });&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;This file uses &lt;a href=&quot;http://api.jquery.com/jquery.grep/&quot;&gt;jQuery.grep()&lt;/a&gt;, so you&apos;ll need to install
            jQuery. Modify &lt;code&gt;bower.json&lt;/code&gt; and add jQuery to the list of dependencies. &lt;pre
        class=&quot;brush: js&quot;&gt;&quot;dependencies&quot;: {
  ...
  &quot;html5-boilerplate&quot;: &quot;~4.3.0&quot;,
  &quot;jquery&quot;: &quot;~1.10.x&quot;
}&lt;/pre&gt;
        &lt;p&gt;Stop the npm process, run
            &amp;quot;npm install&amp;quot; to download and install jQuery, then &amp;quot;npm start&amp;quot; again.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/app.js&lt;/code&gt;&amp;nbsp;and add a dependency on &lt;a
            href=&quot;https://docs.angularjs.org/api/ngMockE2E&quot;&gt;ngMockE2E&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;angular.module(&apos;myApp&apos;, [&apos;ngMockE2E&apos;,
  &apos;ngRoute&apos;,
  ...&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/index.html&lt;/code&gt; to include references to &lt;code&gt;jquery.js&lt;/code&gt;,
            &lt;code&gt;angular-mocks.js&lt;/code&gt; and &lt;code&gt;mock-api.js&lt;/code&gt;.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;script src=&quot;bower_components/angular-route/angular-route.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;bower_components/angular-mocks/angular-mocks.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;bower_components/jquery/jquery.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
...
&amp;lt;script src=&quot;search/mock-api.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;components/version/version.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add the following HTML to &lt;code&gt;app/search/index.html&lt;/code&gt; to display the search results.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div&amp;gt;
    &amp;lt;pre&amp;gt;{{ searchResults | json}}&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
        &lt;p&gt;After making this change, you should be able to search for &amp;quot;p&amp;quot;, &amp;quot;d&amp;quot; or &amp;quot;v&amp;quot; and
            see results as JSON.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;To make the results more readable, change the above HTML to use a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; and Angular&apos;s &lt;a
            href=&quot;https://docs.angularjs.org/api/ng/directive/ngRepeat&quot;&gt;ng-repeat&lt;/a&gt; directive.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;table ng-show=&quot;searchResults.length&quot; style=&quot;width: 100%&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 ng-repeat=&quot;person in searchResults&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;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This section has shown 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&gt;Add an Edit Feature&lt;/h3&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/search/index.html&lt;/code&gt;&amp;nbsp;to add a link for editing a person.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;table ng-show=&quot;searchResults.length&quot; style=&quot;width: 100%&quot;&amp;gt;
    ...
    &amp;lt;tr ng-repeat=&quot;person in searchResults&quot;&amp;gt;
        &amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot; ng-click=&quot;edit(person)&quot;&amp;gt;{{person.name}}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
        ...
    &amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add an &lt;code&gt;edit()&lt;/code&gt; function to &lt;code&gt;SearchController&lt;/code&gt;.
           Notice that the &lt;a href=&quot;https://docs.angularjs.org/api/ng/service/$location&quot;&gt;$location service&lt;/a&gt;
            dependency has been added to the controller&apos;s initialization function. &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.controller(&apos;SearchController&apos;, function ($scope, $location, SearchService) {
    ...

    $scope.edit = function (person) {
        $location.path(&quot;/edit/&quot; + person.id);
    }
})&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create a route to handle the edit URL in &lt;code&gt;app/search/search.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.config([&apos;$routeProvider&apos;, function ($routeProvider) {
    $routeProvider
        .when(&apos;/search&apos;, {
            templateUrl: &apos;search/index.html&apos;,
            controller: &apos;SearchController&apos;
        })
        .when(&apos;/edit/:id&apos;, {
            templateUrl: &apos;search/edit.html&apos;,
            controller: &apos;EditController&apos;
        });
}])&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;app/search/edit.html&lt;/code&gt; to display an editable form. The HTML below shows how you can use &lt;a
            href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes&quot;&gt;HTML5&apos;s data
            attributes&lt;/a&gt; to have valid attributes instead of ng-*.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;form ng-submit=&quot;save()&quot;&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;label for=&quot;name&quot;&amp;gt;Name:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.name&quot; id=&quot;name&quot;&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;label for=&quot;phone&quot;&amp;gt;Phone:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.phone&quot; id=&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 style=&quot;margin-left: 50px&quot;&amp;gt;
            &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.address.street&quot;&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.address.city&quot;&amp;gt;,
            &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.address.state&quot; size=&quot;2&quot;&amp;gt;
            &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.address.zip&quot; size=&quot;5&quot;&amp;gt;
        &amp;lt;/address&amp;gt;
    &amp;lt;/fieldset&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;SearchService&lt;/code&gt;&amp;nbsp;to contain functions for finding a person by their id, and saving
            them.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.factory(&apos;SearchService&apos;, function ($http) {
    var service = {
        query: function (term) {
            return $http.get(&apos;/search/&apos; + term);
        },
        fetch: function (id) {
            return $http.get(&apos;/edit/&apos; + id);
        },
        save: function(data) {
            return $http.post(&apos;/edit/&apos; + data.id, data);
        }
    };
    return service;
});&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;EditController&lt;/code&gt; in &lt;code&gt;app/search/search.js&lt;/code&gt;. &lt;a
            href=&quot;https://docs.angularjs.org/api/ngRoute/service/$routeParams&quot;&gt;$routeParams&lt;/a&gt; is an Angular service
            that allows you to grab parameters from a URL.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.controller(&apos;EditController&apos;, function ($scope, $location, $routeParams, SearchService) {
    SearchService.fetch($routeParams.id).then(function (response) {
        $scope.person = response.data;
    });

    $scope.save = function() {
        SearchService.save($scope.person).then(function(response) {
            $location.path(&quot;/search/&quot; + $scope.person.name);
        });
    }
})&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;At the bottom of &lt;code&gt;app/search/mock-api.js&lt;/code&gt;, in its &lt;code&gt;run()&lt;/code&gt; function, add the following:
        &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;$httpBackend.whenGET(/\/search/).respond(function (method, url, data) {
    var results = ServerDataModel.search(&quot;&quot;);

    return [200, results];
});

$httpBackend.whenGET(/\/edit\/\d+/).respond(function (method, url, data) {
    // parse the matching URL to pull out the id (/edit/:id)
    var id = url.split(&apos;/&apos;)[2];

    var results = ServerDataModel.find(id);

    return [200, results, {Location: &apos;/edit/&apos; + id}];
});

$httpBackend.whenPOST(/\/edit\/\d+/).respond(function(method, url, data) {
    var params = angular.fromJson(data);

    // parse the matching URL to pull out the id (/edit/:id)
    var id = url.split(&apos;/&apos;)[2];

    var person = ServerDataModel.update(id, params);

    return [201, person, { Location: &apos;/edit/&apos; + id }];
});

$httpBackend.whenGET(/search\/edit.html/).passThrough();&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Further up in the same file, add &lt;code&gt;find()&lt;/code&gt; and &lt;code&gt;update()&lt;/code&gt; methods to &lt;code&gt;ServerDataModel&lt;/code&gt;.
        &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;this.getData = function () {
    return this.data;
};

this.search = function (term) {
    ...
};

this.find = function (id) {
    // find the game that matches that id
    var list = $.grep(this.getData(), function (element, index) {
        return (element.id == id);
    });
    if (list.length === 0) {
        return {};
    }
    // even if list contains multiple items, just return first one
    return list[0];
};

this.update = function (id, dataItem) {
    // find the game that matches that id
    var people = this.getData();
    var match = null;
    for (var i = 0; i &lt; people.length; i++) {
        if (people[i].id == id) {
            match = people[i];
            break;
        }
    }
    if (!angular.isObject(match)) {
        return {};
    }
    angular.extend(match, dataItem);
    return match;
};&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;The &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; in &lt;code&gt;app/search/edit.html&lt;/code&gt;&amp;nbsp;calls a &lt;code&gt;save()&lt;/code&gt; function to update a
            person&apos;s data. You already implemented this above. The function executes the logic below.&amp;nbsp;&lt;/p&gt;
        &lt;pre class=&quot;brush: js&quot;&gt;$location.path(&quot;/search/&quot; + $scope.person.name);&lt;/pre&gt;
        &lt;p&gt;Since the SearchController doesn&apos;t execute a search automatically when you execute this URL, add the
            following logic to do so in &lt;code&gt;app/search/search.js&lt;/code&gt;. Note that
            &lt;code&gt;$routeParams&lt;/code&gt; is added to the list of injected dependencies.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.controller(&apos;SearchController&apos;, function ($scope, $location, $routeParams, SearchService) {
    if ($routeParams.term) {
        SearchService.query($routeParams.term).then(function (response) {
            $scope.term = $routeParams.term;
            $scope.searchResults = response.data;
        });
    }
    ...
})&lt;/pre&gt;
        &lt;p&gt;You&apos;ll also need to add a new route so search/term is recognized.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;.config([&apos;$routeProvider&apos;, function ($routeProvider) {
    $routeProvider
        .when(&apos;/search&apos;, {
            templateUrl: &apos;search/index.html&apos;,
            controller: &apos;SearchController&apos;
        })
        .when(&apos;/search/:term&apos;, {
            templateUrl: &apos;search/index.html&apos;,
            controller: &apos;SearchController&apos;
        })
        ...
}])&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;After making all these changes, you should be able to refresh your browser and search/edit/update a person&apos;s
        information. If it works - nice job!
    &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&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/angular-tutorial&quot;&gt;https://github.com/mraible/angular-tutorial&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;There are three commits that make the changes for the three main steps in this tutorial:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/commit/9792a0fbf1c2a5f1171498de7666e6f13cdd0537&quot;&gt;The
        Basics&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/commit/e9c277a50606d7ebaf9bcefa46f5942e2edf7ecf&quot;&gt;The
        Backend&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/commit/56be9decd0242dd60f06ef7232db723d6595ed0c&quot;&gt;Add
        an Edit Feature&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Extra Credit&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://linqed.eu/2014/10/07/deploying-angular-seed-to-heroku/&quot;&gt;Deploy your completed app to Heroku&lt;/a&gt;. See
    running version of this tutorial at &lt;a
        href=&quot;https://angular-123.herokuapp.com&quot;&gt;https://angular-123.herokuapp.com&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on how to get started with AngularJS. In a future tutorial, I&apos;ll show
you &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;how to write unit tests and integration tests for this application&lt;/a&gt;. If you&apos;re in Denver next Tuesday, I&apos;ll be &lt;a href=&quot;http://www.meetup.com/DOSUG1/events/219099019/&quot;&gt;speaking about AngularJS at Denver&apos;s Open Source Users Group&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&apos;re a Java developer that&apos;s interested in developing with AngularJS and Spring Boot, you might want to checkout
the &lt;a href=&quot;http://www.infoq.com/news/2015/01/jhipster-2.0&quot;&gt;recently released JHipster 2.0&lt;/a&gt;.&lt;/p&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/developing_an_ios_native_app</guid>
    <title>Developing an iOS Native App with Ionic</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/developing_an_ios_native_app</link>
        <pubDate>Thu, 27 Mar 2014 16:38:55 -0600</pubDate>
    <category>The Web</category>
    <category>ionic</category>
    <category>ios</category>
    <category>mobile</category>
    <category>html5</category>
    <category>angularjs</category>
    <category>ionicframework</category>
    <atom: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;</atom:summary>        <description>&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;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/the_art_of_angularjs</guid>
    <title>The Art of AngularJS</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/the_art_of_angularjs</link>
        <pubDate>Thu, 27 Feb 2014 09:44:29 -0700</pubDate>
    <category>The Web</category>
    <category>angularjs</category>
    <category>coffeescript</category>
    <category>http2</category>
    <category>javascript</category>
    <category>protractor</category>
    <category>html5</category>
    <category>jasmine</category>
    <category>grunt</category>
    <category>derailed</category>
            <description>Last night, I had the pleasure of &lt;a href=&quot;http://www.meetup.com/DeRailed/events/164446322/&quot;&gt;speaking at Denver&apos;s DeRailed about AngularJS&lt;/a&gt;. &lt;a href=&quot;https://twitter.com/kitesurfer&quot;&gt;Fernand&lt;/a&gt; (the group&apos;s leader) asked me to speak in December, just after I&apos;d finished a &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries&quot;&gt;European speaking tour&lt;/a&gt;. The Modern Java Web Developer talk I created for that tour included a &lt;a href=&quot;https://vimeo.com/80314102&quot;&gt;20-minute AngularJS Deep Dive&lt;/a&gt; screencast. I figured it wouldn&apos;t be much work to augment the screencast and create an hour long talk, so I agreed.
&lt;/p&gt;
&lt;p&gt;When I started creating the presentation last week, I decided I didn&apos;t want to make the audience watch my screencast as part of the presentation. They could easily do that on their own time. So I wrote, from scratch, a brand new presentation on AngularJS. I tried to include all the things about Angular that I thought were important and useful for me in my learning process. The result is a presentation I&apos;m proud of and enjoyed delivering. &lt;/p&gt;
&lt;p&gt;

&lt;p&gt;You can click through it 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-art-of-angularjs&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/31721908?rel=0&quot; width=&quot;512&quot; height=&quot;325&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;/p&gt;

&lt;p&gt;You might notice the presentation has a whole lot of code in it. Normally, when I copy/paste code into a presentation, I use IntelliJ IDEA and everything works. This time, there was something amiss between IDEA 13 and Keynote 6. I tried using IDEA&apos;s plugins (namely &lt;a href=&quot;http://plugins.jetbrains.com/plugin/7198&quot;&gt;Copy on steroids&lt;/a&gt; and &lt;a href=&quot;http://plugins.jetbrains.com/plugin/190&quot;&gt;Copy as HTML&lt;/a&gt;), but none of them worked. IDEA 12 resulted in the same problem. Then I turned to other solutions. I &lt;a href=&quot;https://gist.github.com/jimbojsb/1630790&quot;&gt;installed highlight&lt;/a&gt; and copied code from the command line. This worked, but the fonts and colors weren&apos;t to my liking. Finally, I decided to try another editor: &lt;a href=&quot;http://www.sublimetext.com/&quot;&gt;Sublime Text&lt;/a&gt; with &lt;a href=&quot;https://github.com/n1k0/SublimeHighlight&quot;&gt;SublimeHighlight&lt;/a&gt;. This worked &lt;em&gt;great&lt;/em&gt; and I&apos;m very happy with the results.
&lt;/p&gt;
&lt;p&gt;Most of my presentations end with a Questions/Contact slide. For this one, I added a few more: people to follow on Twitter, resources to learn from and projects with useful code. Below are a handful of links that greatly enhanced my AngularJS knowledge in the last year.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.parleys.com/play/5148922b0364bc17fc56c91b&quot;&gt;Devoxx 2012 - Re-imagining the browser with AngularJS&lt;/a&gt;. This is the original video I watched about AngularJS. I learned enough from this one video to start developing my first app for a client. I wrote about it in a four-part series on &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv&quot;&gt;Developing with AngularJS&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ng-book.com/&quot;&gt;ng-book: The Complete Book on AngularJS&lt;/a&gt;. A great book with all the nitty-gritty Angular details you ever wanted to know.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=UYVcY9EJcRs&quot;&gt;David Mosher&apos;s Testing Strategies for AngularJS&lt;/a&gt;. I stumbled upon this a week ago and it&apos;s greatly enhanced my knowledge of how to test AngularJS apps. It also introduced me to &lt;a href=&quot;http://linemanjs.com/&quot;&gt;Lineman&lt;/a&gt;, which I&apos;m thankful for.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/&quot;&gt;Egghead.io&lt;/a&gt; - bit-sized videos of AngularJS knowledge. Very useful for when you want to learn how to do a specific thing quickly.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/philipsorst/angular-rest-springsecurity&quot;&gt;AngularJS + REST + Spring Security&lt;/a&gt;. This is a sample application from &lt;a href=&quot;http://sorst.net/&quot;&gt;Philip Sorst&lt;/a&gt; (&lt;a href=&quot;https://github.com/joshlong/boot-examples/tree/master/x-auth-security&quot;&gt;ported to Spring Boot&lt;/a&gt; by my good friend &lt;a href=&quot;http://www.joshlong.com/&quot;&gt;Josh Long&lt;/a&gt;) that shows how to integrate AngularJS with Spring Security for stateless, token-based authentication.
&lt;/ul&gt;
&lt;p&gt;One of the audience members at DeRailed recommended &lt;a href=&quot;http://www.thinkster.io/&quot;&gt;thinkster.io&lt;/a&gt; as a good resource too.&lt;/p&gt;
&lt;p&gt;Thanks to Fernand for inviting me to speak and causing me to write this presentation. Creating it greatly improved my AngularJS knowledge and I learned about some new tools in the process. If you&apos;d like to tap into my wealth of knowledge, I&apos;m available for a new gig in April. &lt;img src=&quot;//raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot;&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/you_shouldn_t_have_to</guid>
    <title>You shouldn&apos;t have to worry about front end optimization</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/you_shouldn_t_have_to</link>
        <pubDate>Thu, 16 Jan 2014 13:49:03 -0700</pubDate>
    <category>The Web</category>
    <category>pagespeed</category>
    <category>optimization</category>
    <category>http</category>
    <category>concatenation</category>
    <category>http2</category>
    <category>minification</category>
    <atom: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;</atom:summary>        <description>&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?</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for</guid>
    <title>Using Grunt with AngularJS for Front End Optimization</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for</link>
        <pubDate>Wed, 15 Jan 2014 12:15:52 -0700</pubDate>
    <category>The Web</category>
    <category>angularjs</category>
    <category>grunt</category>
    <category>pagespeed</category>
    <category>yslow</category>
    <atom: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;</atom:summary>        <description>&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;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv</guid>
    <title>Developing with AngularJS - Part IV: Making it Pop</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv</link>
        <pubDate>Thu, 12 Sep 2013 10:54:29 -0600</pubDate>
    <category>The Web</category>
    <category>design</category>
    <category>css3</category>
    <category>responsivedesign</category>
    <category>javascript</category>
    <category>taleo</category>
    <category>angularjs</category>
    <atom: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;</atom:summary>        <description>&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;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii</guid>
    <title>Developing with AngularJS - Part III: Services</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii</link>
        <pubDate>Tue, 25 Jun 2013 07:03:26 -0600</pubDate>
    <category>The Web</category>
    <category>rest</category>
    <category>angularjs</category>
    <category>taleo</category>
    <category>javascript</category>
    <category>dwr</category>
    <atom:summary type="html">&lt;p&gt;This is the 3rd article in a series on my experience developing with &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. I used AngularJS for several months to create a &quot;My Dashboard&quot; feature for a client and learned a whole bunch of Angular goodness along the way. For previous articles, please see &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;Part I: The Basics&lt;/a&gt; and &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;/p&gt;
&lt;p&gt;Angular offers several ways to interact with data from the server. The easiest way is to use the &lt;a href=&quot;http://docs.angularjs.org/api/ngResource.$resource&quot;&gt;$resource factory&lt;/a&gt;, which lets you interact with &lt;a href=&quot;http://en.wikipedia.org/wiki/Representational_State_Transfer&quot;&gt;RESTful&lt;/a&gt; server-side data sources. When we started the My Dashboard project, we were hoping to interact with a REST API, but soon found out that it didn&apos;t have all the data we needed. Rather than loading the page and then making another request to get its data, we decided to embed the JSON in the page. For communication back to the server, we used our tried-and-true Ajax solution: &lt;a href=&quot;http://directwebremoting.org/&quot;&gt;DWR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In Angular-speak, &lt;em&gt;services&lt;/em&gt; are &lt;a href=&quot;http://docs.angularjs.org/guide/dev_guide.services.understanding_services&quot;&gt;singletons that carry out specific tasks common to web apps&lt;/a&gt;. In other words, they&apos;re any &lt;em&gt;$name&lt;/em&gt; object that can be injected into a controller or directive. However, as a Java Developer, I tend to think of services as objects that communicate with the server. Angular&apos;s documentation on &lt;a href=&quot;http://docs.angularjs.org/guide/dev_guide.services.creating_services&quot;&gt;Creating Services&lt;/a&gt; shows you various options for registering services. I used the angular.Module api method.&lt;/p&gt;</atom:summary>        <description>&lt;p&gt;
&lt;a href=&quot;http://angularjs.org/&quot; title=&quot;AngularJS&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7445/9137074888_9d3bb13d32_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;AngularJS&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
This is the 3rd article in a series on my experience developing with &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. I used AngularJS for several months to create a &quot;My Dashboard&quot; feature for a client and learned a whole bunch of Angular goodness along the way. For previous articles, please see &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;Part I: The Basics&lt;/a&gt; and &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;/p&gt;
&lt;p&gt;Angular offers several ways to interact with data from the server. The easiest way is to use the &lt;a href=&quot;http://docs.angularjs.org/api/ngResource.$resource&quot;&gt;$resource factory&lt;/a&gt;, which lets you interact with &lt;a href=&quot;http://en.wikipedia.org/wiki/Representational_State_Transfer&quot;&gt;RESTful&lt;/a&gt; server-side data sources. When we started the My Dashboard project, we were hoping to interact with a REST API, but soon found out that it didn&apos;t have all the data we needed. Rather than loading the page and then making another request to get its data, we decided to embed the JSON in the page. For communication back to the server, we used our tried-and-true Ajax solution: &lt;a href=&quot;http://directwebremoting.org/&quot;&gt;DWR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In Angular-speak, &lt;em&gt;services&lt;/em&gt; are &lt;a href=&quot;http://docs.angularjs.org/guide/dev_guide.services.understanding_services&quot;&gt;singletons that carry out specific tasks common to web apps&lt;/a&gt;. In other words, they&apos;re any &lt;em&gt;$name&lt;/em&gt; object that can be injected into a controller or directive. However, as a Java Developer, I tend to think of services as objects that communicate with the server. Angular&apos;s documentation on &lt;a href=&quot;http://docs.angularjs.org/guide/dev_guide.services.creating_services&quot;&gt;Creating Services&lt;/a&gt; shows you various options for registering services. I used the angular.Module api method.&lt;/p&gt;

&lt;p&gt;When I last worked on the project, there were only two services in My Dashboard: Widget and Preferences.&lt;/p&gt;

&lt;h3 id=&quot;widget&quot;&gt;Widget Service&lt;/h3&gt;
&lt;p&gt;The Widget service is used to retrieve the visible widgets for the user. It has two functions that are exposed to controllers: &lt;code&gt;getUserWidgets(type)&lt;/code&gt; and &lt;code&gt;getHiddenWidgets(type)&lt;/code&gt;. The former function is used at the top of &lt;code&gt;WidgetController&lt;/code&gt;, while the latter is used for the configuration dialog mentioned in the &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii&quot;&gt;previous article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The code for this service is in &lt;em&gt;services.js&lt;/em&gt;. The bulk of the logic is in its &lt;code&gt;filterData()&lt;/code&gt; function, where it goes through a 4-step process:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Get all the widgets by type, ensuring they&apos;re unique.&lt;/li&gt;&lt;li&gt;Remove the widgets that are &lt;em&gt;hidden&lt;/em&gt; by the user&apos;s preferences.&lt;/li&gt;&lt;li&gt;Build an array that&apos;s &lt;em&gt;ordered&lt;/em&gt; by user&apos;s preferences.&lt;/li&gt;&lt;li&gt;Add any new widgets that aren&apos;t &lt;em&gt;hidden&lt;/em&gt; or &lt;em&gt;ordered&lt;/em&gt;.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;The code for the Widget object is as follows:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
angular.module(&apos;dashboard.services&apos;, &amp;#91;&amp;#93;).
    factory(&apos;Widget&apos;,function ($filter, Preferences) {
        var filter = $filter(&apos;filter&apos;);
        var unique = $filter(&apos;unique&apos;);

        function filterData(array, query) {
            // get all possible widgets for a particular type
            var data = filter(array, query);
            data = unique(data);

            // remove widgets that are hidden by users preference
            var hidden = Preferences.getHiddenWidgets(query.type);
            for (var i = 0; i &amp;lt; hidden.length; i++) {
                var w = filter(data, {id: hidden&amp;#91;i&amp;#93;});
                $.each(w, function (index, item) {
                    var itemId = item.id;
                    if (hidden.indexOf(itemId) &amp;gt; -1) {
                        data.splice(data.indexOf(item), 1);
                    }
                });
            }

            // build an array that&apos;s ordered by users preference
            var ordered = &amp;#91;&amp;#93;;
            var visible = Preferences.getUserWidgets(query.type);
            for (var j = 0; j &amp;lt; visible.length; j++) {
                var v = filter(data, {id: visible&amp;#91;j&amp;#93;});
                $.each(v, function (index, item) {
                    var itemId = item.id;
                    if (visible.indexOf(itemId) &amp;gt; -1) {
                        ordered.push(item)
                    }
                });
            }

            // loop through data again and add any new widgets not in ordered
            $.each(data, function (index, item) {
                if (ordered.indexOf(item) === -1) {
                    ordered.push(item);
                }
            });

            return ordered;
        }

        return {
            getUserWidgets: function (type) {
                return filterData(widgetData, {type: type})
            },

            getHiddenWidgets: function (type) {
                var hidden = Preferences.getHiddenWidgets(type);
                var widgetsForType = filter(widgetData, {type: type});
                widgetsForType = unique(widgetsForType);
                var widgets = &amp;#91;&amp;#93;;
                for (var j = 0; j &amp;lt; hidden.length; j++) {
                    var v = filter(widgetsForType, {id: hidden&amp;#91;j&amp;#93;});
                    $.each(v, function (index, item) {
                        if (widgetsForType.indexOf(item) &amp;gt; -1) {
                            widgets.push(item)
                        }
                    });
                }
                return widgets;
            }
        }
    })
&lt;/pre&gt;
&lt;p&gt;Once you have a service configured like this, you can inject it by name. For example, &lt;code&gt;WidgetController&lt;/code&gt; has &lt;code&gt;Widget&lt;/code&gt; injected into its constructor:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
function WidgetController($dialog, $scope, Widget, Preferences) {
&lt;/pre&gt;
&lt;h3 id=&quot;preferences&quot;&gt;Preferences Service&lt;/h3&gt;
&lt;p&gt;The Preferences service is used to get and save user preferences. It&apos;s pretty straightforward and the bulk of its code is interacting with DWR. This service has 5 methods:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;getHiddenWidgets(type) - used by Widget service&lt;/li&gt;&lt;li&gt;getUserWidgets(type) - used by Widget service&lt;/li&gt;&lt;li&gt;saveBarOrder(bars) - called from WidgetController&lt;/li&gt;&lt;li&gt;saveWidgetOrder(type, widgets) - called from WidgetController&lt;/li&gt;&lt;li&gt;saveWidgetPreferences(type, widgets) - called from WidgetController&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;First, let&apos;s take a look at the &lt;code&gt;save*Order()&lt;/code&gt; functions. There are two parts of the page that use the &lt;em&gt;ui-sortable&lt;/em&gt; directive to initialize drag-and-drop functionality. The first is on the main &amp;lt;ul&amp;gt; that holds the 3 bars on the left.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;ul class=&quot;widgets&quot; ui-sortable=&quot;{handle:&apos;.heading&apos;, update: updateBars}&quot;&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The &quot;update&quot; property in the configuration JSON indicates which method to call in the controller. Similarly, the tasks and summary items call an &lt;code&gt;updateOrder&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;ul class=&quot;summary-items&quot; ng-model=&quot;summaryWidgets&quot; ui-sortable=&quot;{update: updateOrder}&quot;&amp;gt;
...
&amp;lt;ul class=&quot;task-items&quot; ng-model=&quot;taskWidgets&quot; ui-sortable=&quot;{update: updateOrder}&quot;&amp;gt;
&lt;/pre&gt;
&lt;p&gt;These functions are in &lt;code&gt;WidgetController&lt;/code&gt; and build an array of widget ids to pass to the Preferences service.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$scope.updateBars = function(event, ui) {
    var bars = &amp;#91;&amp;#93;;
    $.each($(ui.item).parent().children(), function (index, item) {
        bars.push(item.id.substring(0, item.id.indexOf(&apos;-&apos;)))
    });
    Preferences.saveBarOrder(bars);
};

$scope.updateOrder = function(event, ui) {
    var parentId = $(ui.item).parent().parent().attr(&apos;id&apos;);
    var type = parentId.substring(0, parentId.indexOf(&apos;-&apos;));
    var items = &amp;#91;&amp;#93;;
    $.each($(ui.item).parent().children(), function (index, item) {
        items.push(item.id.substring(item.id.indexOf(&apos;-&apos;) + 1))
    });
    Preferences.saveWidgetOrder(type, {items: items});
};
&lt;/pre&gt;
&lt;p&gt;The bar order is used when the page is loaded. The following scriptlet code exists at the bottom of the app&apos;s page, in its $(document).ready:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;%  String barOrder = user.getDashboardBarSortOrder();
    if (barOrder != null) { %&amp;gt;
    sortBars(&amp;#91;&apos;&amp;lt;%= barOrder %&amp;gt;&apos;&amp;#93;);
&amp;lt;% } %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;sortBars()&lt;/code&gt; function is in a &lt;em&gt;dashboard.js&lt;/em&gt; file (where we put all non-Angular functions):&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
function sortBars(barOrder) {
    // Sort bars according to user preferences
    $.each(barOrder, function(index, item) {
        var bar = $(&apos;#&apos; + item + &apos;-bar&apos;);
        if (bar.index() !== index) {
            if (index === 0) {
                bar.insertBefore($(&apos;.widgets&amp;gt;li:first-child&apos;));
            } else if (index === (barOrder.length - 1)) {
                bar.insertAfter($(&apos;.widgets&amp;gt;li:last-child&apos;));
            } else {
                bar.insertBefore($(&apos;.widgets&amp;gt;li:eq(&apos; + index + &apos;)&apos;));
            }
        }
    });
}
&lt;/pre&gt;
&lt;p&gt;Now that you&apos;ve seen where Preferences is called from, let&apos;s take a look at the code for the service.&lt;/p&gt;
&lt;div class=&quot;alert alert-info&quot;&gt;
The checks for undefined and uniqueness in the code below shouldn&apos;t be necessary, but I prefer defensive coding.
&lt;/div&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
factory(&apos;Preferences&apos;, function ($filter) {
    var unique = $filter(&apos;unique&apos;);

    return {
        // Get in-page variable: hiddenWidgets
        getHiddenWidgets: function (type) {
            var items = hiddenWidgets&amp;#91;type&amp;#93;;
            return (angular.isUndefined(items) ? &amp;#91;&amp;#93; : unique(items));
        },

        // Get in-page variable: userWidgets
        getUserWidgets: function (type) {
            var items = userWidgets&amp;#91;type&amp;#93;;
            return (angular.isUndefined(items) ? &amp;#91;&amp;#93; : unique(items));
        },

        // Save main bar (task, summary, chart) order
        saveBarOrder: function (bars) {
            DWRFacade.saveDashboardBarSortOrder(bars, {
                errorHandler: function (errorString) {
                    alert(errorString);
                }
            })
        },

        // Save order of widgets from sortable
        saveWidgetOrder: function (type, widgets) {
            userWidgets&amp;#91;type&amp;#93; = widgets.items;
            DWRFacade.saveDashboardWidgetPreference(type, widgets, {
                errorHandler: function (errorString) {
                    alert(errorString);
                }
            });
        },

        // Save hidden and visible (and order) widgets from config dialog
        saveWidgetPreferences: function (type, widgets) {
            // widgets is a map of hidden and visible
            var hiddenIds = &amp;#91;&amp;#93;;
            $.each(widgets.hidden, function (index, item) {
                hiddenIds.push(item.id);
            });
            var visibleIds = &amp;#91;&amp;#93;;
            $.each(widgets.items, function (index, item) {
                visibleIds.push(item.id);
            });
            var preferences = {
                hidden: hiddenIds,
                items: visibleIds
            };
            // reset local variables in page
            hiddenWidgets&amp;#91;type&amp;#93; = hiddenIds;
            userWidgets&amp;#91;type&amp;#93; = visibleIds;
            DWRFacade.saveDashboardWidgetPreference(type, preferences, {
                errorHandler: function (errorString) {
                    alert(errorString);
                }
            });
        }
    }
})
&lt;/pre&gt;
&lt;h3 id=&quot;http&quot;&gt;Using $http and Receiving Data&lt;/h3&gt;
&lt;p&gt;In this particular application, we didn&apos;t do any reading from the server with Angular. We simply wrote preferences to the server, and updated embedded variables when data changed. Real-time functionality of the app wouldn&apos;t be noticeable if a write failed. 
&lt;/p&gt;
&lt;p&gt;
In my current Angular project, it&apos;s more of a full-blown application that does as much reading as writing. For this, I&apos;ve found it useful to either 1) pass in callbacks to services or 2) use Angular&apos;s event system to publish/subscribe to events.
&lt;/p&gt;
&lt;p&gt;The first method is the easiest, and likely the most familiar to JavaScript developers. For example, here&apos;s the controller code to remove a profile picture:
&lt;/p&gt;

&lt;pre class=&quot;brush: js&quot;&gt;
Profile.removePhoto($scope.user, function (data) {
    // close the dialog
    $scope.close(&apos;avatar&apos;);
    // success message using toastr: http://bit.ly/14Uisgm
    Flash.pop({type: &apos;success&apos;, body: &apos;Your profile picture was removed.&apos;});
})
&lt;/pre&gt;
&lt;p&gt;And the &lt;code&gt;Profile.removePhoto()&lt;/code&gt; method:
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
removePhoto: function (user, callback) {
    $http.post(&apos;/profile/removePhoto&apos;, user).success(function (response) {
        return callback(response);
    });
}
&lt;/pre&gt;
&lt;p&gt;The second, event-driven method works equally as well, but can easily suffer from typos in event names.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
// controller calling code
Profile.getUser();

// service code
getUser: function () {
    $http.get(&apos;/profile&apos;).success(function (data) {
        if (data.username) {
            $log.info(&apos;Profile for &apos; + data.username + &apos; retrieved!&apos;);
            $rootScope.$broadcast(&apos;event:profile&apos;, data);
        }
    });
}

// controller receiving code
$rootScope.$on(&apos;event:profile&apos;, function (event, data) {
    $scope.user = data;
});
&lt;/pre&gt;
&lt;p&gt;I like both methods, but the event-driven one seems like it could offer more extensibility in the future.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Using in-page variables and DWR doesn&apos;t seem to be recommended by the Angular Team. However, it worked well for us and seems like a good way to construct Angular services. Even if a REST API becomes available to get all the data, I think using in-page variables to minimize requests is a good idea. &lt;/p&gt;
&lt;p&gt;When retrieving data, you can use callbacks or Angular&apos;s pub/sub event system ($broadcast and $on) to get data in your controllers. If you want to learn more about this technique, see Eric Terpstra&apos;s &lt;a href=&quot;http://ericterpstra.com/2012/09/angular-cats-part-3-communicating-with-broadcast/&quot;&gt;Communicating with $broadcast&lt;/a&gt;. In his article, Eric mentions &lt;a href=&quot;https://groups.google.com/d/msg/angular/M0SHItdgBqg/R1t_17cR0pYJ&quot;&gt;Thomas Burleson&apos;s pub/sub module&lt;/a&gt; that acts as a message queue. If you&apos;ve used Thomas&apos;s MessageQueue (or something similar) with Angular, I&apos;d love to hear about your experience. 
&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv&quot;&gt;next article&lt;/a&gt;, I&apos;ll talk about how we redesigned My Dashboard and used CSS3 and JavaScript to implement new ideas.&lt;/p&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii</guid>
    <title>Developing with AngularJS - Part II: Dialogs and Data</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii</link>
        <pubDate>Thu, 20 Jun 2013 08:45:13 -0600</pubDate>
    <category>The Web</category>
    <category>angularui</category>
    <category>jqueryui</category>
    <category>jquery</category>
    <category>angularjs</category>
    <category>javascript</category>
    <category>taleo</category>
    <atom:summary type="html">&lt;p&gt;A couple of days ago, I wrote an article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;how I started developing with AngularJS&lt;/a&gt;. I used AngularJS for several months to develop a &quot;My Dashboard&quot; feature for a client&apos;s product and learned a whole bunch of stuff along the way. &lt;/p&gt;
&lt;p&gt;This article provides an overview of how I changed some of My Dashboard&apos;s features to use Angular instead of jQuery. After finishing the prototype work in January, we started moving bits and pieces into the main application. We kept the same file names for our Angular-related files and copied them into the project.
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://www.flickr.com/photos/mraible/8904352343/&quot; title=&quot;Directory Structure&quot;&gt;&lt;img src=&quot;http://farm3.staticflickr.com/2856/8904352343_20beb87da8_o.png&quot; width=&quot;280&quot; height=&quot;268&quot; alt=&quot;Directory Structure&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;All these files are packaged up into a &lt;code&gt;dashboard.js&lt;/code&gt; file that&apos;s included at the bottom of our Dashboard page. While our prototype used jQuery 1.9 and jQuery UI 1.10, the application&apos;s codebase used jQuery 1.7.1 and jQuery UI 1.8.3. Luckily, this didn&apos;t present a problem as everything continued to work as expected.&lt;/p&gt;
&lt;p&gt;Around this time, we also had many discussions with the Product Team about charts. Since Highcharts required we purchase a license, we took at look at &lt;a href=&quot;http://www.anychart.com/&quot;&gt;AnyChart&lt;/a&gt;, which we were already using. We were able to get AnyChart to work with our existing &lt;em&gt;chart&lt;/em&gt; directive with minimal changes. Most changes were in the JSON itself. &lt;/p&gt;
&lt;p&gt;We committed the first pass (with sample data still hard-coded) in mid-February.&lt;/p&gt;</atom:summary>        <description>&lt;p&gt;A couple of days ago, I wrote an article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;how I started developing with AngularJS&lt;/a&gt;. I used AngularJS for several months to develop a &quot;My Dashboard&quot; feature for a client&apos;s product and learned a whole bunch of stuff along the way. &lt;/p&gt;
&lt;p&gt;This article provides an overview of how I changed some of My Dashboard&apos;s features to use Angular instead of jQuery. After finishing the prototype work in January, we started moving bits and pieces into the main application. We kept the same file names for our Angular-related files and copied them into the project.
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://www.flickr.com/photos/mraible/8904352343/&quot; title=&quot;Directory Structure&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2856/8904352343_20beb87da8_o.png&quot; width=&quot;280&quot; height=&quot;268&quot; alt=&quot;Directory Structure&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;All these files are packaged up into a &lt;code&gt;dashboard.js&lt;/code&gt; file that&apos;s included at the bottom of our Dashboard page. While our prototype used jQuery 1.9 and jQuery UI 1.10, the application&apos;s codebase used jQuery 1.7.1 and jQuery UI 1.8.3. Luckily, this didn&apos;t present a problem as everything continued to work as expected.&lt;/p&gt;
&lt;p&gt;Around this time, we also had many discussions with the Product Team about charts. Since Highcharts required we purchase a license, we took at look at &lt;a href=&quot;http://www.anychart.com/&quot;&gt;AnyChart&lt;/a&gt;, which we were already using. We were able to get AnyChart to work with our existing &lt;em&gt;chart&lt;/em&gt; directive with minimal changes. Most changes were in the JSON itself. &lt;/p&gt;
&lt;p&gt;We committed the first pass (with sample data still hard-coded) in mid-February.&lt;/p&gt;
&lt;h3 id=&quot;angular-ui-carousel&quot;&gt;Angular UI&apos;s Carousel&lt;/h3&gt;
&lt;p&gt;While finishing our initial prototype, I learned about Angular UI&apos;s &lt;a href=&quot;https://github.com/angular-ui/bootstrap/tree/master/src/carousel&quot;&gt;Carousel&lt;/a&gt;, an implementation of Bootstrap&apos;s Carousel. It required a 70% less HTML and is quite a bit easier to read. Below is the refactored carousel section.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;carousel interval=&quot;-1&quot; class=&quot;oneup&quot;&amp;gt;
    &amp;lt;slide ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot; active=&quot;slide.active&quot; class=&quot;chart&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;/slide&amp;gt;
&amp;lt;/carousel&amp;gt;
&amp;lt;carousel interval=&quot;-1&quot; class=&quot;twoup&quot;&amp;gt;
    &amp;lt;slide ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | chunk: 2 | orderBy: &apos;order&apos;&quot; active=&quot;slide.active&quot; class=&quot;chart&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;/slide&amp;gt;
&amp;lt;/carousel&amp;gt;
&lt;/pre&gt;
&lt;p&gt;I was also able to remove the JavaScript that once initialized the carousel.&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-    var carousel = $(&apos;.carousel&apos;);
-    $(carousel).carousel({
-        interval: 0
-    });
&lt;/pre&gt;
&lt;p&gt;While integrating this directive, I found a way to improve it by &lt;a href=&quot;https://github.com/angular-ui/bootstrap/commit/aedc05654b19342d96eabc4fc08d6a090765a48b&quot;&gt;hiding navigation indicators if there&apos;s only one slide&lt;/a&gt;. And thus my first contribution to Angular UI was born.&lt;/p&gt;
&lt;h3 id=&quot;angular-ui-sortable&quot;&gt;Angular UI&apos;s Sortable&lt;/h3&gt;
&lt;p&gt;Next, I switched from using JavaScript to Angular UI&apos;s &lt;a href=&quot;https://github.com/angular-ui/ui-sortable&quot;&gt;&lt;em&gt;sortable&lt;/em&gt;&lt;/a&gt;&lt;em&gt; &lt;/em&gt;to initialize drag-and-drop functionality. This was as simple as removing 5 lines of JavaScript and adding &quot;ui-sortable&quot; as an attribute to HTML tags.&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&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;ul class=&quot;widgets&quot; ui-sortable=&quot;{handle:&apos;.heading&apos;}&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;a href=&apos;#&apos; class=&apos;configure&apos;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;
-                        &amp;lt;ul class=&quot;tiles&quot;&amp;gt;
+                        &amp;lt;ul class=&quot;tiles&quot; ui-sortable&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&amp;gt;{{widget.value}}&amp;lt;/h3&amp;gt;
@@ -36,7 +36,7 @@
                 &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;a href=&apos;#&apos; class=&apos;configure&apos;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;
-                        &amp;lt;ul class=&quot;tasks&quot;&amp;gt;
+                        &amp;lt;ul class=&quot;tasks&quot; ui-sortable&amp;gt;
@@ -151,55 +131,44 @@
-    $(&apos;.widgets&apos;).sortable({
-        cursor: &quot;move&quot;,
-        handle: &quot;.heading&quot;
-    }).disableSelection();
-    $(&apos;.tiles,.tasks&apos;).sortable();
&lt;/pre&gt;
&lt;p&gt;This directive uses jQuery UI under the covers, so it accepts all the same arguments you&apos;d normally use.&lt;/p&gt;
&lt;h3 id=&quot;dialogs-with-jquery&quot;&gt;Dialogs with jQuery&lt;/h3&gt;
&lt;p&gt;The next feature I implemented was a dialog that allowed end users to add/remove widgets from their dashboard. Since I knew how to do this with jQuery, that&apos;s the path I started down. I figured it&apos;d be easier to get something working quickly and then refactor to Angular later. I put the HTML for the dialog at the bottom of the page:
&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;js/move.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;js/upDown.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;configure-widgets&quot; class=&quot;hidden&quot; data-title=&quot;Bar Configuration&quot; data-close=&quot;Close&quot;&amp;gt;
    &amp;lt;div class=&quot;row-fluid center&quot;&amp;gt;
        &lt;p&gt;Decide which items you would like to display and set the display order.&lt;/p&gt;
        &amp;lt;div class=&quot;span5&quot; style=&quot;margin-left: 25px&quot;&amp;gt;
            &amp;lt;label for=&quot;available-widgets&quot; class=&quot;bold&quot;&amp;gt;Available Widgets&amp;lt;/label&amp;gt;
            &amp;lt;select size=10 id=&quot;available-widgets&quot; multiple class=&quot;width100pr&quot;&amp;gt;&amp;lt;/select&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;span1 arrows&quot; style=&quot;padding-top: 50px&quot;&amp;gt;
            &amp;lt;img src=&quot;images/arrow_up.png&quot; class=&quot;arrow arrow-up&quot; alt=&quot;Move Up&quot; onclick=&quot;return moveUp($(&apos;#assigned-widgets&apos;)&amp;#91;0&amp;#93;);&quot;/&amp;gt;
            &amp;lt;img src=&quot;images/arrow_right.png&quot; class=&quot;arrow arrow-right&quot; alt=&quot;Move Right&quot; onclick=&quot;return move($(&apos;#available-widgets&apos;)&amp;#91;0&amp;#93;, $(&apos;#assigned-widgets&apos;)&amp;#91;0&amp;#93;);&quot;/&amp;gt;
            &amp;lt;img src=&quot;images/arrow_left.png&quot; class=&quot;arrow arrow-left&quot; alt=&quot;Move Left&quot; onclick=&quot;return move($(&apos;#assigned-widgets&apos;)&amp;#91;0&amp;#93;, $(&apos;#available-widgets&apos;)&amp;#91;0&amp;#93;);&quot;/&amp;gt;
            &amp;lt;img src=&quot;images/arrow_down.png&quot; class=&quot;arrow arrow-down&quot; alt=&quot;Move Down&quot; onclick=&quot;return moveDown($(&apos;#assigned-widgets&apos;)&amp;#91;0&amp;#93;);&quot;/&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;span5&quot;&amp;gt;
            &amp;lt;label for=&quot;assigned-widgets&quot; class=&quot;bold&quot;&amp;gt;Assigned Widgets&amp;lt;/label&amp;gt;
            &amp;lt;select size=10 id=&quot;assigned-widgets&quot; multiple class=&quot;width100pr&quot;&amp;gt;&amp;lt;/select&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Then I changed the &lt;code&gt;WidgetController&lt;/code&gt; to split the &lt;code&gt;widgets&lt;/code&gt; variable into variables for each widget type.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
+    var filter = $filter(&apos;filter&apos;);
+    var orderBy = $filter(&apos;orderBy&apos;);
+
+    $scope.summaryWidgets = filterData($scope.widgets, {type: &apos;summary&apos;}, filter, orderBy);
+    $scope.taskWidgets = filterData($scope.widgets, {type: &apos;task&apos;}, filter, orderBy);
+    $scope.chartWidgets = filterData($scope.widgets, {type: &apos;chart&apos;}, filter, orderBy);
+}
+
+function filterData(array, query, filter, orderBy) {
+    var data = filter(array, query);
+    return orderBy(data, &apos;order&apos;);
+}
&lt;/pre&gt;
&lt;p&gt;Finally, I added a new &lt;em&gt;config&lt;/em&gt; directive and hooked it into the page by adding &lt;em&gt;config=&quot;type&quot; &lt;/em&gt;to each heading (e.g. &lt;code&gt;&amp;lt;div class=&quot;heading&quot; config=&quot;task&quot;&amp;gt;My Tasks&amp;lt;/div&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;

.directive(&apos;config&apos;, function ($sanitize, $filter) {
    return {
        restrict: &apos;A&apos;,
        link: function (scope, element, attrs) {
            var configBtn = $(&apos;&amp;lt;a href=&quot;#&quot;/&amp;gt;&apos;).addClass(&apos;configure&apos;);
            configBtn.appendTo(element);
            var widgets = scope&amp;#91;attrs.config + &apos;Widgets&apos;&amp;#93;;
             
            // availableWidgets is defined as a global variable embedded in the page
            var allWidgets = availableWidgets; 
            $(configBtn).on(&apos;click&apos;, function (e) {
                e.preventDefault();
                var configDialog = $(&apos;#configure-widgets&apos;);
                var availableWidgets = $(&apos;#available-widgets&apos;);
                availableWidgets.empty();
                var filter = $filter(&apos;filter&apos;);
                var orderBy = $filter(&apos;orderBy&apos;);
                allWidgets = filter(allWidgets, {type: attrs.config});
                allWidgets = orderBy(allWidgets, &apos;order&apos;);
                var unselectedWidgets = jQuery.grep(allWidgets, function(item) {
                    return jQuery.inArray(item, widgets) &amp;lt; 0;
                });
                $.each(unselectedWidgets, function(index, item) {
                    var title = (item.title) ? item.title.replace(/&amp;amp;quot;/g, &apos;&quot;&apos;) : &apos;No Title&apos;;
                    availableWidgets.append(new Option(title, item.id));
                });
                var assignedWidgets = $(&apos;#assigned-widgets&apos;);
                assignedWidgets.empty();
                $.each(widgets, function(index, item) {
                    var title = (item.title) ? item.title.replace(/&amp;amp;quot;/g, &apos;&quot;&apos;) : &apos;No Title&apos;;
                    assignedWidgets.append(new Option(title, item.id));
                });
                configDialog.dialog({
                    title: $(element).text() + &apos; &apos; + configDialog.attr(&apos;data-title&apos;),
                    width: 600,
                    modal: true,
                    buttons: &amp;#91;{
                            text: configDialog.attr(&apos;data-close&apos;),
                            &apos;class&apos;: &apos;btn&apos;,
                            click: function() {
                                $(this).dialog(&quot;close&quot;);
                            }
                        }&amp;#93;
                });
            });
        }
    }
})
&lt;/pre&gt;
&lt;p&gt;As you can tell, this is quite a bit of code, and it doesn&apos;t even show you the JavaScript in move.js and upDown.js (included at the top of the dialog HTML). While writing this code, I could tell that I was not doing things the &lt;em&gt;Angular Way&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To refactor, I did some research, found the &lt;a href=&quot;https://github.com/angular-ui/bootstrap/tree/master/src/dialog&quot;&gt;$dialogProvider&lt;/a&gt; service and went to work.&lt;/p&gt;
&lt;h3 id=&quot;dialogs-with-angular&quot;&gt;Dialogs with Angular&lt;/h3&gt;
&lt;p&gt;I started by refactoring the &lt;em&gt;config&lt;/em&gt; directive to be much shorter.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.directive(&apos;config&apos;, function() {
    return {
        restrict: &apos;A&apos;,
        link: function (scope, element, attrs) {
            var configBtn = $(&apos;&amp;lt;a href=&quot;&quot;/&amp;gt;&apos;).addClass(&apos;configure&apos;);
            configBtn.appendTo(element);
            configBtn.bind(&apos;click&apos;, function(e) {
                e.preventDefault();
                e.stopPropagation();
                scope.$apply(scope.configureDialog(attrs.config, $(element).text()))
            });
        }
    }
})
&lt;/pre&gt;
&lt;p&gt;Line #10 that starts with &lt;code&gt;scope.$apply&lt;/code&gt; is what makes the magic happens.This calls the &lt;code&gt;configureDialog()&lt;/code&gt; function in &lt;code&gt;WidgetController&lt;/code&gt;. The &lt;code&gt;$dialog&lt;/code&gt; service you see below is injected by adding it as a parameter to the &lt;code&gt;WidgetController&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;

$scope.configureDialog = function (type, title) {
    var dialog = $dialog.dialog({
        dialogClass: &apos;modal draggable&apos;,
        modalFade: false,
        backdropClick: true,
        controller: &apos;ConfigController&apos;,
        template: $(&apos;#configure-widgets&apos;).html(),
        resolve: {
            title: function() {
                return title;
            },
            hiddenItems: function () {
                return Widget.getHiddenWidgets(type);
            },
            items: function () {
                return angular.copy($scope&amp;#91;type + &apos;Widgets&apos;&amp;#93;);
            }
        }
    });
    dialog.open().then(function(results) {
        if (!angular.isUndefined(results)) {
            $scope&amp;#91;type + &apos;Widgets&apos;&amp;#93; = results.items;
            Preferences.saveWidgetPreferences(type, results);
        }
    });
};
&lt;/pre&gt;
&lt;div class=&quot;alert alert-info&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;&lt;strong&gt;draggable directive&lt;/strong&gt;&lt;br/&gt;
If you look closely, you&apos;ll see this dialog is initialized with a &apos;draggable&apos; class. I created a &lt;em&gt;draggable &lt;/em&gt;directive that gives draggability using jQuery UI to any elements with this class.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.directive(&apos;draggable&apos;, function() {
    return {
        restrict: &apos;C&apos;,
        link: function(scope, elem, attr, ctrl) {
            elem.draggable({handle: &apos;.modal-header&apos;});
        }
    };
});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The interesting parts of this file are the &lt;strong&gt;controller&lt;/strong&gt;, &lt;strong&gt;template&lt;/strong&gt;, and &lt;strong&gt;resolve&lt;/strong&gt; parameters. The &lt;strong&gt;controller&lt;/strong&gt; is just another function in &lt;em&gt;controllers.js&lt;/em&gt;, the &lt;strong&gt;template&lt;/strong&gt; is HTML in the page (so text could be i18n-ized) and &lt;strong&gt;resolve&lt;/strong&gt; has the variables to pass to the controller. &lt;code&gt;Widget&lt;/code&gt; and &lt;code&gt;Preferences&lt;/code&gt; are services I&apos;ll talk about in the Part 3. The reason angular.copy() is used is so the widget arrays aren&apos;t modified while the dialog is displayed (we want to wait until the user clicks &quot;Save&quot;).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ConfigController&lt;/code&gt; function started quite simply:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
function ConfigController($scope, $sanitize, dialog, title, hiddenItems, items) {
    $scope.title = title;
    $scope.hiddenItems = hiddenItems;
    $scope.items = items;
}
&lt;/pre&gt;
&lt;p&gt;The HTML (defined by the aforementioned &lt;strong&gt;template&lt;/strong&gt; parameter) is as follows. You can see that &lt;em&gt;title&lt;/em&gt;, &lt;em&gt;hiddenItems&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; are the variables defined in $scope and used to render the data.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div id=&quot;configure-widgets&quot; class=&quot;modal hidden&quot;&amp;gt;
    &amp;lt;div class=&quot;modal-header&quot; style=&quot;border: 0&quot;&amp;gt;
        &amp;lt;button type=&quot;button&quot; class=&quot;close&quot; ng-click=&quot;close(false)&quot; aria-hidden=&quot;true&quot; style=&quot;margin-top: -3px&quot;&amp;gt;&amp;amp;times;&amp;lt;/button&amp;gt;
        &amp;lt;h4&amp;gt;{{title}} Bar Configuration&amp;lt;/h4&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;modal-body&quot;&amp;gt;
        &lt;p&gt;Decide which items you would like to display and set the display order.&lt;/p&gt;
        &amp;lt;div class=&quot;row-fluid center&quot;&amp;gt;
            &amp;lt;div class=&quot;span4&quot; style=&quot;width: 320px; margin-left: 25px&quot;&amp;gt;
                &amp;lt;label for=&quot;available-widgets&quot; class=&quot;bold&quot;&amp;gt;Available Widgets&amp;lt;/label&amp;gt;
                &amp;lt;select size=10 id=&quot;available-widgets&quot; ng-model=&quot;items.available&quot; ng-options=&quot;i.title for i in hiddenItems&quot;
                        class=&quot;width100pr&quot; multiple=&quot;multiple&quot;&amp;gt;&amp;lt;/select&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;span1 arrows&quot; style=&quot;padding-top: 50px&quot;&amp;gt;
                &amp;lt;img src=&quot;images/arrow_up.png&quot; class=&quot;arrow arrow-up&quot; alt=&quot;Move Up&quot; ng-click=&quot;moveUp(items.selected, items)&quot;/&amp;gt;
                &amp;lt;img src=&quot;images/arrow_right.png&quot; class=&quot;arrow arrow-right&quot; alt=&quot;Move Right&quot; ng-click=&quot;moveItem(items.available, hiddenItems, items)&quot;/&amp;gt;
                &amp;lt;img src=&quot;images/arrow_left.png&quot; class=&quot;arrow arrow-left&quot; alt=&quot;Move Left&quot; ng-click=&quot;moveItem(items.selected, items, hiddenItems)&quot;/&amp;gt;
                &amp;lt;img src=&quot;images/arrow_down.png&quot; class=&quot;arrow arrow-down&quot; alt=&quot;Move Down&quot; ng-click=&quot;moveDown(items.selected, items)&quot;/&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;span4&quot; style=&quot;width: 320px&quot;&amp;gt;
                &amp;lt;label for=&quot;assigned-widgets&quot; class=&quot;bold&quot;&amp;gt;Assigned Widgets&amp;lt;/label&amp;gt;
                &amp;lt;select size=10 id=&quot;assigned-widgets&quot; ng-model=&quot;items.selected&quot; ng-options=&quot;i.title for i in items&quot;
                        class=&quot;width100pr&quot; multiple=&quot;multiple&quot;&amp;gt;&amp;lt;/select&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;modal-footer&quot;&amp;gt;
        &amp;lt;button ng-click=&quot;close(true)&quot; class=&quot;btn btn-primary&quot;&amp;gt;Save and Close&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;There&apos;s a couple of new directives introduced by this code: &lt;em&gt;ngOptions&lt;/em&gt;&amp;nbsp;and &lt;em&gt;ngClick&lt;/em&gt;. The former is used to display options in a &amp;lt;select&amp;gt;, the latter to call functions in the controller. These functions are defined in &lt;code&gt;ConfigController&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$scope.moveUp = function(items, list) {
    angular.forEach(items, function(item) {
        var idx = list.indexOf(item);
        if (idx != -1) {
            list.splice(idx - 1, 0, list.splice(idx, 1)[0]);
        }
    });
};

$scope.moveDown = function(items, list) {
    angular.forEach(items, function(item) {
        var idx = list.indexOf(item);
        if (idx != -1) {
            list.splice(idx + 1, 0, list.splice(idx, 1)[0]);
        }
    });
};

$scope.moveItem = function(items, from, to) {
    angular.forEach(items, function(item) {
        var idx = from.indexOf(item);
        if (idx != -1) {
            from.splice(idx, 1);
            to.push(item);
        }
    });
};

$scope.close = function(save) {
    if (save) {
        dialog.close({
            hidden: $scope.hiddenItems,
            items: $scope.items
        });
    } else {
        dialog.close();
    }
};
&lt;/pre&gt;
&lt;p&gt;As you can see, Angular allows you to easily access and manipulate the data. Its two-way binding feature is great because when you modify the object in JavaScript, it auto-updates the displayed HTML.The only thing you need to do to the HTML is to add the &lt;a href=&quot;http://docs.angularjs.org/api/ng.directive:ngModel&quot;&gt;&lt;em&gt;ngModel&lt;/em&gt;&lt;/a&gt; directive.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;ul class=&quot;tasks&quot; ng-model=&quot;taskWidgets&quot; ui-sortable&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The dialog&apos;s &lt;code&gt;close()&lt;/code&gt; method is called in the header (where it passes false) and in the footer (where it passes true). The &lt;code&gt;configureDialog()&lt;/code&gt; function handles saving if the user indicates they wanted to do so. The 3rd line (starts with $scope) is all that&apos;s needed to update the UI. The Preferences service is covered in the next article.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
dialog.open().then(function(results) {
    if (!angular.isUndefined(results)) {
        $scope[type + &apos;Widgets&apos;] = results.items;
        Preferences.saveWidgetPreferences(type, results);
    }
});
&lt;/pre&gt;
&lt;h3 id=&quot;modals-with-data&quot;&gt;Modals with Data&lt;/h3&gt;
&lt;p&gt;Displaying the widgets in a consolidated dashboard is a nice product feature, but we wanted to take it a step further and allow users to &quot;click through&quot; to see the data. To do this, I added an &quot;event&quot; directive that could read from our JSON data and act upon it accordingly. We decided on 2 types of events: &lt;strong&gt;function&lt;/strong&gt; and &lt;strong&gt;href&lt;/strong&gt;. The &lt;strong&gt;href&lt;/strong&gt; event type is for report widgets, because we want to allow users to click on the widget and it takes them directly to the report. For &lt;strong&gt;function&lt;/strong&gt;, we simply &lt;em&gt;eval()&lt;/em&gt; what&apos;s passed in. The function is expected to have a &lt;em&gt;container&lt;/em&gt; argument that it can use to render data in a modal window.&lt;/p&gt;
&lt;p&gt;Using the &lt;em&gt;event&lt;/em&gt; directive, you can attach this behavior to a widget simply by adding a class.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;h3 class=&quot;events&quot;&amp;gt;{{widget.value}}&amp;lt;/h3&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;em&gt;event&lt;/em&gt; directive that attaches click behavior is below:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
directive(&apos;events&apos;, function () {
    // This is necessary because widgets may be chunked when charts are displayed 2-up
    function getWidget(element, widget, scope) {
        if (element.hasClass(&apos;chart-title&apos;)) {
            if (element.parent().hasClass(&apos;first&apos;)) {
                widget = scope.widget[0];
            } else if (element.parent().hasClass(&apos;second&apos;)) {
                widget = scope.widget[1];
            } else {
                widget = scope.widget;
            }
        } else {
            widget = scope.widget;
        }
        return widget;
    }
    return {
        restrict: &apos;C&apos;,
        link: function (scope, element, attrs) {
            var widget = getWidget(element, widget, scope);
            if (angular.isUndefined(widget)) {
                return;
            }
            var events = widget.events;
            if (!angular.isUndefined(events)) {
                for (var e in events) {
                    if (e === &apos;function&apos;) {
                        if ($(&apos;#dialog-frame&apos;).length === 0) {
                            $(&apos;&amp;lt;div id=&quot;dialog-frame&quot; class=&quot;modal hide&quot;/&amp;gt;&apos;).appendTo(&apos;body&apos;);
                            var header = $(&apos;&amp;lt;div class=&quot;modal-header&quot;&amp;gt;&apos;);
                            header.append($(&apos;&amp;lt;button type=&quot;button&quot; class=&quot;close&quot; data-dismiss=&quot;modal&quot;&amp;gt;&amp;times;&amp;lt;/button&amp;gt;&apos;));
                            header.append($(&apos;&amp;lt;h4/&amp;gt;&apos;).append(scope.widget.title));
                            header.appendTo(&apos;#dialog-frame&apos;);
                            $(&apos;&amp;lt;div class=&quot;modal-body&quot;/&amp;gt;&apos;).appendTo(&apos;#dialog-frame&apos;);
                        }
                        element.bind(&apos;click&apos;, function(event) {
                            event.preventDefault();
                            event.stopPropagation();
                            var dialog = $(&apos;#dialog-frame&apos;);
                            var title = widget.title;
                            dialog.find(&apos;h4&apos;).html(title);
                            var dialogBody = dialog.find(&apos;.modal-body&apos;);
                            dialogBody.empty();
 
                            // display a checking for new data message when widget&apos;s value is 0
                            var checkingMessage = $(&apos;#wait-checking&apos;).html();
                            if (scope.widget.value === &quot;0&quot;) {
                                dialogBody.html(checkingMessage);
                            // otherwise, display a loading message
                            } else {
                                dialogBody.html($(&apos;#wait-loading&apos;).html());
                            }
 
                            // center the dialog on the page
                            dialog.css({
                                width: &apos;560px&apos;,
                                &apos;margin-left&apos;: function() {
                                    return -($(this).width() / 2);
                                }
                            });
 
                            dialog.modal(&apos;show&apos;);
                            var container = dialogBody;
                            eval(events[e]);
                        });
                    } else if (e === &quot;href&quot;) {
                        element.bind(&apos;click&apos;, function() {
                            location.href = events[e];
                        })
                    } else {
                        console.log(&apos;Event type &quot;&apos; + e + &apos;&quot; not supported.&apos;);
                    }
                }
            }
        }
    }
})
&lt;/pre&gt;
&lt;h3 id=&quot;empty-widgets-message&quot;&gt;Empty Widgets Message&lt;/h3&gt;
&lt;p&gt;When there are no widgets for a particular type, we wanted to display a message telling the end user. To do this, I used the &lt;a href=&quot;http://docs.angularjs.org/api/ng.directive:ngHide&quot;&gt;&lt;em&gt;ngHide&lt;/em&gt;&lt;/a&gt; directive and passed in the array&apos;s length as an expression. I originally had this on a &amp;lt;li&amp;gt; in the respective widget list, but noticed it causes issues when dragging and dropping.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div ng-hide=&quot;summaryWidgets.length&quot; class=&quot;widgets-empty&quot;&amp;gt;
    No Summary Bar Widgets currently visible
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;This article has provided an overview of how I changed some of My Dashboard&apos;s features to use Angular instead of jQuery. I hope it&apos;s helped to show how powerful directives can be and how MVC works in Angular. I particularly enjoyed learning how to use the $dialog service. As a word of warning, its usage might change in future releases since it is currently being &lt;a href=&quot;https://github.com/angular-ui/bootstrap/issues/441&quot;&gt;rewritten to be more maintainable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii&quot;&gt;next article&lt;/a&gt;, I&apos;ll talk about how I developed Services and integrated DWR. If you see any code that can be improved upon, or issues with the code/architecture in this article, please leave a comment.&lt;/p&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/developing_with_angularjs_part_i</guid>
    <title>Developing with AngularJS - Part I: The Basics</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_i</link>
        <pubDate>Tue, 18 Jun 2013 09:06:52 -0600</pubDate>
    <category>The Web</category>
    <category>jquery</category>
    <category>highcharts</category>
    <category>javascript</category>
    <category>angularjs</category>
    <category>bootstrap</category>
    <category>taleo</category>
    <atom: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;
</atom:summary>        <description>&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;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/my_bootstrap_presentation_from_html5</guid>
    <title>My Bootstrap Presentation from HTML5 Denver</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/my_bootstrap_presentation_from_html5</link>
        <pubDate>Tue, 23 Apr 2013 10:50:07 -0600</pubDate>
    <category>The Web</category>
    <category>css3</category>
    <category>bootstrap</category>
    <category>html5</category>
    <category>html5denver</category>
            <description>&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;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/the_html5_roadshow_rocks</guid>
    <title>The HTML5 Roadshow Rocks!</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/the_html5_roadshow_rocks</link>
        <pubDate>Thu, 14 Mar 2013 15:16:58 -0600</pubDate>
    <category>The Web</category>
    <category>html5</category>
    <category>scottdavis</category>
    <category>html5roadshow</category>
    <category>kylesimpson</category>
    <category>training</category>
    <category>javascript</category>
            <description>&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://html5roadshow.com/&quot; title=&quot;Canyonland&apos;s Sunset Road by Trish of McGinity Photo&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8248/8557138083_8b32d4a06a_o.png&quot; width=&quot;500&quot; height=&quot;183&quot; alt=&quot;Canyonland&apos;s Sunset Road by Trish of McGinity Photo&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
In early February, as I was creating my &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_modern_java_web_developer&quot;&gt;Modern Java Web Developer&lt;/a&gt; presentation, I came across a &lt;a href=&quot;http://www.eventbrite.com/event/1470493285/eorg&quot;&gt;training event&lt;/a&gt; that was similarly named. Since I happened to know the instructor, I shot him off an email asking more about it. He promptly replied a week later with an intriguing description. The following sentence really stood out for me:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
They are NOT beginning workshops -- newbie information is easily found online, in books, at UG meetings, etc. They are targeted at working professionals who want to take their game to the next level. 
&lt;/p&gt;
&lt;p&gt;The other thing that really struck me about this training was it was &#224; la carte; meaning you could pick and pay for just the days you wanted to attend. Scott gave me a two-for-one deal and I signed up for the following workshops:&lt;/p&gt;
&lt;ul&gt;

&lt;li&gt;&lt;a href=&quot;http://html5roadshow.com/workshops#advanced_javascript&quot;&gt;Advanced JavaScript: The &quot;What You Need To Know&quot; Parts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://html5roadshow.com/workshops#airplane_mode_html5&quot;&gt;Airplane-Mode HTML5: Mobile Web Development for Tablets and Smartphones&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;
&lt;p&gt;Normally, attending a training course would have an opportunity cost for me. However, since I work remotely and it doesn&apos;t matter &lt;em&gt;where&lt;/em&gt; I work, I decided to work from the class. It turned out to be a great idea because it had the same feel of a productive day at the coffeeshop, but it was a coffeeshop full of knowledge. Instead of looking up and doing some people watching or getting distracted by social media, I would look up and learn something new. I actually ended up getting more done in those two training classes than I normally do at my &lt;a href=&quot;http://www.businessatthrive.com/v3/&quot;&gt;LoDo Office&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advanced JavaScript&lt;/strong&gt;&lt;br/&gt;The Advanced JavaScript workshop was taught by &lt;a href=&quot;http://getify.me/&quot;&gt;Kyle Simpson&lt;/a&gt;. This was my first time meeting Kyle and I was immediately impressed by his resume, especially his &lt;em&gt;worked on Firefox Developer Tools for 9 months&lt;/em&gt; credentials. He started out talking about good JavaScript documentation on sites such as &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN&lt;/a&gt; and &lt;a href=&quot;https://github.com/rwldrn/idiomatic.js&quot;&gt;Principles of Writing Consistent, Idiomatic JavaScript&lt;/a&gt;. Tip: append &quot;mdn&quot; to any searches for JavaScript topics and you&apos;re likely to get better results. Not only that, but MDN is a wiki so you can improve it too. From there, we moved onto DOM Events, event propagation (e.g. bubbling vs. capturing), event management, scopes, &lt;code&gt;this&lt;/code&gt;, closures and design patterns. It was a very deep dive into JavaScript and I enjoyed every minute of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Airplane-Mode HTML5&lt;/strong&gt;&lt;br/&gt;The Airplane-Mode HTML5 workshop was taught by &lt;a href=&quot;http://thirstyhead.com&quot;&gt;Scott Davis&lt;/a&gt; (his site is currently down, should be fixed next week). Scott talked about many things I was already familiar with, but I still managed to learn a bunch of new tips and tricks. He started by having us install &lt;a href=&quot;http://nodejs.org/&quot;&gt;Node&lt;/a&gt;, &lt;a href=&quot;http://volojs.org/&quot;&gt;volo&lt;/a&gt; and creating projects. He showed how &lt;code&gt;package.json&lt;/code&gt; is the JS project&apos;s equivalent of &lt;code&gt;pom.xml&lt;/code&gt;. 
&lt;/p&gt;
&lt;p&gt;Next, he plunged into viewports, orientation and how to use CSS3 media queries. I&apos;ve always used min-device-width and max-device-width in my media queries, so &lt;code&gt;orientation: portrait&lt;/code&gt; and &lt;code&gt;orientation: landscape&lt;/code&gt; was a useful tip. He taught us about CSS3&apos;s pseudo-classes (esp. for validation) and pointed us to the &lt;a href=&quot;http://alistapart.com/article/forward-thinking-form-validation&quot;&gt;Forward Thinking Form Validation&lt;/a&gt; article. We learned about using &lt;a href=&quot;http://fmbip.com&quot;&gt;findmebyIP.com&lt;/a&gt; and how it can be a useful tool for finding your browser&apos;s capabilities, particularly when you&apos;re in a black box environment (e.g. a browser on a TV). I knew about &lt;a href=&quot;http://modernizr.com/&quot;&gt;Modernizr&lt;/a&gt;, but wasn&apos;t aware of &lt;a href=&quot;https://github.com/phiggins42/has.js/&quot;&gt;has.js&lt;/a&gt; for JavaScript feature detection. 
&lt;/p&gt;
&lt;p&gt;Scott talked about many more topics, from HTML5 forms and mobile links to geolocation and maps. He ended the day talking about local storage and application cache. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Good&lt;/strong&gt;&lt;br/&gt;
First of all, the price was perfect at around $250 per day. The best part for me was that I learned more than I expected to. Not only that, but I didn&apos;t have to endure any opportunity cost to attend these workshops. There were times I wish I didn&apos;t have to work, but it was cool to have &quot;learning stuff&quot; as a distraction rather than social media.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Bad&lt;/strong&gt;&lt;br/&gt;
The Location. I ride my bike to work year-round and only sit in traffic a few times per year. So I know I&apos;m biased. The location was in Louisville and while it should be only a 30-minute commute, I spent 2 hours in the car each day. Once I got there, the facility was great, the internet was fast and the lunch choices were splendid.
&lt;/p&gt;
&lt;p&gt;To see if the &lt;a href=&quot;http://html5roadshow.com/&quot;&gt;HTML5 Roadshow&lt;/a&gt; is coming to your town, checkout &lt;a href=&quot;http://thirstyhead.eventbrite.com/&quot;&gt;ThirstyHead on Eventbrite&lt;/a&gt; or &lt;a href=&quot;http://twitter.com/html5roadshow&quot;&gt;follow them on Twitter&lt;/a&gt;.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/core_html5_canvas_book_review</guid>
    <title>Core HTML5 Canvas Book Review</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/core_html5_canvas_book_review</link>
        <pubDate>Fri, 14 Sep 2012 09:21:56 -0600</pubDate>
    <category>The Web</category>
    <category>canvas</category>
    <category>html5</category>
    <category>bookreview</category>
    <category>davidgeary</category>
            <description>&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;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with</guid>
    <title>Refreshing Taleo&apos;s UI with HTML5, Twitter Bootstrap and CSS3</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with</link>
        <pubDate>Mon, 20 Aug 2012 12:27:21 -0600</pubDate>
    <category>The Web</category>
    <category>taleo</category>
    <category>css3</category>
    <category>html5</category>
    <category>twitterbootstrap</category>
            <description>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.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/twitter_s_open_source_summit</guid>
    <title>Twitter&apos;s Open Source Summit: Bootstrap 2.0 Edition</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/twitter_s_open_source_summit</link>
        <pubDate>Wed, 1 Feb 2012 11:28:40 -0700</pubDate>
    <category>The Web</category>
    <category>bootstrap</category>
    <category>opensource</category>
    <category>twitter</category>
    <category>ossummit</category>
    <category>hogan.js</category>
    <category>nasa</category>
            <description>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. </description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/phonegap_for_hybrid_app_development</guid>
    <title>PhoneGap for Hybrid App Development</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/phonegap_for_hybrid_app_development</link>
        <pubDate>Wed, 16 Nov 2011 10:22:16 -0700</pubDate>
    <category>The Web</category>
    <category>devoxx</category>
    <category>ios</category>
    <category>javascript</category>
    <category>phonegap</category>
    <category>android</category>
    <category>adobe</category>
    <category>devoxx2011</category>
            <description>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.
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/my_everything_you_ever_wanted</guid>
    <title>My Everything You Ever Wanted To Know About Online Video Presentation</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/my_everything_you_ever_wanted</link>
        <pubDate>Fri, 3 Dec 2010 10:16:44 -0700</pubDate>
    <category>The Web</category>
    <category>youtube</category>
    <category>richweb</category>
    <category>onlinevideo</category>
    <category>hulu</category>
    <category>video</category>
    <category>rwx2010</category>
    <category>encoding</category>
    <category>html5</category>
    <category>netflix</category>
    <category>richwebexperience</category>
    <category>flash</category>
            <description>This week I&apos;ve had the pleasure of speaking at &lt;a href=&quot;http://www.therichwebexperience.com/conference/fort_lauderdale/2010/11/home&quot;&gt;The Rich Web Experience&lt;/a&gt; in Fort Lauderdale. I did two talks, one on Comparing JVM Web Frameworks and one titled &lt;a href=&quot;http://www.therichwebexperience.com/conference/fort_lauderdale/2010/11/session?id=20473&quot;&gt;Everything You Ever Wanted To Know About Online Video&lt;/a&gt;. Both talks had full rooms and very engaged audiences. 
&lt;/p&gt;
&lt;p&gt;
In the video talk, there were some audience members that knew &lt;em&gt;way&lt;/em&gt; more than me about the topic. This made for a very interactive session and one of the most fun presentations I&apos;ve ever done. It was also cool to talk about a lot of things I&apos;ve learned over the last year (for more details on that, check out my &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_s_the_ol_team&quot;&gt;team status&lt;/a&gt; or &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_we_hired_a_team&quot;&gt;team hiring&lt;/a&gt; posts). If you don&apos;t have Flash installed, you can &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/Everything_Online_Video_RWX2010.pdf&quot;&gt;download a PDF&lt;/a&gt; of this presentation.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;&lt;object id=&quot;__sse6011456&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=everythingonlinevideo-101202175612-phpapp01&amp;rel=0&amp;stripped_title=everything-you-ever-wanted-to-know-about-online-video&amp;userName=mraible&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed name=&quot;__sse6011456&quot; src=&quot;//static.slidesharecdn.com/swf/ssplayer2.swf?doc=everythingonlinevideo-101202175612-phpapp01&amp;rel=0&amp;stripped_title=everything-you-ever-wanted-to-know-about-online-video&amp;userName=mraible&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;/p&gt;
&lt;p&gt;The first talk about Comparing JVM Web Frameworks was largely an extension of the &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_comparing_jvm_web_frameworks&quot;&gt;one I presented at Devoxx&lt;/a&gt; two weeks ago. The main differences between this one and the last one is I extended it a bit and took into account some &lt;a href=&quot;http://blog.tapestry5.de/index.php/2010/11/21/response-to-matt-raibles-presentation-at-devoxx-2010/&quot;&gt;community&lt;/a&gt; &lt;a href=&quot;http://www.logemann.org/2010/11/on-matt-raibles-web-framework.html&quot;&gt;feedback&lt;/a&gt;. However, this seemed to simply &lt;a href=&quot;http://old.nabble.com/Matt-Raible%27s-JVM-Web-Framework-matrix-td30345655.html&quot;&gt;inspire&lt;/a&gt; &lt;a href=&quot;http://twitter.com/#!/ptrthomas/status/9818249143255040&quot;&gt;anger&lt;/a&gt;, so I&apos;ll pass on embedding it here. You can &lt;a href=&quot;http://www.slideshare.net/mraible/comparing-jvm-web-frameworks-rich-web-experience-2010&quot;&gt;view it on Slideshare&lt;/a&gt; or &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/Comparing_JVM_Web_Frameworks_RWX2010.pdf&quot;&gt;download the PDF&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;My Comparing Web Frameworks slides often inspire harsh words, but folks &lt;a href=&quot;http://twitter.com/#!/snicoll/status/5251494903291905&quot; title=&quot;you may disagree with some of the content but @mraible session was the most interesting session of devoxx 2010 so far IMO.&quot;&gt;really seem to like the presentation&lt;/a&gt;. I encourage you to watch my &lt;a href=&quot;http://parleys.com/d/2118&quot;&gt;Devoxx presentation on Parleys.com&lt;/a&gt; to see for yourself.
&lt;/p&gt;
&lt;p&gt;This marks the end of 2010 conferences for me. I had a blast speaking at The Rich Web Experience, as well as &lt;a href=&quot;http://raibledesigns.com/rd/entry/tssjs_2010_presentations_and_summary&quot;&gt;TheServerSide Java Symposium&lt;/a&gt;, &lt;a href=&quot;http://raibledesigns.com/rd/entry/presentations_from_the_irish_software&quot;&gt;The Irish Software Show&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_comparing_jvm_web_frameworks&quot;&gt;Devoxx&lt;/a&gt;. Now it&apos;s time to sit back, relax, get some powder days in and find my next gig.&lt;/p&gt;
&lt;p&gt;Hope y&apos;all have a great holiday season!</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/scaling_flash_movies_to_match</guid>
    <title>Scaling Flash Movies to match Browser Zoom Levels</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/scaling_flash_movies_to_match</link>
        <pubDate>Tue, 13 Jul 2010 12:18:42 -0600</pubDate>
    <category>The Web</category>
    <category>javascript</category>
    <category>flex</category>
    <category>browsers</category>
    <category>flash</category>
    <category>textzoom</category>
    <category>actionscript</category>
            <description>Recently I was tasked with figuring out how to scale the Flash assets in the web application I&apos;m working on. In the app, there&apos;s two different Flash assets: a Spotlight (cycles through images) and a Video Player. Before I started working on the issue, our assets would stay the same fixed size no matter what the browser zoom level. You can see this issue in action by going to &lt;a href=&quot;http://hulu.com&quot;&gt;Hulu&lt;/a&gt; or &lt;a href=&quot;http://fancast.com&quot;&gt;Fancast&lt;/a&gt; and zooming in/out (Command +/- on Mac, Control +/- on Windows). The Flash assets don&apos;t scale with the browser&apos;s text.
&lt;/p&gt;
&lt;p&gt;I found a &lt;a href=&quot;http://delicious.com/mraible/textzoom&quot;&gt;lot of references&lt;/a&gt; for how to trap and handle resizing in JavaScript, so that&apos;s the initial path I took. I ended up having issues trapping the resize event in IE, as well as persisting the appropriate zoom level on page reload. Because of this, I ended up using a pure &lt;a href=&quot;http://raibledesigns.com/rd/entry/scaling_flash_movies_to_match#actionscript&quot;&gt;ActionScript solution&lt;/a&gt; that works much better. This article shows how I implemented both solutions.
&lt;/p&gt;
&lt;p&gt;
Regardless of implementation, the first change I had to make was to move the height and width from the Flash asset (object/embed/JS) to its surrounding tag (&amp;lt;section&gt; in our app). Then I changed the height/width to 100% on the Flash asset.
&lt;/p&gt;
&lt;p&gt;
&lt;strong id=&quot;javascript&quot;&gt;JavaScript Implementation&lt;/strong&gt;&lt;br/&gt;
To allow zooming in ActionScript, I modified our main class to expose a &quot;zoom&quot; method to JavaScript:
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
ExternalInterface.addCallback(&quot;zoom&quot;, _zoom);

...

private function _zoom(scale:Number):void {
    _view.scaleX = _view.scaleX * scale;
    _view.scaleY = _view.scaleY * scale;
}
&lt;/pre&gt;
In the code above, _view refers to the container that holds all the items in the player. To call this method in JavaScript, I added the following code:
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
var windowHeight;
var documentHeight;

$(document).ready(function() { 
    ...
    windowHeight = $(window).height();
    documentHeight = $(document).height();

    $(window).resize(resizeWindow);
}

// Resize Flash assets when page is zoomed
function resizeWindow() {
    var newWindowHeight = $(window).height();
    var newDocumentHeight = $(document).height();
    // if document height hasn&apos;t changed, it&apos;s a browser window resize
    // event instead of a text zoom - don&apos;t change anything
    if (newDocumentHeight === documentHeight) {
        return;
    } else {
        documentHeight = newDocumentHeight;
    }
    var scale = (windowHeight / newWindowHeight); 

    var player = getFlashMovie(&apos;playerId&apos;);
    if (player &amp;&amp; player.zoom) {
        player.zoom(scale);
    }
    var spotlight = getFlashMovie(&apos;spotlightId&apos;);
    if (spotlight &amp;&amp; spotlight.zoom) {
        spotlight.zoom(scale);
    }

    windowHeight = newWindowHeight;
}
&lt;/pre&gt;
&lt;p&gt;
This seemed to work well in Firefox, Safari and Opera, but not in IE. I found &lt;a href=&quot;http://old.nabble.com/$%28window%29.bind%28%27resize%27,-fn%29-not-working-in-IE-td21447946s27240.html&quot;&gt;this explanation&lt;/a&gt; about why it might not work, but I was unsuccessful in getting IE to recognize a resize/zoom event. 
&lt;/p&gt;
&lt;p&gt;
To fix scaling in our Spotlight asset, I used a similar solution. However, since the Spotlight didn&apos;t have all its elements in a container (they were being added directly to the stage), I had to refactor the code to add a SpotlightView (extends Sprite) that contains the bulk of the code.
&lt;/p&gt;
&lt;p&gt;Browsers persist the zoom level you&apos;ve set for a site. The problem with the solution used here is it only scales up and down properly if you start from scale = 1 and revert to scale = 1 before leaving the site. If you zoom in and close your browser, when you come back the flash movies will be scale = 1 while the rest of the site is zoomed in. To solve this problem, I attempted to save the scale value in a cookie. This worked, and I was able to read the cookie in the *.as files to scale the movie correctly. However, I experienced some issues with this approach and didn&apos;t like having to delete cookies when I wanted the Flash assets to scale correctly.
&lt;/p&gt; 
&lt;p&gt;&lt;strong id=&quot;actionscript&quot;&gt;ActionScript Implementation&lt;/strong&gt;&lt;br/&gt;
After discovering issues with the JavaScript implementation, I did some research to see if it was possible to listen for the browser resize event in ActionScript. The &lt;a href=&quot;http://www.republicofcode.com/tutorials/flash/as3fluidresize/&quot;&gt;Flash Fluid Layouts and Stage Resize in AS3&lt;/a&gt; tutorial clued me in that the stage could listen for a resize event. 
&lt;/p&gt;
&lt;pre class=&quot;brush: java; toolbar: false&quot;&gt;
stage.addEventListener(Event.RESIZE, resizeListener); 
&lt;/pre&gt;
&lt;p&gt;After adding the above line in the initialization, I added a resizeListener function that scales based on the default dimensions. It also ensures no scaling happens in full screen mode. 
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
private function resizeListener(e:Event):void {
    // don&apos;t scale if entering full screen mode
    if (stage.displayState == StageDisplayState.FULL_SCREEN)  {
        _view.scaleX = 1;
        _view.scaleY = 1;
    } else {
        _view.scaleX = stage.stageWidth / 964;
        _view.scaleY = stage.stageHeight / 586;
    }
}
&lt;/pre&gt;
&lt;p&gt;
For the Spotlight asset, there are a number of different layouts (home, featured and news). The main class has a resizeListener function that scales accordingly to which layout type is being used.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
private function resizeListener(e:Event):void {
    var type:String = _view.getLayoutType();

    if (type == &quot;featured&quot;) { 
        _view.scaleX = stage.stageWidth / 958;
       _view.scaleY = stage.stageHeight / 180;
   } else if (type == &quot;home&quot;) { 
        _view.scaleX = stage.stageWidth / 964;
        _view.scaleY = stage.stageHeight / 428;
    } else if (type == &quot;news&quot;) {
        _view.scaleX = stage.stageWidth / 964;
        _view.scaleY = stage.stageHeight / 189;
    }
}
&lt;/pre&gt;
&lt;p&gt;Because the layout type isn&apos;t set until the XML is loaded, I listen for that event in my URLLoader.
&lt;/p&gt;
&lt;pre class=&quot;brush: java; toolbar: false&quot;&gt;
xmlLoader.addEventListener(Event.COMPLETE, resizeListener);
&lt;/pre&gt;
&lt;p&gt;With the pure ActionScript implementation, the zoom level is automatically persisted. The Event.RESIZE event is fired by the Flash plugin when the page first loads if the dimensions are not the default. 
&lt;/p&gt;
&lt;p&gt;
That&apos;s it! Special thanks to &lt;a href=&quot;http://www.jamesward.com/&quot;&gt;James Ward&lt;/a&gt; for clueing me into &lt;em&gt;scaleX&lt;/em&gt; and &lt;em&gt;scaleY&lt;/em&gt;. Hopefully Hulu and Comcast can use this tutorial to scale their video players too. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;-)&quot; title=&quot;;-)&quot; /&gt;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/introduction_to_objective_j_and</guid>
    <title>Introduction to Objective-J and Cappuccino with Tom Robinson</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/introduction_to_objective_j_and</link>
        <pubDate>Thu, 3 Dec 2009 11:21:59 -0700</pubDate>
    <category>The Web</category>
    <category>tlrobinson</category>
    <category>javascript</category>
    <category>dom</category>
    <category>objectivej</category>
    <category>richwebexperience</category>
    <category>richweb</category>
    <category>280north</category>
    <category>cappuccino</category>
            <description>This morning, I attended &lt;a href=&quot;http://tlrobinson.net/&quot;&gt;Tom Robinson&apos;s&lt;/a&gt; talk on &lt;a href=&quot;http://www.therichwebexperience.com/conference/orlando/2009/12/session?id=15958&quot;&gt;Objective-J and Cappuccino&lt;/a&gt;. Tom is one of the founders of &lt;a href=&quot;http://280north.com/&quot;&gt;280 North&lt;/a&gt; and creators of the &lt;a href=&quot;http://cappuccino.org/&quot;&gt;Cappuccino framework&lt;/a&gt; and Objective-J language, so I was very interested in hearing about Cappuccino from &lt;em&gt;the source&lt;/em&gt;. The text below are my notes, but they&apos;re also mostly Tom&apos;s words, not mine. I&apos;ve added a &quot;Thoughts&quot; section at the end that are my words.&lt;/p&gt;
&lt;p&gt;Tom&apos;s Team was Cocoa programmers before they started building Cappuccino. They wanted to focus on building Desktop Class Web Applications (for example, Google Maps, Meebo and 280 Slides). Tom showed a  demo of 280 Slides and how it can rotate and scale images very easily, something you don&apos;t often see in web applications. 
&lt;/p&gt;
&lt;p&gt;
To build desktop class web applications, you can use Flash or Silverlight, but they&apos;re controlled by Adobe and Microsoft. Also, they have no iPhone support and poor Mac and Linux performance. The other option is JavaScript + DOM. They&apos;re open standards, available almost everywhere (including mobile devices) and its a very rich ecosystem with lots of competition. The downside to JavaScript is standards bodies, many incompatibilities, technical limitations (e.g. can&apos;t access web cam) and the DOM is very document-centric.
&lt;/p&gt;
&lt;p&gt;The bottom line is we can&apos;t fix Flash, but we can fix JavaScript. 
&lt;/p&gt;
&lt;p&gt;
This is what 280 North is trying to do with Objective-J. It&apos;s a proper superset of JavaScript, has a few syntax additions, has a powerful runtime and is implemented &lt;em&gt;in&lt;/em&gt; JavaScript. Objective-J is analogous to Objective-C. It adds to JavaScript like Objective-C adds to C. &lt;/p&gt;
&lt;p&gt;One of the first things Objective-J adds is Dependency Management. You can import from search paths:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
@import &amp;lt;Foundation/CPObject.j&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Or from relative paths:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
@import &quot;ApplicationController.j&quot;
&lt;/pre&gt;
&lt;p&gt;@import prevents duplicate loads has asynchronous downloading and synchronous execution. That means all files are downloaded before evaluation begins, but to the programmer, it seems to happen synchronously.&lt;/p&gt;
&lt;p&gt;The thing that sets Cappuccino apart from other libraries is its inheritance model. It uses  classical OO inheritance (using Objective-C syntax).
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
@implementation Person : CPObject {
    String firstName @accessors;
    String lastName @accessors;
}

- (String) fullName {
    return firstName + lastName;
}

@end
&lt;/pre&gt;
&lt;p&gt;The type definitions (String) are ignored for now and primarily used for documentation. In the future, they plan to add optional static typing, hence the reason for having them. Tom is unsure if you can leave off the String type or if the compiler requires it.&lt;/p&gt;
&lt;p&gt;@implementation has proper support for &lt;code&gt;super&lt;/code&gt; and language syntax support. One of the reasons they chose Objective-C is because classical inheritance works great for UI Frameworks. &lt;/p&gt;
&lt;p&gt;Objective-J uses &quot;send a message&quot; syntax instead of &quot;call a method&quot; syntax. In the code snippets below, the first line is JavaScript, the second is Objective-J:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
object.method()
[object method]

object.methodWithFoot(arg1)
[object methodWithFoo:arg1]

object.methodWithFooBar(arg1, arg2)
[object methodWithFoo:arg1 bar:arg2]
&lt;/pre&gt;
&lt;p&gt;Dynamic Dispatch is one of the most interesting parts of Objective-J. &lt;em&gt;forwardInvocation&lt;/em&gt; in Objective-C is like &lt;em&gt;method_missing&lt;/em&gt; in Ruby. Methods can be used as references, for example:
&lt;/p&gt;
&lt;pre&gt;
var action = @selector(someMethod:);
&lt;/pre&gt;
&lt;p&gt;Runtime mutability is important for KeyValueCoding (KVC) and KeyValueObserving (KVO). KVC allows you to swap classes at runtime and KVO allows you to listen for when property values change. At runtime, a $KVO_ClassName is generated. This class notifies any registered observers when values are changed and then calls the original class to change the property.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cappuccino&lt;/strong&gt;&lt;br/&gt;
Cappuccino is an application framework, not a library. It uses the &lt;em&gt;Hollywood Principle&lt;/em&gt;: &quot;Don&apos;t call us, we&apos;ll call you&quot;. 
&lt;/p&gt;
&lt;p&gt;
The Framework handles document management (open, save, revert), content editing (undo, redo, copy, paste) and graphics manipulation. The DOM is designed for documents (same is true for HTML and CSS). Tom doesn&apos;t like the DOM as its not a good API for building applications. Proof is all the JavaScript libraries built to make the DOM better. &lt;/p&gt;
&lt;p&gt;Cappuccino has an MVC framework and CPView is its View. It&apos;s analogous to a &amp;lt;div&amp;gt; and represents a rectangle on the screen. Everything visible is a CPView or one of its subclasses. It defines resizing and layout behavior. CoreGraphics is Cappuccino&apos;s canvas-like drawing API. It uses VML on IE, canvas on everything else. &lt;/p&gt;
&lt;p&gt;Very little of the code in Cappuccino talks to the DOM (less than 2%). It&apos;s not just about providing widgets that work in all browsers, it&apos;s a way to write platform independent display code. &lt;/p&gt;
&lt;p&gt;Events are done very differently than most JavaScript libraries. Browser&apos;s dispatching is not used. A single event listener is registered for each type of event on the window. These events are captured and sent to the objects that need to know about them. This allows for consistent events across all browsers, even keyboard events. It also allows for creating custom event flows and easily creating custom events. Cappuccino events allow you to get around a common problem with DOM Events where you can&apos;t click on overlapping rectangles.&lt;/p&gt;
&lt;p&gt;Notifications can be registered and sent very easily. Both &quot;scoped&quot; and private notifications can be created.&lt;/p&gt;
&lt;p&gt;Undo Management is included in Cappuccino. It manages a stack of undos for you. Redos are &quot;free&quot; and undo functionality is part of the document architecture. This makes it easy to integrate with auto-save functionality.&lt;/p&gt;
&lt;p&gt;Run loops (also called event loops) are an advanced feature of Cappuccino. They allow you to perform actions on every run loop. This enables complex optimizations for DOM/Graphic operations and undo grouping. &lt;/p&gt;
&lt;p&gt;The final part of Cappuccino is Keyed Archiving. Keyed Archiving stores a graph of Objective-J objects. It handles reference cycles, conditional inclusions, has an efficient data format and works on the client and server (Objective-J can be run on the server). The data format is similar like binary, but it&apos;s UTF-8. Keyed Archiving is used for archiving views and used heavily in &lt;a href=&quot;http://280slides.com/&quot;&gt;280 Slides&lt;/a&gt; for storing, retrieving, and exporting presentations. 
&lt;/p&gt;
&lt;p&gt;Other applications implemented with Cappuccino include &lt;a href=&quot;http://almost.at&quot;&gt;almost.at&lt;/a&gt; 
and &lt;a href=&quot;http://gomockingbird.com&quot;&gt;Mockingbird&lt;/a&gt;. &lt;a href=&quot;http://enstore.com&quot;&gt;EnStore&lt;/a&gt; uses it too, but only for its admin interface.&lt;/p&gt;
&lt;p&gt;An interesting extension for Rails developers is &lt;a href=&quot;http://github.com/nciagra/Cappuccino-Extensions/tree/master/CPActiveRecord/&quot;&gt;CPActiveRecord&lt;/a&gt;, a reimplementation of Rails&apos; ActiveRecord in Cappuccino.
&lt;/p&gt;
&lt;p&gt;There are several tools included with Cappuccino:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;objj: command line Objective-J&lt;/li&gt;
&lt;li&gt;objcc: &quot;compile&quot; ahead of time&lt;/li&gt;
&lt;li&gt;press: optimize code and resources&lt;/li&gt;
&lt;li&gt;nib2cib: convert Mac OS X nibs&lt;/li&gt;
&lt;li&gt;capp: project creation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these tools are built on &lt;a href=&quot;http://github.com/tlrobinson/narwhal&quot;&gt;Narwhal&lt;/a&gt; (which conforms to the CommonJS standard).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CommonJS&lt;/strong&gt;&lt;br/&gt;
&lt;a href=&quot;http://wiki.commonjs.org/wiki/CommonJS&quot;&gt;CommonJS&lt;/a&gt; is an effort among server-side JavaScript projects to standardize non-browser JavaScript APIs. There&apos;s numerous API specifications (so far):
&lt;ul&gt;
&lt;li&gt;Binary, File, IO&lt;/li&gt;
&lt;li&gt;stdin, stdout, stderr, args, env&lt;/li&gt;
&lt;li&gt;Web server gateway (&lt;a href=&quot;http://jackjs.org/jsgi-spec.html&quot;&gt;JSGI&lt;/a&gt;) - similar to &lt;a href=&quot;http://wsgi.org/wsgi/&quot;&gt;WSGI&lt;/a&gt; and &lt;a href=&quot;http://rack.rubyforge.org/&quot;&gt;Rack&lt;/a&gt; for Python and Ruby&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To learn more about CommonJS, see &lt;a href=&quot;http://arstechnica.com/web/news/2009/12/commonjs-effort-sets-javascript-on-path-for-world-domination.ars&quot;&gt;CommonJS effort sets JavaScript on path for world domination&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Narwhal is 280 North&apos;s implementation of CommonJS APIs. It works with multiple JavaScript engines, including Rhino, JavaScriptCore (SquirrelFish) and XUL Runner. According to Tom, Rhino is an order of magnitude slower than JavaScriptCore and V8. Of course, Narwhal supports Objective-J too.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Aristo&lt;/strong&gt;&lt;br/&gt;
Aristo is the new default theme in Cappuccino and was created by the &lt;a href=&quot;http://www.madebysofa.com/&quot;&gt;Sofa&lt;/a&gt; design firm. It includes windows, tabs and menus and is &lt;a href=&quot;http://wiki.github.com/280north/aristo&quot;&gt;open source&lt;/a&gt; so you can modify. 
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Atlas&lt;/strong&gt;&lt;br/&gt;
Atlas is an IDE for Cappuccino, focused on building user interfaces graphically. Atlas is a downloadable application for OS X. It&apos;s written almost entirely in Cappuccino. The desktop version bridges Cappuccino windows to native windows. Tom did a demo of Atlas and showed how its layout feature allows you pin, center and align very easily. It&apos;s all done with JavaScript because doing layouts with CSS is often very painful. After that, he showed us how can you Atlas to very easily build a Web Application and then export it as a native OS X application without changing a line of code. Atlas includes Mozilla&apos;s &lt;a href=&quot;https://bespin.mozilla.com/&quot;&gt;Bespin&lt;/a&gt; for code editing. &lt;/p&gt;
&lt;p&gt;
&lt;p&gt;To learn more about Aristo and Atlas, you might want to checkout Ajaxian&apos;s &lt;a href=&quot;http://ajaxian.com/archives/big-news-from-cappuccino-aristo-and-atlas&quot;&gt;Big News from Cappuccino: Aristo and Atlas&lt;/a&gt; from earlier this year.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://280atlas.com&quot;&gt;Atlas&lt;/a&gt; has a $20 &lt;a href=&quot;https://atlas-beta.heroku.com/users/new&quot;&gt;Beta Program&lt;/a&gt; if you&apos;re interested in trying it out.&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
&lt;strong&gt;My Thoughts&lt;/strong&gt;&lt;br/&gt;
Cappuccino looks like a very cool web framework. It reminds me of GWT in that you have to learn a new language to use it. However, Atlas takes a lot of that pain away. I particularly like how it has document and undo/redo support built-in. On my current GWT project, this would be very useful as we&apos;ve had to build this functionality by hand. </description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/gwt_oauth_and_linkedin_apis</guid>
    <title>GWT OAuth and LinkedIn APIs</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/gwt_oauth_and_linkedin_apis</link>
        <pubDate>Tue, 24 Nov 2009 15:46:05 -0700</pubDate>
    <category>The Web</category>
    <category>api</category>
    <category>gwt</category>
    <category>oauth</category>
    <category>linkedin</category>
    <category>profile</category>
    <category>google</category>
    <category>twitter</category>
            <description>&lt;a href=&quot;http://www.linkedin.com&quot;&gt;&lt;img src=&quot;//static.raibledesigns.com/repository/images/linkedin-logo.gif&quot; width=&quot;129&quot; height=&quot;36&quot; alt=&quot;LinkedIn Logo&quot; class=&quot;picture&quot; style=&quot;border: 0&quot; /&gt;&lt;/a&gt;
When I worked at LinkedIn last year, I received a lot of inquiries from friends and developers about LinkedIn&apos;s APIs. After a while, I started sending the following canned response:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
For API access to build LinkedIn features into your application, fill
out the following form:
&lt;br/&gt;&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&quot;http://www.linkedin.com/static?key=developers_apis&quot;&gt;http://www.linkedin.com/static?key=developers_apis&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
For requests to build an application, go to:
&lt;br/&gt;&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&quot;http://www.linkedin.com/static?key=developers_opensocial&quot;&gt;http://www.linkedin.com/static?key=developers_opensocial&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
I talked with the API team and they did say they look at every request that&apos;s sent via these forms. They don&apos;t respond to all of them b/c they know that many people would be angry if they told them &quot;no&quot;, so they&apos;d rather not have that headache.
&lt;/p&gt;
&lt;p&gt;Yesterday, I was pumped to see that they&apos;ve finally decided to &lt;a href=&quot;http://blog.linkedin.com/2009/11/23/linkedin-platform-launch/&quot;&gt;open up their API to Developers&lt;/a&gt;.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Starting today, developers worldwide can integrate LinkedIn into their business applications and Web sites.  &lt;a href=&quot;http://developer.linkedin.com/&quot;&gt;Developer.linkedin.com&lt;/a&gt; is now &lt;strong&gt;live &lt;/strong&gt;and open for business.
&lt;/p&gt;
&lt;p&gt;First of all, congratulations to the API team on finally making this happen! I know it&apos;s no small feat. Secondly, it&apos;s great to see them using &lt;a href=&quot;http://www.jivesoftware.com/products&quot;&gt;Jive SBS&lt;/a&gt; for their API documentation and developer community. My current client uses this to facilitate development and I love how it integrates a wiki, JIRA, FishEye, Crucible and Bamboo into one central jumping off point.&lt;/p&gt;
&lt;p&gt;I&apos;ve always been a fan of LinkedIn, ever since I joined way back in &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_cinco_de_linko&quot;&gt;May 2003&lt;/a&gt;. However, I&apos;ve longed for a way to access my data. &lt;a href=&quot;http://developer.linkedin.com/community/widgets&quot;&gt;LinkedIn Widgets&lt;/a&gt; are nice, but there&apos;s something to be said for the full power of an API. Last night, I sat down for a couple hours and enhanced my &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_oauth_with_gwt&quot;&gt;Implementing OAuth with GWT&lt;/a&gt; example to support LinkedIn&apos;s API.&lt;/p&gt;
&lt;p&gt;I&apos;m happy to report my experiment was a success and you can &lt;a href=&quot;http://static.raibledesigns.com/downloads/gwt-oauth-1.2.zip&quot;&gt;download GWT OAuth 1.2&lt;/a&gt; or &lt;a href=&quot;http://demo.raibledesigns.com/gwt-oauth&quot;&gt;view it online&lt;/a&gt;. For now, I&apos;m simply &lt;a href=&quot;http://developer.linkedin.com/docs/DOC-1008&quot;&gt;authenticating with OAuth&lt;/a&gt; and accessing the &lt;a href=&quot;http://developer.linkedin.com/docs/DOC-1002&quot;&gt;Profile API&lt;/a&gt;. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://demo.raibledesigns.com/gwt-oauth&quot; title=&quot;OAuth with GWT Demo&quot;&gt;&lt;img src=&quot;//farm3.static.flickr.com/2655/4132814004_b423779e59.jpg&quot; width=&quot;453&quot; height=&quot;326&quot; alt=&quot;OAuth with GWT&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;In the process, I learned a couple things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LinkedIn&apos;s OAuth implementation returns an &lt;a href=&quot;http://wiki.oauth.net/Signed-Callback-URLs&quot;&gt;oauth_verifier&lt;/a&gt; parameter after authenticating, whereas Google and Twitter do not. This parameter needs to be included when calling the &lt;em&gt;Access token path&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The Profile API example I implemented gets the current user&apos;s profile with &lt;a href=&quot;http://api.linkedin.com/v1/people/~&quot;&gt;http://api.linkedin.com/v1/people/~&lt;/a&gt;. This returns a &quot;light&quot; version of your profile. To get a more detailed version, you need to use &lt;a href=&quot;http://developer.linkedin.com/docs/DOC-1014&quot;&gt;Field Selectors&lt;/a&gt;. For example:
&lt;span style=&quot;display: block; margin: 5px 5px&quot;&gt;
&lt;a href=&quot;http://api.linkedin.com/v1/people/~:(id,first-name,last-name,picture-url,headline,summary,positions,educations)&quot;&gt;http://api.linkedin.com/v1/people/~:(id,first-name,last-name,picture-url,headline,summary,positions,educations)&lt;/a&gt;
&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;LinkedIn&apos;s API only supports passing OAuth parameters in a header, rather than query parameters. To make this work, I modified my &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_do_cross_domain#proxyServlet&quot;&gt;ProxyServlet&lt;/a&gt; to convert query parameters to an &quot;Authorization&quot; header at the end of the &lt;em&gt;setProxyRequestHeaders()&lt;/em&gt; method.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
// For LinkedIn&apos;s OAuth API, convert request parameters to an AuthorizationHeader
if (httpServletRequest.getRequestURL().toString().contains(&quot;linkedin-api&quot;)) {
    String&amp;#91;&amp;#93; parameters = httpServletRequest.getQueryString().split(&quot;&amp;amp;&quot;);
    StringBuilder sb = new StringBuilder(&quot;OAuth realm=\&quot;http://api.linkedin.com/\&quot;,&quot;);
    for (int i = 0; i &amp;lt; parameters.length; i++) {
        sb.append(parameters&amp;#91;i&amp;#93;);
        if (i &amp;lt; parameters.length - 1) {
            sb.append(&quot;,&quot;);
        }
    }

    Header authorization = new Header(&quot;Authorization&quot;, sb.toString());
    httpMethodProxyRequest.setRequestHeader(authorization);
}
&lt;/pre&gt;
&lt;p&gt;You might recall that my previous example had issues authenticating with Google, but worked well with Twitter. LinkedIn&apos;s authentication seems to work flawlessly. This leads me to believe that Twitter and LinkedIn have a much more mature OAuth implementation than Google.&lt;/p&gt;
&lt;p style=&quot;padding-top: 5px; border-top: 1px dotted silver; color: #666&quot;&gt;&lt;em&gt;&lt;strong&gt;Related OAuth News&lt;/strong&gt;: Apache Roller 5 will be shipping with OAuth support. See &lt;a href=&quot;http://rollerweblogger.org/roller&quot;&gt;Dave Johnson&apos;s&lt;/a&gt; &lt;a href=&quot;http://www.slideshare.net/snoopdave/whats-new-in-roller5&quot;&gt;What&apos;s New in Roller 5 presentation&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update December 6, 2009:&lt;/strong&gt; I modified the gwt-oauth project to use GWT 1.7.1 and changed to the &lt;a href=&quot;http://mojo.codehaus.org/gwt-maven-plugin/&quot;&gt;Maven GWT Plugin&lt;/a&gt; from Codehaus. &lt;a href=&quot;http://static.raibledesigns.com/downloads/gwt-oauth-1.3.zip&quot;&gt;Download GWT OAuth 1.3&lt;/a&gt; or &lt;a href=&quot;http://demo.raibledesigns.com/gwt-oauth&quot;&gt;view it online&lt;/a&gt;.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/user_interface_schema_definitions</guid>
    <title>User Interface Schema Definitions</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/user_interface_schema_definitions</link>
        <pubDate>Fri, 6 Nov 2009 15:18:25 -0700</pubDate>
    <category>The Web</category>
    <category>usixml</category>
    <category>uiml</category>
    <category>xml</category>
    <category>ui</category>
    <category>schemas</category>
    <category>xforms</category>
    <category>xsd</category>
            <description>On my current project, we&apos;re developing a &quot;designer&quot; tool that allows users to build forms in their browser. These forms are displayed in various &lt;em&gt;channels&lt;/em&gt; (e.g. web, mobile, sms) to capture data and make decisions based on user input. 
&lt;/p&gt;
&lt;p&gt;
When I joined the project, it was in a proof-of-concept phase and the form definitions created where serialized as XML (using JAXB) with element names that seemed logical. Now that we&apos;re moving the PoC to production mode, we&apos;re thinking it might be better to change our form definitions to leverage something that&apos;s more &quot;industry standard&quot;. If nothing else, it&apos;ll help with marketing. ;-)
&lt;/p&gt;
&lt;p&gt;This week, I was tasked with doing research on &quot;existing user interface schema definitions&quot;. I&apos;m writing this post to see if there&apos;s any major specifications I&apos;m missing. I plan on providing my recommendation to my team on Monday. Here&apos;s what I&apos;ve found so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.uiml.org/&quot;&gt;User Interface Markup Language (UIML)&lt;/a&gt;: At 120 pages, the 4.0 spec seems very detailed. I&apos;m not sure we&apos;d use all of it, but it&apos;s interesting how the spec allows you to describe the initial tree structure (&amp;lt;structure&amp;gt;) and to dynamically modify its structure (&amp;lt;restructure&amp;gt;). It also has the notion of &lt;em&gt;templates&lt;/em&gt;, which mirrors a similar concept we have in our application. Furthermore, it has VoiceXML support, which could be useful if we use call centers as a channel.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.usixml.org/&quot;&gt;USer Interface eXtensible Markup Language (UsiXML)&lt;/a&gt;: I admit that I haven&apos;t read much of this specification -- mostly because the UIML spec seemed to cover most of what we needed (especially since we&apos;re most interested in describing forms). As far as a I can tell, the major difference between UsiXML is its being submitted to the W3C for standardization (&lt;a href=&quot;http://en.wikipedia.org/wiki/UsiXML&quot;&gt;according to Wikipedia&lt;/a&gt;), while UIML is being standardized by &lt;a href=&quot;http://en.wikipedia.org/wiki/OASIS_%28organization%29&quot;&gt;OASIS&lt;/a&gt;. Beyond that, I find it strange that UIML&apos;s spec is 120 pages and UsiXML is 121. Neither project seems to have any activity this year.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://xml.coverpages.org/userInterfaceXML.html&quot;&gt;Numerous others&lt;/a&gt;: including &lt;abbr title=&quot;Alternate Abstract Interface Markup Language&quot;&gt;AAIML&lt;/abbr&gt;, &lt;abbr title=&quot;Abstract User Interface Markup Language&quot;&gt;AUIML&lt;/abbr&gt;, &lt;abbr title=&quot;Extensible Interface Markup Language&quot;&gt;XIML&lt;/abbr&gt;, &lt;abbr title=&quot;Extensible User Interface Language&quot;&gt;XUL&lt;/abbr&gt;, &lt;abbr title=&quot;Microsoft Extensible Application Markup Language&quot;&gt;XAML&lt;/abbr&gt; and &lt;a href=&quot;http://xml.coverpages.org/userInterfaceXML.html#w3c-XForms&quot;&gt;XForms&lt;/a&gt;. XForms seems like it may be the most logical if we&apos;re only interested in form layout and describing elements within them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all we&apos;re interested in is an XSD to define our forms, the most appealing specs have them: &lt;a href=&quot;http://www.uiml.org/specs/uiml3/DTD.htm&quot;&gt;UIML&lt;/a&gt;, &lt;a href=&quot;http://www.usixml.org/documentation/usixml1.8.0/UsiXML.xsd.html&quot;&gt;UsiXML&lt;/a&gt; and &lt;a href=&quot;http://www.w3.org/MarkUp/Forms/2002/XForms-Schema.xsd&quot;&gt;XForms&lt;/a&gt;. If activity is any sort of motivator for adoption, it&apos;s interesting to note that &lt;a href=&quot;http://www.w3.org/TR/xforms/&quot;&gt;XForms 1.1&lt;/a&gt; was submitted as a W3C recommendation a couple weeks ago (October 20, 2009).&lt;/p&gt;
&lt;p&gt;If you&apos;ve developed some sort of &quot;form designer&quot; tool that renders to multiple channels, I&apos;d love to hear about your experience. Did you use some sort of industry standard to define your form elements, layout, etc. or did you come up with your own?</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/ajax_the_state_of_the</guid>
    <title>Ajax: The State of the Art with Dion and Ben</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/ajax_the_state_of_the</link>
        <pubDate>Thu, 5 Feb 2009 11:03:10 -0700</pubDate>
    <category>The Web</category>
    <category>frameworks</category>
    <category>firefox</category>
    <category>ben</category>
    <category>bespin</category>
    <category>dion</category>
    <category>canvas</category>
    <category>ajax</category>
    <category>html5</category>
    <category>ajaxian</category>
    <category>opera</category>
    <category>thunderhead</category>
            <description>This morning, I added Dion and Ben&apos;s talk titled &lt;a href=&quot;http://north.webdirections.org/program#ajax-the-state-of-the-art&quot;&gt;Ajax: The State of the Art&lt;/a&gt;. Below are my notes from the event.
&lt;/p&gt;
&lt;p&gt;
Ajax started out as a bunch of hacks. It showed that we could take our web interfaces and do a lot more with them. A hack isn&apos;t necessarily a bad thing. Often, they turn into something much more elegant over time. The new browsers have many amazing capabilities that we haven&apos;t taken advantage of yet. We&apos;ve seen discussions on Ajax go from how to do XHR to frameworks and how rich and mature they are. Dojo is great for Enterprise Development (packing system, namespaces). jQuery is well-suited for lightweight developers (PHP). Prototype is fantastic for people who do a lot of JavaScript development and take it very seriously. 
&lt;/p&gt;
&lt;p&gt;
Today&apos;s Ajax landscape is mature, really rich, and really exciting. Today, Dion and Ben are going to talk about technologies they&apos;re really excited about for the future. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Canvas&lt;/strong&gt;&lt;br/&gt;
The building blocks of the web are text, boxes and images. With canvas, it really makes a lot more things possible. You can do bitmap rendering and image manipulation. They&apos;re showing a slide with Doom and Mario Kart running. Canvas 3D does true 3D rendering. Firefox and Opera have done prototypes of this. Can you do canvas-type things today in a browser? Yes, if you use Flash or Curl. Dion and Ben are excited about canvas over plugins for the following reasons:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No start-up delay&lt;/li&gt;
&lt;li&gt;Available on mobile devices today&lt;/li&gt;
&lt;li&gt;Rendering fidelity with browser (especially important for typography)&lt;/li&gt;
&lt;li&gt;No bridges necessary (no marshalling/unmarshalling)&lt;/li&gt;
&lt;li&gt;Not a plug-in&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &amp;lt;canvas&amp;gt; tag originally came from Apple&apos;s Dashboard. Dashboard&apos;s programming model was in HTML and JavaScript. Dashboard is using WebKit under the covers. Today, canvas support exists in every major browser except for IE. The good news is there are Flash and Silverlight bridges to add support to IE. There&apos;s also an ActiveX component that wraps the Firefox implementation and allows it to run in IE.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SVG&lt;/strong&gt;&lt;br/&gt;
Dion and Ben aren&apos;t that excited about SVG because it&apos;s such a huge spec. We&apos;ve been struggling with the HTML standard for the last 10 years and the thought of another huge spec for the next 10 years isn&apos;t that appealing.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fast JavaScript&lt;/strong&gt;&lt;br/&gt;
Almost all major browsers have a Fast JavaScript implementation. Chrome has V8, Safari has SquirrelFish Extreme, Firefox has TraceMonkey and Opera has Carakan. This is exciting because of industry trends and how companies are trying to reduce computation cycles in data centers. The more computing that can be put on the client, the better. IE doesn&apos;t have anything, but Dion and Ben believe they are working on something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Web Workers&lt;/strong&gt;&lt;br/&gt;
Interface latency is awful for applications. Jakob Nielsen once said:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
0.1 second is about the limit for having the user feel that the system is reacting instantaneously. 1.0 second is about the limit for the user&apos;s flow of thought to stay uninterrupted, even though the user will notice the delay.&lt;/p&gt;
&lt;p&gt;
Anything that takes longer than a tenth of a second should be pushed to a background thread. Unfortunately, there are no threads in the web. Maybe we can add threads to JavaScript? Brendan Eich has said that &quot;Threads suck&quot; and there&apos;s very little chance for threads getting into JavaScript. Gears brought Worker Pools and this is going into HTML 5 as Web Workers. You could also use Java applets to do this. With the latest Java Plugin, many of applets&apos; long-standing issues have been solved.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Desktop Integration&lt;/strong&gt;&lt;br/&gt;
The ability to build desktop apps as web apps is very exciting. There&apos;s a few technologies that demonstrate this: Fluid, Mozilla Prism, Adobe AIR, Appcelerator Titanium and Gears. The Palm Pre demonstrates the logical extension of this. The Palm Pre uses the web stack as its developer SDK. It&apos;s very cool that web developers don&apos;t have to learn anything new to become a Palm developer. Desktop integration is exciting especially if we can access desktop applications like email and address book. 
&lt;/p&gt;
&lt;p&gt;
The Ajax frameworks that are out there have done a lot to make web development simpler. However, there&apos;s still a lot of pain with CSS and cross-browser issues. What if you took canvas and combined it with a sophisticated grid-based layout in JavaScript? 
&lt;/p&gt;
&lt;p&gt;
There&apos;s a lot of platforms out there: Microsoft Silverlight, Adobe Flash, Apple Cocoa and Sun&apos;s JavaFX. The web often isn&apos;t considered a platform. Dion and Ben believe there should be an &lt;em&gt;Open Web Platform&lt;/em&gt;. The problem right now is there is no central location to find out how to get stuff done. You have to search and find resources from many different locations. Mozilla is putting it&apos;s resources into creating an Open Web Platform. This site will consist of 4 different areas:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Home&lt;/li&gt;
&lt;li&gt;Documentation (for different frameworks, browsers, quirks)&lt;/li&gt;
&lt;li&gt;Dashboard (state of the open web)&lt;/li&gt;
&lt;li&gt;Roadmap (what&apos;s going on)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
This is not just Mozilla, it&apos;s very much a community effort. This is something that Ben and Dion have been working on. But there&apos;s something else they&apos;ve been working on too. They&apos;ve been talking about all these cool things, but what about an interesting application to test all these technologies?
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Bespin&lt;/strong&gt;&lt;br/&gt;
As they looked at code editors, most of them provide awful user experiences. Bespin is the Editor of Your Dreams and contains the following features:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Accessible from anywhere - any device in any location&lt;/li&gt;
&lt;li&gt;Simple to use, like Textmate (not heavyweight like Eclipse) - an editor, not an IDE&lt;/li&gt;
&lt;li&gt;Wicked Fast - performance, performance, performance&lt;/li&gt;
&lt;li&gt;Rock-solid real-time collaboration, like SubEthaEdit - it just works&lt;/li&gt;
&lt;li&gt;Integrated command-line, like vi - Fun like Quicksilver, social like Ubiquity&lt;/li&gt;
&lt;li&gt;&quot;Self-hosted&quot; environment, like Emacs - For extreme extensibility, but with JavaScript!&lt;/li&gt;
&lt;/ul&gt;
Dion and Ben are showed a screen shot of Bespin and now they&apos;re doing a demo. The core editor has what you&apos;d expect with syntax highlighting and line numbers. Canvas doesn&apos;t have text-selection by default, so they had to write it from scratch. The command line allows you to get help, run core command and also to subscribe to commands that others write. You can change your keybindings to emacs or vi as well as many other settings. Much of Bespin is event-driven, so you can easily plugin new behavior for different events. 
&lt;/p&gt;
&lt;p&gt;
For viewing files, they couldn&apos;t bring themselves to use a tree. Instead, they developed a file-browsing interface that looks very much like Apple&apos;s Finder. Personally, I like Finder, but wish it had Windows Explorer&apos;s path bar that allows you to simply type in the path without mouse clicks. Back to the command line. They&apos;ve done a lot to make things more discoverable so users can easily find the power of the editor.
&lt;/p&gt;
&lt;p&gt;
Bespin could be used to engage developers more with open source projects. Checking out projects, modifying code and creating patches can be a real pain. Bespin could be used to interface with open source projects in the cloud. You could login, modify code and easily patch/build with the click of a button. One other thing they want to do is to have the server do code-analysis as you&apos;re developing.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Is it OK to love a software tool? You &lt;em&gt;must&lt;/em&gt; love your software tools. What we do as Software Developers is one of the most difficult jobs on the planet. Programmers, like poets, start with a blank slate and create something from nothing. If you don&apos;t love your tools, you&apos;ll start resenting what you do. If you don&apos;t love your tools, it shows in your work. &lt;em&gt;-- Dave Thomas at RubyConf08&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Thunderhead&lt;/strong&gt;&lt;br/&gt;
A GUI Toolkit written with canvas and JavaScript. Allows you to do layouts with very little thought. It&apos;s a lab experiment that&apos;s in progress, stay tuned for more information.
&lt;p&gt;
All users care about is the user interface. Dion and Ben believe there&apos;s a key to creating compelling user experiences. It all has to do with &lt;em&gt;managing expectations&lt;/em&gt;. 
It&apos;s not that different from how you manage relationships in your life. Expectations for movies and games have changes drastically over the years. What used to be the web (animated gifs and awful web pages) has also changed drastically (video of Apple&apos;s online store). What was cool with MapQuest got changed drastically with Google Maps. What we have today isn&apos;t the end of the game - expectations will continue to change. However, users have different expectations for software. 
&lt;/p&gt;
&lt;p&gt;
Alan Cooper has done some interesting work in this area. The software designer needs to focus in on a user&apos;s goals. There are basic things you can apply to all users, for instance &quot;sex sells&quot;. An example of this is Delicious Library. This application allows you to keep track of things in your home such as books, movies, music and games. They made $500K in 3 months and made $54K the first day, with no advertising. 
&lt;/p&gt;
&lt;p&gt;
The quality of any software is determined by the interaction. If the interaction isn&apos;t good, it will poison the entire experience. Donald Norman has a good quote: &quot;Attractive things work better&quot;. In society, this is often called &quot;Dress for Success&quot;. 
&lt;/p&gt;
&lt;p&gt;
The Open Web is hear to stay because it has:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An Easy Programming Model&lt;/li&gt;
&lt;li&gt;Easy Remoting&lt;/li&gt;
&lt;li&gt;Extensive Customization Vectors (e.g. GreaseMonkey)&lt;/li&gt;
&lt;li&gt;Easy Deployment&lt;/li&gt;
&lt;li&gt;Great Widgets&lt;/li&gt;
&lt;li&gt;Great Visual Effects&lt;/li&gt;
&lt;li&gt;Great Mobile Story&lt;/li&gt;
&lt;li&gt;Desktop Integration&lt;/li&gt;
&lt;li&gt;State-of-the-Art Plug-ins&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Bespin is a tech preview that they hope to release next week. Thunderhead will be released at the same time.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
This was a great talk and easily the most inspiring of the conference. Dion and Ben always do a great job and the sexiness of their presentation made it all the more appealing. </description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/the_state_of_the_web</guid>
    <title>The State of the Web 2009</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/the_state_of_the_web</link>
        <pubDate>Wed, 4 Feb 2009 18:05:38 -0700</pubDate>
    <category>The Web</category>
    <category>web</category>
    <category>chriswilson</category>
    <category>webdirectionsnorth</category>
    <category>w3c</category>
    <category>wdn09</category>
    <category>opera</category>
    <category>danconnolly</category>
    <category>johnallsopp</category>
    <category>microsoft</category>
    <category>adobe</category>
    <category>larserikbolstad</category>
    <category>scottgegette</category>
            <description>This afternoon, I attended &lt;a href=&quot;http://north.webdirections.org/program#the-state-of-the-web-2009&quot;&gt;The State of the Web 2009&lt;/a&gt; at &lt;a href=&quot;http://north.webdirections.org&quot;&gt;Web Directions North&lt;/a&gt;. Below are my notes from this session.
&lt;/p&gt;
&lt;p&gt;
This panel has quite the list of superstars:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#chris-wilson&quot;&gt;Chris Wilson&lt;/a&gt; (Microsoft)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#dan-connolly&quot;&gt;Dan Connolly&lt;/a&gt; (W3C HTML Working Group)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers#michael-tm-smith&quot;&gt;Mike (TM) Smith&lt;/a&gt; (W3C HTML Working Group)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#lars-erik-bolstad&quot;&gt;Lars Erik Bolstad&lt;/a&gt; (Opera)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#scott-fegette&quot;&gt;Scott Fegette&lt;/a&gt; (Adobe)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#john-allsopp&quot;&gt;John Allsopp&lt;/a&gt; (Organizer and &lt;a href=&quot;http://westciv.com/style_master/index.html&quot;&gt;Style Master&lt;/a&gt; Developer)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
John is moderating this session and is starting by asking each panelist to speak about what they believe the state of the web is. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chris Wilson&lt;/strong&gt;&lt;br/&gt;
The future of the web may be as ubiquitous as electricity. Chris has a desktop, two laptops (one 10&quot; NetBook, one is a 13&quot; MacBook) and an iPhone. There&apos;s a lot of difference between these devices, especially when it comes to screen size. Chris uses a number of different browsers throughout the day. The web isn&apos;t just one browser, it isn&apos;t just one platform. He&apos;s showing a slide with a browser market share graph from &lt;a href=&quot;http://www.netapplications.com&quot;&gt;http://www.netapplications.com&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;Many different browsers are a reality. Many different devices are a reality. Web builders need to learn to write scalable applications that run across multiple browsers, devices and environments. They need to use progressive functionality and learn the tools they have in CSS and HTML. Semantic structuring helps. 
&lt;/p&gt;
&lt;p&gt;
Developers need to learn to live with multiple browsers. Cross-browser interoperability will get better, but it&apos;s likely to be an issue forever. Test suites with new specifications are helping. Developers should build for browsers of today &lt;em&gt;and&lt;/em&gt; tomorrow. 
&lt;/p&gt;
&lt;p&gt;You should build the applications you want to build and then figure out how how to make them degrade gracefully on the web.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lars Erik Bolstad&lt;/strong&gt;&lt;br/&gt;
Opera Software has 600 employees in 10 countries. They&apos;ve been developing Opera and other browsers since 1995. The bulk of what Opera does is based on commercial browser deliveries to OEMs around the world. 
&lt;/p&gt;
&lt;p&gt;Devices, platforms and networks: they come in all shapes and sizes. Only one thing unifies them: &lt;strong&gt;The Web&lt;/strong&gt;. The browser is becoming more and more important on these devices. Users are not satisfied with WAP-based content anymore, they want the same content no matter which device they&apos;re using.&lt;/p&gt;
&lt;p&gt;Opera Mini does its processing on the server-side. This allows Opera to gather statistics. These stats show that users around the world hit the same top sites on their mobile devices as they do on their desktops. It&apos;s a one-to-one match. Opera is seeing tremendous growth in the usage of Opera Mini, both in developed countries and emerging markets. 
&lt;/p&gt;
&lt;p&gt;The point: don&apos;t just develop for desktop browsers. The mobile market seems to be growing much faster. The problem is actually more in the hands of browser developers since they have to satisfy the user&apos;s desire to see the same content on mobile vs. desktop. 
&lt;/p&gt;
&lt;p&gt;Opera is focusing on advancing core browser technology in three areas:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Web standards&lt;/strong&gt;: CSS (webfonts, backgrounds/borders, transitions, transforms), HTML 5 (video/audio, persistent storage, drag and drop) and W3C Geolocation API and &quot;Mobile DOM&quot; API (access to camera, address book, calendars).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: VEGA (vector graphics-based rendering, hardware acceleration) and CARAKAN (new JavaScript engine, native compilation). For more details, see &lt;a href=&quot;http://my.opera.com/core&quot;&gt;http://my.opera.com/core&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web applications&lt;/strong&gt;: Standalone web apps, RIAs, Widgets. Gears support and &lt;a href=&quot;http://www.opera.com/dragonfly&quot;&gt;Developer Tools&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Dan Connolly&lt;/strong&gt;&lt;br/&gt;
The web is kinda important these days. It&apos;s a big deal. Make a mistake and 300 million dollars go away (see end of last entry about United news). One of the beauties of the web is you can easily participate as an individual. You can report bugs, write articles and be a part of many web standards groups. Most of the other systems in the world don&apos;t provide this kind of access.
&lt;/p&gt;
&lt;p&gt;Dan has been under a rock for the last 5 years working on Semantic Web stuff. Now that he&apos;s back in the game, it&apos;s incredible how much stuff is going on. He&apos;s glad there&apos;s JavaScript frameworks so he doesn&apos;t have to learn everything. The default security policies in browsers are a little rickety at this point. They allow you to download and run JavaScript from virtually any site. &lt;a href=&quot;http://code.google.com/p/google-caja/&quot;&gt;Caja&lt;/a&gt; might help to solve this. Dan believes that security will become more important and stricter to protect web users.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scott Fegette&lt;/strong&gt;&lt;br/&gt;
Scott is a Product Manager in the Web Group of Adobe. At the beginning of each year, they do heavy user research. Adobe wants people that develop content for the web to be as expressive as possible. Scott is going to give us a peak into the conversations he&apos;s had with the web community.
&lt;/p&gt;
&lt;p&gt;One of the biggest topics on people&apos;s minds is The Economy, but it&apos;s not negative as you might think. Small web designers are actually getting more business in the downturn, likely because companies are polishing their presence on the web. People are working much more distributed these days. There&apos;s a few areas that Adobe generally asks about: CSS, JavaScript, HTML (both statically and dynamically).
&lt;/p&gt;
&lt;p&gt;Frameworks are becoming more important to developers, as well as with clients. They&apos;ve even seen some clients demand certain frameworks. Two years ago, when Adobe talked to small design shops and agencies, most web sites were built statically. Now they&apos;re developing with frameworks like WordPress. Out of 60 folks they talked to, only 2 were using static systems and not CMSes.
&lt;/p&gt;
&lt;p&gt;In the JavaScript frameworks arena, jQuery is the dominant leader. Shops are starting to use CSS frameworks as well. The only one Scott mentioned was Reset. Design is becoming a technical discipline and Adobe is calling this &lt;em&gt;Stateful Design&lt;/em&gt;. WYSIWIG is definitely dying and designers aren&apos;t developing with visual tools.
&lt;/p&gt;
&lt;p&gt;The kind of projects that people are working on has changed a lot. Many shops are being asked to do work on mobile development. The iPhone has done wonders for the industry in raising the awareness of what a mobile device can do.&lt;/p&gt;
&lt;p&gt;The other big investments for Adobe is RIAs and AIR. Ajax has matured enough that it can now compete with proprietary plugins like Flash. The reason for AIR is to allow web developers to use their skills to develop desktop applications. Flash and Flex are often overkill for browser-based applications, but they do often handle video and audio better than Ajax applications.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mike (TM) Smith&lt;/strong&gt;&lt;br/&gt;
Mike is also known as the &quot;W3C HTML jackass&quot;. Mike thinks the state of the web is that it&apos;s a mess in a lot of ways. If you don&apos;t believe him, ask Doug Crockford. Most of this stuff is going to remain a mess for the next 20 years, unless another genius like Tim Berners-Lee comes along and invents something new. However, the good part about it being a mess is that we all have jobs.
&lt;/p&gt;
&lt;p&gt;
One of the biggest things they&apos;re trying to do with HTML 5 is not breaking backward compatibility. Other working groups at the W3C don&apos;t share this philosophy, hence the reason they don&apos;t have browser vendors participating. Many of the ideas for HTML 5 game from Gears and Ajax Framework developers like &lt;a href=&quot;http://ejohn.org&quot;&gt;John Resig&lt;/a&gt;. All this will make things less messy, especially with the help of browser vendors.
&lt;/p&gt;
&lt;p&gt;Developers like the ubiquitous web and are pushing the mobile web. Mike thinks everyone just needs to get a life (big applause). For mobile, SVG has already been a big success. You will see significant great things with SVN happen in major browsers by next fall. If you&apos;re a web developer, you should spend some time experiment with SVG. It will payoff for you. If it doesn&apos;t pay off for you and you see Mike next year at Web Directions North, you can punch him in the face.
&lt;/p&gt;
&lt;p&gt;Location-aware applications will be big as well. Browser vendors are implementing the Geo Location API. It&apos;s implemented in Opera, Firefox, WebKit and Gears. Video on the web will be significant as well. The SVG working group pioneered video support into standards, before HTML 5. Many of the problems they face are related to video codecs. The only way to solve the problems with video on the web is with money and lawyers. Very specifically, there&apos;s no royalty-free codec for video. This is nothing that standards bodies can solve. The most promising is that Sun Microsystems is developing an open codec and spending money to make sure they&apos;re not infringing on patents. &lt;/p&gt;
&lt;p&gt;Not only is the HTML Working Group improving markup in HTML, they&apos;re also working on coming up with new APIs that give you access to features. If you have ideas that aren&apos;t included in HTML 5, the group is definitely interested in hearing about them. 
&lt;/p&gt;
&lt;p&gt;
After each panelist talked, John asked them questions about what&apos;s the biggest thing they&apos;d like to see implemented by everyone (open video codec, geo location api were the winners). Mike also did some complaining about XML and how broken it is because there&apos;s no failure mechanism. There was some audience banter with Chris about SVG in IE. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
This was a very interesting session, especially to hear from the people who are building/supporting the future of the web. I liked Scott&apos;s talk on what Adobe&apos;s hearing from their users. I also liked hearing Mike (TM)&apos;s opinionated thoughts on XML and his non-marketing approach to most everything related to the web. Lars from Opera had a marketing-ish presentation, but it was nevertheless interesting to hear what Opera&apos;s working on. Good stuff.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/changes_in_the_languages_of</guid>
    <title>Changes in the Languages of the Web with Dan Connolly</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/changes_in_the_languages_of</link>
        <pubDate>Wed, 4 Feb 2009 11:34:54 -0700</pubDate>
    <category>The Web</category>
    <category>languages</category>
    <category>webdirectionsnorth</category>
    <category>css</category>
    <category>opensource</category>
    <category>xhtml</category>
    <category>web</category>
    <category>wdn09</category>
    <category>danconnolly</category>
    <category>html</category>
            <description>&lt;a href=&quot;http://north.webdirections.org&quot; title=&quot;Web Directions North&quot;&gt;&lt;img src=&quot;//farm4.static.flickr.com/3437/3253069385_f3afc41182_o.png&quot; width=&quot;248&quot; height=&quot;68&quot; alt=&quot;Web Directions North Logo&quot; class=&quot;picture&quot; style=&quot;border: 0&quot; /&gt;&lt;/a&gt;
Web Directions is held in Australia and Japan and now the US. A few months ago, they were a bit hesitant about doing it in Denver in the middle of winter. However, they&apos;ve discovered our best-kept secret: it&apos;s beautiful and sunny all week. People are attending this conference from all around the world. 
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://www.w3.org/People/Connolly/&quot;&gt;Dan Connolly&lt;/a&gt; is the keynote speaker. He&apos;s played some very important roles, such as Chairmain of the HTML Working Group during HTML 4. He&apos;s also a research scientist at the Computer Science and Artificial Intelligence Laboratory at MIT and a member of the technical staff at the W3C. &lt;/p&gt;
&lt;p&gt;Dan&apos;s talk is titled &lt;strong&gt;Changes in the Languages of the Web&lt;/strong&gt; and you can view it online at &lt;a href=&quot;http://www.w3.org/2009/Talks/02wdn/slides&quot;&gt;http://www.w3.org/2009/Talks/02wdn/slides&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;Web Languages are like languages of the world. Like programming languages, web languages are artificial. Web-native languages are PHP and JavaScript. A Web Language is influenced by natural languages but are artificial. Learning languages is like reading music. With music, most learn from good ol&apos; fashion sheet music. Nowadays, many are learning to read music from Rock Band (the game).&lt;/p&gt;
&lt;p&gt;Is there a web language for music? It&apos;s not mp3/ogg and it&apos;s not Apple&apos;s GarageBand. ABC music notation is close and fake-book style cords mostly works. The problem is often these sites and specifications disappear because copyright holders come and scare them away.
&lt;/p&gt;
&lt;p&gt;Technology deployment rides on the practice of sharing media and culture. Open standards preserve freedom to tinker and supports cultural heritage. &lt;a href=&quot;http://www.lessig.org/blog/&quot;&gt;Lawrence Lessig&lt;/a&gt; at OSCON 2002:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creativity and innovation always builds on the past.&lt;/li&gt;
&lt;li&gt;The past always tries to control the creativity that builds upon it.&lt;/li&gt;
&lt;li&gt;Free societies enable the future by limiting this power of the past.&lt;/li&gt;
&lt;li&gt;Our is less and less a free society.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Lastly, Lawrence asked &lt;strong&gt;what have you done about it?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
In 1991, some places would fire you for looking at code from the Net. At the time, Dan worked at Convex, who encouraged participation in Free Software. Convex did business with HP; HP used SGML; HTML was SGML (almost). When Dan read about the World Wide Web from &lt;a href=&quot;http://www.w3.org/People/Berners-Lee/&quot;&gt;Tim Berners-Lee&lt;/a&gt;, he had an excuse to look at HTML as part of his day job.
&lt;/p&gt;
&lt;p&gt;
Computer Science students and hackers learn &lt;a href=&quot;http://www.cui.unige.ch/db-research/Enseignement/analyseinfo/AboutBNF.html&quot;&gt;BNF&lt;/a&gt; and parse trees. SGML is a little funny looking, but works mostly like BNF. Feedback loop: 
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Draft a DTD&lt;/li&gt;
&lt;li&gt;Run some tests, &lt;em&gt;ask the computer&lt;/em&gt; if it matches the test cases&lt;/li&gt;
&lt;li&gt;Discover an issue; repeat&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
While Dan was at Hal in Austin in 1994, and adding HTML support in products, he:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Asked other HTML Working Group members to try James Clark&apos;s &lt;code&gt;sgmls&lt;/code&gt; parser&lt;/li&gt;
&lt;li&gt;Not many of them were in the habit of building software from source&lt;/li&gt;
&lt;li&gt;Mark Gaither and Dan installed &lt;code&gt;sgmls&lt;/code&gt; as a CGI service&lt;/li&gt;
&lt;li&gt;Feedback loops works over the Web!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Dan was first the editor and then the chair of the standardization of HTML 4. HTML&apos;s standardization timeline: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;November 1995: HTML 2.0&lt;/li&gt;
&lt;li&gt;January 1997: HTML 3.2&lt;/li&gt;
&lt;li&gt;December 1997: HTML 4.0&lt;/li&gt;
&lt;li&gt;December 199: HTML 4.0.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Browser marketplace explodes and then stagnates. Early 1990&apos;s - lots of little projects. In 1995, Netscape Navigator IPO rewrites the business books. In the late 90&apos;s IE takes over Netscape. After HTML, Dan started working on other stuff: Feb 98 (XML 1.0), Jan 99 (Namespaces in XML), Jan 00 (XHTML 1.0), Feb 04 (RDF and OWL), Apr 06 (SPARQL) and Sep 2007 (GRDDL). 
&lt;/p&gt;
&lt;p&gt;
W3C fostered many of the technologies of Ajax and Web 2.0:
&lt;/p&gt;
&lt;p&gt;
&lt;ul&gt;
&lt;li&gt;HTML, CSS, DOM, XML from W3C circa 2000&lt;/li&gt;
&lt;li&gt;JavaScript from Netscape, ECMA in 1995&lt;/li&gt;
&lt;li&gt;XMLHttpRequest from Microsoft in 1999&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
W3C&apos;s efforts since then lacked clear deployment paths. 
&lt;/p&gt;
&lt;p class=&quot;quote&quot; style=&quot;color: #999&quot;&gt;
XHTML is not the solution to a problem that concerns anybody except the guys who have to write parsers that convert markup into DOM trees. It turns out that XHTML put the validation on the wrong end of the network. It turned out that the market didn&apos;t put much value in a document delivery system that could decide to not display the document because there was an unrecognized attribute on an invisible meta tag.&lt;br/&gt;
&lt;span style=&quot;float: right&quot;&gt;&lt;em&gt;-- &lt;a href=&quot;http://blog.360.yahoo.com/blog-TBPekxc1dLNy5DOloPfzVvFIVOWMB0li?p=736&quot;&gt;Doug Crockford Jan 2008&lt;/a&gt;&lt;/em&gt;&lt;/span&gt;&lt;br/&gt;
&lt;/p&gt;
&lt;p&gt;
The web isn&apos;t just for computer geeks anymore. From The Future of Information by &lt;a href=&quot;http://en.wikipedia.org/wiki/Ted_Nelson&quot;&gt;Ted Nelson&lt;/a&gt; in 1997:&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;
The software world currently corresponds to the Pre-Director stage in movie-making (1893-1904). During those years, when short films were already being shown in theaters, the job of making the movie was given to the cameraman - because he knew how to work the equipment.
&lt;br/&gt;&lt;br/&gt;
That is how it is with software today. Today&apos;s software designers are those who only understand the technicalities, and not - with rare exceptions - those who understand how to integrate the &lt;strong&gt;presentation of ideas to the mind and heart&lt;/strong&gt;.
&lt;/p&gt;
&lt;p&gt;
The Web facilitates a shift from mass media to participatory culture. Worth watching: &lt;a href=&quot;http://www.youtube.com/watch?v=TPAO-lZ4_hU&quot;&gt;An Anthropological Introduction to YouTube&lt;/a&gt; by Michael Wesch and the Digital Ethnography Working Group.
&lt;/p&gt;
&lt;p&gt;
The W3C HTML Working Group charted in March 2007 and ended up with hundreds of participants (most prior working groups had 20-30 people). In November 2007, there was an HTML WG meeting at W3C Technical Plenary and was very much an unconference. The HTML 5 Working draft was published in January 2008. Goals for HTML 5:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Off-the-shelf parsers and tools for reading web pages like browsers do, including &lt;em&gt;tag soup&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Modern test materials&lt;/li&gt;
&lt;li&gt;Standardize successful experiments in the Web Applications platform to balance the attraction of proprietary approaches:
&lt;ul&gt;
 &lt;li&gt;&amp;lt;video&gt;, &amp;lt;audio&gt;&lt;/li&gt;
 &lt;li&gt;scripting details, security policies&lt;/li&gt;
 &lt;li&gt;offline storage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
HTML 5 co-chairs: Chris Wilson (Microsoft) and Sam Ruby (IBM) with W3C staff support from Mike Smith and Dan Connolly.
&lt;/p&gt;
&lt;p&gt;
Some ideas from the &lt;a href=&quot;http://esw.w3.org/topic/CssValidator#head-5af29f77778fcf8e536ae2aa959b11753c380dae&quot;&gt;CSS validator roadmap&lt;/a&gt;:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript CSS parser&lt;/li&gt;
&lt;li&gt;Support for CSS 2.1 forward-compatible grammar&lt;/li&gt;
&lt;li&gt;Integrate test-result data showing browser support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
The browser marketplace is moving again. 
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
2003: Internet Explorer, Firefox, Safari, Opera. 
&lt;/li&gt;
&lt;li&gt;April 2006: &lt;a href=&quot;http://www.ipsos-na.com/news/pressrelease.cfm?id=3049&quot;&gt;Mobile Phones Poised To Overtake The PC As The Dominate Internet Platform In Some Markets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;August 2007: iPhone&lt;/li&gt;
&lt;li&gt;Sep 2008: Google Chrome&lt;/li&gt;
&lt;li&gt;Jan 2009: Internet Explorer 8 Release Candidate&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
How about authors? How do they feel about HTML 5? Two days ago, Dan &lt;a href=&quot;http://www.w3.org/News/2009#item11&quot;&gt;received support from Adobe&lt;/a&gt; to work on HTML 5 materials for authors. 
&lt;/p&gt;
&lt;p&gt;
Design Principles Last Through Change. From &lt;a href=&quot;http://twitter.com/zeldman/status/1137456194&quot;&gt;Zeldman on Twitter&lt;/a&gt;: Client who saves $5K buying cut-rate non-semantic HTML will later spend $25K on SEO consultant to compensate. &lt;/p&gt;
&lt;p&gt;
There&apos;s still something to the nothing behind XHTML+CSS Web Design. Kudos to whoever designed the &lt;a href=&quot;https://www.accesskansas.org/ssrv-webfile/index.html&quot;&gt;Kansas Tax web site&lt;/a&gt; (Dan is from Kansas City).
&lt;/p&gt;
&lt;p&gt;
Sharing data in documents is one of the original goals of the WWW. Dan has been investing some of his own time into microformats. 
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://tantek.com/log/2006/06.html#d17t2231&quot;&gt;Tantek &#199;elik in June 2006&lt;/a&gt;: &quot;XML formats in the long run are not better than propriety binary formats.&quot;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XML, both in technology (namespaces...) and as a &quot;technical culture&quot; is too biased towards Tower of Babel outcomes.&lt;/li&gt;
&lt;li&gt;A few XML formats may survive and converge (RSS, maybe Atom).&lt;/li&gt;
&lt;li&gt;But for now, XHTML is the only longterm reliable XML format that has more to do with it being based on HTML than it being XML.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
And if longevity is not a goal, try JSON - it&apos;s yummy.
&lt;/p&gt;
&lt;p&gt;
Microformats are not just technical ideas, but it&apos;s something that you can actually use in your life. XSPF - what if media players had used an XHTML dialect a la hMedia. RSS and hAtom - will feed readers grow native support for hAtom? Will calendar subscription clients grow native support for hCalendar?  The process and the technology of microformats provide an 80% solution for global scale problems. 
&lt;/p&gt;
&lt;p&gt;
The Personal Information Disaster: The bane of my existence is doing things I know the computer could do for me. -- &lt;a href=&quot;http://www.nature.com/nature/webmatters/xml/xml.html&quot;&gt;The XML Revolution&lt;/a&gt;, Nature Web Matters Oct 1998. 
&lt;/p&gt;
&lt;p&gt;
Let&apos;s find ways to make it cost-effective record and share knowledge &lt;em&gt;formally&lt;/em&gt;, i.e. so that computers can manipulate it. How great would it be if your kid&apos;s soccer coach could distribute a schedule that would feed into everyone&apos;s calendaring system?
&lt;/p&gt;
&lt;p&gt;
Flickr, Facebook and Twitter demonstrate the attraction of hosted services. Identi.ca supports federation (OpenMicroBlogging). Instant Messaging is much like e-mail used to be, where you couldn&apos;t e-mail folks that used a different provider.
&lt;/p&gt;
&lt;p&gt;
Be careful not to delegate &lt;i&gt;too much&lt;/i&gt; to machines!
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A United Airlines near-bankruptcy item from 2002 appeared as 2008 news via Google News&lt;/li&gt;
&lt;li&gt;Syndication continued up to a Bloomberg news flash.&lt;/li&gt;
&lt;li&gt;UAL stock cratered from $12 to $3 ($1.14 billion in market cap).&lt;/li&gt;
&lt;li&gt;The stock recovered within the day to $10 (down $300M in market cap)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Something to keep an eye on - &lt;em&gt;SEC Interactive DATA and XBRL&lt;/em&gt;. Three dozen companies, representing more than $1 trillion of market value, have joined the SEC&apos;s test group. Have been working on this since 2005.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
The balance between proprietary risk/reward and open standards is delicate. Media independence is more important than ever as mobile emerges. When content doesn&apos;t match specs, changing browsers is cheap compared to changing all the content, authors. Web technology is deeply intertwingled with social, economic context.
&lt;/p&gt;
&lt;p&gt;I enjoyed Dan&apos;s talk. He&apos;s obviously a smart guy and has been involved with the web since before it even existed. More than anything, I like the conference location. It&apos;s 1/2 block from my office and has &lt;a href=&quot;http://twitpic.com/1bs8c&quot;&gt;excellent views&lt;/a&gt;. I hope to return for &lt;a href=&quot;http://north.webdirections.org/program#the-state-of-the-web-2009&quot;&gt;The State of the Web 2009&lt;/a&gt; later this afternoon.
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/oscon_2008_even_faster_web</guid>
    <title>[OSCON 2008] Even Faster Web Sites by Steve Souders</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/oscon_2008_even_faster_web</link>
        <pubDate>Thu, 24 Jul 2008 16:23:12 -0600</pubDate>
    <category>The Web</category>
    <category>stylesheets</category>
    <category>css</category>
    <category>yslow</category>
    <category>ie</category>
    <category>javascript</category>
    <category>firefox</category>
    <category>performance</category>
    <category>webperformance</category>
    <category>oscon</category>
    <category>oscon08</category>
    <category>browsers</category>
    <category>firebug</category>
            <description>&lt;a href=&quot;http://stevesouders.com/&quot;&gt;Steve&lt;/a&gt; works at Google on web performance and open source initiatives. To begin his talk, Steve is running YSlow in &quot;autorun&quot; mode. This runs YSlow Performance tests on the top 100 sites according to &lt;a href=&quot;http://www.alexa.com/&quot;&gt;Alexa&lt;/a&gt;. Before Google, Steve was at Yahoo for 7 years. You can download Steve&apos;s slides from &lt;a href=&quot;http://stevesouders.com/&quot;&gt;his site&lt;/a&gt; (don&apos;t ask me where).
&lt;/p&gt;
&lt;p&gt;iGoogle with an empty cache: 9% of the time is spent getting the HTML document. The % of time of what a webserver does is a pretty small percentage of the overall picture. If the cache is primed, the time goes up to 17% of the time. &lt;/p&gt;
&lt;p&gt;80-90% of the end-user response time is spent on the frontend. Start there.&lt;/p&gt;
&lt;p&gt;There&apos;s a greater potential of improvement on the frontend. If you improve the backend performance by 50%, chances are the end-user only sees a 5% improvement. 
&lt;/p&gt;
&lt;p&gt;
The 14 Rules are encapsulated in the YSlow plugin. At OSCON last year, Yahoo released YSlow. 500,000 downloads since it was released. Following the release of YSlow, Steve wrote &lt;a href=&quot;http://oreilly.com/catalog/9780596529307/&quot;&gt;High Performance Web Sites&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;High Performance Web Sites, Vol 2. The 3 most important rules are:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Split the initial payload&lt;/li&gt;
&lt;li&gt;Load scripts without blocking&lt;/li&gt;
&lt;li&gt;Don&apos;t scatter inline scripts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Why focus on JavaScript? A lot of the top sites use JavaScript. For example, up until a few weeks ago, Facebook served up 1MB of JavaScript, uncompressed. Scripts block parallel downloads and page rendering. To see it in action, go to &lt;a href=&quot;http://stevesouders.com/cuzillion/?ex=10008&quot;&gt;http://stevesouders.com/cuzillion/?ex=10008&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Any content below a &amp;lt;script&gt; is blocked from rendering - even if it&apos;s already cached in the browser. &lt;a href=&quot;http://stevesouders.com/cuzillion/&quot;&gt;Cuzillion&lt;/a&gt; is an open source project that Steve is releasing that allows you to add components to a page and test their performance.&lt;/p&gt;
&lt;p&gt;Split your JavaScript between what&apos;s needed to render the page and everything else. Load &quot;everything else&quot; &lt;em&gt;after&lt;/em&gt; the page is rendered. To do this, you can use Firebug to do it manually, or you can use &lt;a href=&quot;http://research.microsoft.com/projects/doloto/&quot;&gt;Doloto&lt;/a&gt; from Microsoft to automate the splitting of your files.&lt;/p&gt;
&lt;p&gt;MSN.com solves the script blocking problem by using &lt;a href=&quot;http://ajaxpatterns.org/On-Demand_Javascript#DOM-Based_On-Demand_Javascript&quot;&gt;JS DOM&lt;/a&gt; to allow for parallel downloading. There&apos;s 6 techniques for doing this:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XHR Eval (must have same domain as page)&lt;/li&gt;
&lt;li&gt;XHR Injection (same domain)&lt;/li&gt;
&lt;li&gt;Script in iFrame (same domain)&lt;/li&gt;
&lt;li&gt;Script DOM Element (domains can differ)&lt;/li&gt;
&lt;li&gt;Script Defer (only supported in IE, domains can differ)&lt;/li&gt;
&lt;li&gt;document.write (not recommended, parallelization only works in IE)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How do these techniques cause &quot;browser busy&quot; indicators? XHR Eval and Injection don&apos;t trigger any indicators. You need to choose when you want to show busy indicators. It&apos;s good to show them when you want to show your users that something is processing (but not for lazy-loading JavaScript that&apos;s not required for load). For the different techniques, most don&apos;t ensure order of parsing. 
&lt;/p&gt;
&lt;p&gt;
Based on 3 factors, Steve can tell you which technique is best to use. These three factors are 1) the URL of the page and script 2) if you want busy indicators and 3) if you care about order. Steve thinks it would be awesome if web frameworks could support this to write out JavaScript appropriately for the developer&apos;s input.
&lt;/p&gt;
&lt;p&gt;
Long executing inline scripts block rendering and downloads. If you know you&apos;re going to have scripts like this, you can solve it with a couple workarounds:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initiate execution with setTimeout(&gt;250 for Firefox)&lt;/li&gt;
&lt;li&gt;Move JavaScript to enternal script with advanced downloading techniques&lt;/li&gt;
&lt;li&gt;Use defer attribute for IE&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
In Firefox 2, stylesheets block parallel downloads just like scripts. IE doesn&apos;t. However, IE will block when you have a stylesheet followed by an inline script. To solve, it&apos;s best to move line scripts above stylesheets or below other resources. use &amp;lt;link&gt;, not @import. 
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Takeaways:&lt;/strong&gt; focus on the frontend, run &lt;a href=&quot;http://developer.yahoo.com/yslow/&quot;&gt;YSlow&lt;/a&gt;, focus on JavaScript (split initial payload, load scripts w/o blocking, don&apos;t scatter inline scripts).
&lt;/p&gt;
&lt;p&gt;
Three Announcements:
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;http://httpwatch.com&quot;&gt;HTTPWatch&lt;/a&gt; for Firefox (not public yet, not free). 
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://getfirebug.com/lite.html&quot;&gt;Firebug Light 1.2&lt;/a&gt; (can be used with IE or Opera).
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://ejohn.org/blog/firebuggin/&quot;&gt;Mozilla is Firebuggin&apos;&lt;/a&gt; (Mozilla is dedicating 3 people to work on Firebug).
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/oscon_2008_css_for_high</guid>
    <title>[OSCON 2008] CSS for High Performance JavaScript UI by Gavin Doughtie</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/oscon_2008_css_for_high</link>
        <pubDate>Thu, 24 Jul 2008 15:30:32 -0600</pubDate>
    <category>The Web</category>
    <category>css</category>
    <category>javascript</category>
    <category>oscon</category>
    <category>oscon08</category>
    <category>ie6</category>
            <description>&lt;a href=&quot;http://blog.xdraw.org/&quot;&gt;Gavin&lt;/a&gt; is a front end developer on Google&amp;rsquo;s Picasa Web Albums and a contributor to the dojo toolkit. The original designer tools for Web 1.0 were the FONT tag, TABLE hacks and spacer gifs. Now we live in the future (shows a picture of the iPhone). Maybe it&apos;s time to accept that we&apos;re stuck with CSS. &quot;I&apos;m a coder, not a designer&quot; is what happens at OSCON.
&lt;/p&gt;
&lt;p&gt;There are costs of JavaScript: development, download, parse time and runtime performance. It&apos;s extremely powerful and high-level, but it&apos;s slow because it&apos;s interpreted. Drawing a box with JavaScript in Firefox 3 isn&apos;t too difficult. Another way to do the same thing is with CSS. Doing the same thing in CSS is much faster and requires less code. Surrender To Win. You don&apos;t have to code it all. You can hand off part of that to your runtime environment (browser).
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CSS Fundamentals&lt;/strong&gt;&lt;br/&gt;
You want to how things &lt;em&gt;flow&lt;/em&gt; in a page. Browsers were originally designed to render text. They&apos;re built to render flowing text. Other important fundamentals include &lt;em&gt;float&lt;/em&gt;, &lt;em&gt;positioning&lt;/em&gt;, &lt;em&gt;negative margins&lt;/em&gt;, &lt;em&gt;relative units&lt;/em&gt;, &lt;em&gt;pseudo-selectors&lt;/em&gt;. Lastly, you need to know when to use tables. Gavin is now showing an example of using CSS to modify an image so it scales with the browser window. It&apos;s pretty simple:
&lt;/p&gt;
&lt;pre&gt;
body {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
  overflow: hidden;
}

img { 
  position: relative;
  width: 100%;
  height: 100%;
}
&lt;/pre&gt;
&lt;p&gt;Relatively Absolutely: &quot;Absolute&quot; in relative to &lt;code&gt;position:relative&lt;/code&gt;. If an element has absolute positioning, it will only be positioned relative to it&apos;s parent element. To draw a box on an image, it&apos;s usually best to calculate the percentage position and sizes in script or on the server. The world&apos;s greatest psuedo-selector is &lt;code&gt;:hover&lt;/code&gt;. Unfortunately, &lt;code&gt;:hover&lt;/code&gt; doesn&apos;t work on anything but &amp;lt;a&gt; in IE 6. &lt;/p&gt;
&lt;p&gt;With &lt;em&gt;floats&lt;/em&gt;, you can &quot;float&quot; elements to the top, right, bottom and left. In Firefox and Safari, you can set a border-radius to draw lines that are curved. Before Firefox 3, these lines where terrible looking.&lt;/p&gt;
&lt;p&gt;Tables make a lot of sense when you have a grid layout. Use &lt;code&gt;table-layout:fixed&lt;/code&gt; if you don&apos;t want table cells to be the width of their contents.
&lt;/p&gt;
&lt;p&gt;For sizing images and other elements, it&apos;s usually best to use &lt;code&gt;em&lt;/code&gt; for the height and width and relative measurements. You can then use &lt;code&gt;style.fontSize&lt;/code&gt; to resize the images. Using this technique is must faster than using JavaScript to set the height and width on an image.&lt;/p&gt;
&lt;p&gt;WebKit, the CSS Wonderland. Gavin loves WebKit. It&apos;s on Safari (Windows, OS X and iPhone), Android, GNOME and KDE. With WebKit, you can build expanding lists w/o any coding. To do gradients in WebKit, you use:
&lt;/p&gt;
&lt;pre&gt;
background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#00abeb));
-webkit-border-radius: 0.24em;
&lt;/pre&gt;
&lt;p&gt;
To do animation, you can use:&lt;/p&gt;
&lt;pre&gt;
-webkit-transition-property: width, height;
-webkit-transition-duration: 250ms;
-webkit-transition-timing-function: default;
&lt;/pre&gt;
&lt;p&gt;
Another thing you can do with WebKit is rotate images (using &lt;code&gt;-webkit-transform: rotate()&lt;/code&gt;) and just about anything (divs, forms, etc.). WebKit also supports SVN masks, which means you can open up a hole and view an image through it. 
&lt;/p&gt;
&lt;p&gt;
Gecko&apos;s pretty cool too. Firefox 3 introduced a new rendering engine based on Cairo. You can do an SVG transform in Firefox 3 to create reflections of elements (divs) in your page.
&lt;/p&gt;
&lt;p&gt;
&quot;With browsers, you cut people off at the knees, but everyone&apos;s the same height.&quot; -- Alex Russell
&lt;/p&gt;
&lt;p&gt;
IE 6 is not the problem, we&apos;re the problem. If you drop support for IE 6, you won&apos;t have to worry about coding towards it. It might not be possible if you work for a large company, but if you&apos;re small, you should definitely think about it hard. 
&lt;/p&gt;
&lt;p&gt;
It&apos;s funny to think that IE 6 is the new Netscape 4.
&lt;/p&gt;
&lt;p&gt;Gavin&apos;s slides from this talk will be available at &lt;a href=&quot;http://xdraw.org/oscon2008.html&quot;&gt;http://xdraw.org/oscon2008.html&lt;/a&gt; in the next few days.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/the_416</guid>
    <title>The 416</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/the_416</link>
        <pubDate>Fri, 18 Jul 2008 14:10:37 -0600</pubDate>
    <category>The Web</category>
    <category>pradiptarolodex</category>
    <category>pradipta</category>
    <category>the416</category>
    <category>rails</category>
            <description>&lt;div style=&quot;text-align: center; margin-top: -10px&quot;&gt;&lt;a href=&quot;http://thepradipta416.com&quot; title=&quot;Proud Member of the Pradipta 416&quot;&gt;&lt;img src=&quot;//thepradipta416.com/img/badge1.gif&quot; alt=&quot;The Few, The Proud, The Pradipta 416&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/re_are_people_blogging_less</guid>
    <title>RE: Are people blogging less?</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/re_are_people_blogging_less</link>
        <pubDate>Mon, 23 Jun 2008 20:54:13 -0600</pubDate>
    <category>The Web</category>
    <category>twitter</category>
    <category>summer</category>
    <category>blogging</category>
            <description>&lt;a href=&quot;http://farm3.static.flickr.com/2068/2500489689_3d02e76562_o.jpg&quot; title=&quot;Nice Trail&quot; rel=&quot;lightbox[summer]&quot;&gt;&lt;img src=&quot;//farm3.static.flickr.com/2068/2500489689_7eded80450_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Nice Trail&quot; class=&quot;picture&quot; /&gt;&lt;/a&gt;

James Strachan asks &lt;a href=&quot;http://macstrac.blogspot.com/2008/06/are-people-blogging-less.html&quot;&gt;Are people blogging less?&lt;/a&gt; Looking at my &lt;a href=&quot;http://raibledesigns.com/rd/page/archives&quot;&gt;archives&lt;/a&gt;, I don&apos;t see a noticeable decline in the number of entries I&apos;m writing. Granted, I don&apos;t blog nearly as much as I did in &lt;a href=&quot;http://raibledesigns.com/rd/page/archives?date=200212&quot;&gt;December 2002&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One interesting thing I&apos;ve noticed though, is I don&apos;t read blogs much anymore. I open NetNewsWire about once a week. However, I don&apos;t think it&apos;s because of the &lt;em&gt;Twitter effect&lt;/em&gt;. I think it&apos;s because I work in an office full of people now and I get my social interaction from them, rather than from blogs. I also think it&apos;s because I&apos;m more interested in what&apos;s going on with LinkedIn and social networking competitors. Most of that news I get from LinkedIn News on the homepage.&lt;/p&gt;
&lt;p&gt;If there really is a decline in blogging, it may be because of Twitter, but I think it&apos;s something bigger. I think it&apos;s folks realizing 1) it&apos;s summer and 2) you don&apos;t get a whole lot of satisfaction out of blogging - you get satisfaction in life from spending time with family and friends. So quit reading this blog and go read your kids a book or invite your friends to happy hour tomorrow. It&apos;s a beautiful time of year and it won&apos;t last forever. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;-)&quot; title=&quot;;-)&quot; /&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/share_on_linkedin</guid>
    <title>Share on LinkedIn</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/share_on_linkedin</link>
        <pubDate>Thu, 5 Jun 2008 22:13:04 -0600</pubDate>
    <category>The Web</category>
    <category>linkedin</category>
    <category>news</category>
            <description>This is a test to see if I can get the &lt;a href=&quot;http://blog.linkedin.com/blog/2008/06/share-news-with.html&quot;&gt;Share on LinkedIn&lt;/a&gt; widget working on this site. Click below to invoke.
&lt;/p&gt;
&lt;p style=&quot;padding: 10px&quot;&gt;
&lt;span style=&quot;border: 1px outset #94D4FF; background: #0099CC; padding: 5px 7px&quot; onmouseover=&quot;this.style.border=&apos;1px inset #94D4FF&apos;&quot; onmouseout=&quot;this.style.border=&apos;1px outset #94D4FF&apos;&quot;&gt;
&lt;a style=&quot;color: white&quot; href=&quot;javascript:function%20liSub(_LIN_sU){_LIN_t=document.title;_LIN_sT=%27%27;try{_LIN_sT=((window.getSelection%20&amp;amp;&amp;amp;%20window.getSelection())%20||%20(document.getSelection%20&amp;amp;&amp;amp;%20document.getSelection())%20||%20(document.selection%20&amp;amp;&amp;amp;%20document.selection.createRange%20&amp;amp;&amp;amp;%20document.selection.createRange().text));}catch(e){_LIN_sT=%27%27;};_LIN_sU+=%27&amp;amp;summary=%27+encodeURIComponent(_LIN_sT)+%27&amp;amp;title=%27+encodeURIComponent(_LIN_t)+%27&amp;amp;url=%27+encodeURIComponent(location.href);_LIN_w=window.open(_LIN_sU,%27News%27,%27width=520,height=570,toolbar=0,location=0,status=0,scrollbars=yes%27);};void(liSub(%27http://www.linkedin.com/shareArticle?mini=true%27));&quot;&gt;Share on LinkedIn&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;Seems to work pretty well. I like how you can select text and it&apos;ll automatically populate the summary. You can drag the link above to your toolbar in Safari and Firefox if you want to use it like a &lt;a href=&quot;http://tantek.com/favelets/&quot;&gt;favelet&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Now I just need to get one of the &lt;a href=&quot;http://tinyurl.com/4sl2jq&quot;&gt;designers&lt;/a&gt; to create a nifty little &quot;Share on LinkedIn&quot; icon so I can add it to all my entries by default.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/why_no_more_that_500</guid>
    <title>Why no more than 500 connections?</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/why_no_more_that_500</link>
        <pubDate>Sun, 1 Jun 2008 18:40:59 -0600</pubDate>
    <category>The Web</category>
    <category>linkedin</category>
            <description>I recently updated my status on &lt;a href=&quot;http://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt; to read:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Matt is determined not to have 500+ connections. Will start removing connections soon.
&lt;/p&gt;
&lt;p&gt;A couple of days later, I received the following message from a connection:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
I noticed the other day you mentioned that you are determined to not have over 500 LinkedIn connections. I&apos;m wondering what the reason is? Not just because LinkedIn shows 500+ after that, is it? As you work for LinkedIn, I assume there&apos;s some other reason. I&apos;m interested to know what it is...
&lt;/p&gt;
&lt;p&gt;I joined LinkedIn May 27, 2003, 22 days after it initially launched. For the first few years, I accepted invitations when I received them. Some folks I knew, some I didn&apos;t. When I started consulting for LinkedIn last summer, I had somewhere between 200 and 300 connections. Most of them were people who had contacted me, not folks I had contacted. 
&lt;/p&gt;
&lt;p&gt;
One day, I used the &lt;a href=&quot;https://www.linkedin.com/secure/uploadContacts?displayWebMail=&quot;&gt;import webmail contacts&lt;/a&gt; feature to pull in my contacts from Gmail. My number of connections quickly jumped by 100 and it&apos;s increased quite a bit since then (mostly due to co-workers from LinkedIn). Of the almost 500 connections I have, I believe there&apos;s a good 100-200 of them that are folks I don&apos;t know, have never had contact with, and will likely never benefit from being &quot;connected&quot; with. 
&lt;/p&gt;
&lt;p&gt;
I guess the main reason I&apos;m planning on trimming my connections is to make my network higher quality. I admit I&apos;m somewhat motivated by the 500+ icon, but it&apos;s also a genuine feeling that there&apos;s quite a few folks I won&apos;t benefit from being connected to. I&apos;m not a &lt;a href=&quot;http://www.themetanetwork.com/&quot;&gt;LION&lt;/a&gt; after all. I believe my LinkedIn network should resemble my real-world network.&lt;/p&gt;
&lt;p&gt;What&apos;s your opinion? Should I have folks in my network that know me, but I don&apos;t know them?</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/linkedin_groups</guid>
    <title>LinkedIn Groups</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/linkedin_groups</link>
        <pubDate>Tue, 20 May 2008 14:16:18 -0600</pubDate>
    <category>The Web</category>
    <category>linkedin</category>
            <description>&lt;a href=&quot;http://www.linkedin.com&quot;&gt;&lt;img src=&quot;//static.raibledesigns.com/repository/images/linkedin-logo.gif&quot; alt=&quot;LinkedIn&quot; width=&quot;129&quot; height=&quot;36&quot; class=&quot;picture&quot; style=&quot;border: 0&quot; /&gt;&lt;/a&gt;
This afternoon, I noticed there&apos;s a &lt;a href=&quot;http://blogs.sun.com/theaquarium/entry/linkedin_glassfish_group_now_available&quot;&gt;LinkedIn &quot;GlassFish&quot; group now available&lt;/a&gt; and it reminded me of a couple things:
&lt;/p&gt;
&lt;ul class=&quot;glassList&quot;&gt;
&lt;li&gt;LinkedIn currently doesn&apos;t have a way to search for groups, but Jason Bailes has setup a &lt;a href=&quot;http://www.google.com/coop/cse?cx=012022021532202637257%3An0e8vkkccdq&quot;&gt;LinkedIn Groups Search&lt;/a&gt; with Google Custom Search. &lt;em&gt;Thanks Jason!&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;I created a &lt;a href=&quot;http://www.linkedin.com/groupInvitation?groupID=35000&amp;amp;sharedKey=59AF23639168&quot;&gt;Apache Software Foundation&lt;/a&gt; group on LinkedIn a few months ago. If you&apos;re a committer or member, you&apos;re more than welcome to join the group.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;LinkedIn Groups don&apos;t provide a whole lot of functionality at this point, but I&apos;ve heard there&apos;s big things in store for them. Chances are they&apos;ll be very valuable in the future.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/the_thin_server_architecture_working</guid>
    <title>The Thin Server Architecture Working Group</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/the_thin_server_architecture_working</link>
        <pubDate>Wed, 19 Mar 2008 09:23:56 -0600</pubDate>
    <category>The Web</category>
    <category>sofea</category>
    <category>soui</category>
    <category>webframeworks</category>
            <description>From &lt;a href=&quot;http://wisdomofganesh.blogspot.com/2008/03/new-home-for-sofea-thin-server.html&quot;&gt;The Wisdom of Ganesh&lt;/a&gt;:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
&lt;a href=&quot;http://unclescript.blogspot.com/&quot;&gt;Peter Svensson&lt;/a&gt; has set up a &lt;a href=&quot;http://www.thinserverarchitecture.com/home&quot;&gt;website&lt;/a&gt; where like-minded people can discuss the brave new world of applications whose common characteristic is that no aspect of presentation logic resides on the server side. I admit that&apos;s an overly broad-brush generalisation, and it will be necessary to read what the various authors of this camp have to say.
&lt;/p&gt;
&lt;p&gt;I thought about doing something similar when &lt;a href=&quot;http://raibledesigns.com/rd/entry/re_life_above_the_service&quot;&gt;I first read about SOFEA&lt;/a&gt;. I&apos;m glad to see that someone has taken on this challenge. However, doesn&apos;t it seem ironic that this site doesn&apos;t use SOFEA/SOUI for its own architecture? 
&lt;/p&gt;
&lt;p&gt;
IMO, if this site isn&apos;t written with some sort of SOFEA-based framework like it advocates, it&apos;s pretty much worthless.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/weborb_have_you_ever_heard</guid>
    <title>WebORB: Have you ever heard of it?</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/weborb_have_you_ever_heard</link>
        <pubDate>Wed, 13 Feb 2008 13:42:38 -0700</pubDate>
    <category>The Web</category>
    <category>ria</category>
    <atom:summary type="html">A colleague sent me an e-mail today and asked me if I&apos;d ever heard of &lt;a href=&quot;http://www.themidnightcoders.com/weborb/index.htm&quot;&gt;WebORB&lt;/a&gt; today. Since I hadn&apos;t, I figured I&apos;d write this post and see if any of you have heard of it? If so, what is it and what does it do? It it similar to &lt;a href=&quot;http://www.appcelerator.org/index.html&quot;&gt;Appcelerator&lt;/a&gt;, but server-side only? Or is it more like &lt;a href=&quot;http://www.infoq.com/news/2008/02/granite-data-services&quot;&gt;Granite DS&lt;/a&gt;?&lt;br/&gt;&lt;br/&gt;</atom:summary>        <description>A colleague sent me an e-mail today and asked me if I&apos;d ever heard of &lt;a href=&quot;http://www.themidnightcoders.com/weborb/index.htm&quot;&gt;WebORB&lt;/a&gt; today. Since I hadn&apos;t, I figured I&apos;d write this post and see if any of you have heard of it? If so, what is it and what does it do? It it similar to &lt;a href=&quot;http://www.appcelerator.org/index.html&quot;&gt;Appcelerator&lt;/a&gt;, but server-side only? Or is it more like &lt;a href=&quot;http://www.infoq.com/news/2008/02/granite-data-services&quot;&gt;Granite DS&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;One thing&apos;s for sure - their license for the Java version looks like you can&apos;t have any fun with it.&lt;/p&gt;
&lt;p class=&quot;smokey&quot;&gt;
1.      RIGHT TO USE / RESTRICTIONS ON USE:  The Software is provided in and is licensed for use in object code form only.  CUSTOMER may make copies of the Software for archival or backup purposes, but any and all copies must include LICENSOR&apos;s copyright notice, and are fully subject to the terms of this Agreement.  CUSTOMER may not reverse engineer, disassemble, decompile, translate or otherwise attempt to create the source code from the Software or create derivative works of the Software or any portion thereof, including for reasons of error correction or interoperability.  At CUSTOMER&apos;s request and at LICENSOR&apos;s election or as may be required by applicable law, LICENSOR will make commercially reasonable efforts to make available to CUSTOMER certain interface specifications so that CUSTOMER may develop software interfaces to provide interoperability with the Software. CUSTOMER is forbidden (i) from using Evaluation Software for Development or Production Use, CUSTOMER may not (ii) publish or provide any results of benchmark tests run on the Software to a third party without LICENSOR&apos;s prior written consent, (iii) disclose, distribute or otherwise make available the Software to any other party or permit others to use it, except employees and agents of CUSTOMER who use it on CUSTOMER&apos;s behalf, if CUSTOMER is an entity, or (iv) remove or alter any trademark, logo, copyright or other proprietary notices, legends, symbols or labels in the Software.  CUSTOMER may not rent, lease, sublicense, assign (except as provided herein), grant a security interest in or otherwise encumber, or otherwise transfer rights to the Software.  CUSTOMER may not bundle Software or a derivative of it as part of a software development environment such as, but not limited to, JBuilder, WebGain, IDEA or VisualAge.  Note that this restriction is targeted at IDE vendors, and does not prevent developers from loading Software into an IDE and using it for the evaluation purposes.  CUSTOMER may not integrate Software or a derivative of it into a software infrastructure platform or products such as, but not limited to: EJB Application Servers, Enterprise Application Integration products, Business to Business Integration products, Web Services Platforms, Web Services Management products, Process Control products, Business Process Automation products, Process Orchestration products, Distributed Computing Infrastructure products or platforms, Messaging Middleware products or Web Server products.  Note that this restriction is targeted at software infrastructure vendors, and does not prevent developers from using and bundling Software with higher level applications that run on these platforms.  CUSTOMER may not use Software or a derivative of it on a device which is not a standard PC or server.  Examples of such devices include, but are not limited to, cell phones, PDAs, vehicles, factory controllers, routers and printers.  The CUSTOMER agrees that CUSTOMER must contact LICENSOR in advance to determine whether any specific use is prohibited by this Agreement, if the right to use the Software in such manner is unclear, vague or ambiguous.    
&lt;/p&gt;
&lt;p&gt;BTW, what&apos;s up with the reference to WebGain and VisualAge? It sounds like this is a license from 5 years ago. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;-)&quot; title=&quot;;-)&quot; /&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/yui_grid_css_and_rails</guid>
    <title>YUI Grid CSS and Rails Performance</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/yui_grid_css_and_rails</link>
        <pubDate>Sat, 9 Feb 2008 08:14:18 -0700</pubDate>
    <category>The Web</category>
    <category>yui</category>
    <category>css</category>
    <category>rails</category>
    <category>performance</category>
    <category>cssframework</category>
    <category>ruby</category>
            <description>From &lt;a href=&quot;http://redmonk.com/sogrady/2008/02/07/links-for-2008-02-08/&quot;&gt;Stephen O&apos;Grady&lt;/a&gt;, I learned a couple interesting tidbits yesterday.&lt;/p&gt;
&lt;p&gt;The first is Jeremy Zawodny talking about Yahoo&apos;s new Grid Builder in 
&lt;a href=&quot;http://jeremy.zawodny.com/blog/archives/009883.html&quot;&gt;YUI Grid CSS and Grid Builder Kick Ass!&lt;/a&gt; The last time I looked at YUI Grid CSS (that&apos;s a mouthful) was almost &lt;a href=&quot;http://raibledesigns.com/rd/entry/yahoo_s_grids_css&quot;&gt;2 years ago&lt;/a&gt;, when it first came out. It&apos;s obvious that this library is better supported than Mike Stenhouse&apos;s &lt;a href=&quot;http://www.contentwithstyle.co.uk/Articles/17/a-css-framework&quot;&gt;CSS Framework&lt;/a&gt;. Maybe it&apos;s time to switch in AppFuse? Anyone know of themes available for Grid CSS?
&lt;/p&gt;
&lt;p&gt;The second item is Charlie Savage&apos;s entry titled
&lt;a href=&quot;http://cfis.savagexi.com/articles/2008/02/02/must-read-rails-performance-article&quot;&gt;Must Read Rails Performance Article&lt;/a&gt;:&lt;/li&gt;
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Using a patched version of ruby and &lt;a href=&quot;http://ruby-prof.rubyforge.org/&quot;&gt;ruby-prof&lt;/a&gt;, Alex was able to more than double performance  (with hints of more to come) and reduced memory consumption by 75%, or 750MB (yes - that is Megabytes). Alex does a wonderful job of documenting his approach with a series of blog posts &lt;a href=&quot;http://blog.pluron.com/2008/01/guerrillas-way.html&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://blog.pluron.com/2008/01/ruby-on-rails-i.html&quot;&gt;here&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;This reminds me of &lt;a href=&quot;http://java.dzone.com/news/don-brown-fixing-maven-2&quot;&gt;Don Brown&apos;s recent work on Maven&lt;/a&gt;. This is how open source is supposed to work - instead of complaining about the problems, fix them. In both Rails&apos; and Maven 2&apos;s cases - it&apos;s somewhat surprising these issues weren&apos;t fixed earlier. Kudos to Alex Dymo and Don Brown for stepping up to the plate. Well done gents.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/shadowbox_a_slick_lightbox_that</guid>
    <title>Shadowbox - a slick Lightbox that supports Flash</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/shadowbox_a_slick_lightbox_that</link>
        <pubDate>Fri, 25 Jan 2008 21:05:16 -0700</pubDate>
    <category>The Web</category>
    <category>javascript</category>
    <category>lightbox</category>
            <description>&lt;p&gt;Via &lt;a href=&quot;http://ajaxian.com/archives/library-agnostic-lightbox&quot;&gt;Ajaxian&lt;/a&gt;, I learned about &lt;a href=&quot;http://mjijackson.com/shadowbox/&quot;&gt;Shadowbox.js&lt;/a&gt;. From its creator, &lt;a href=&quot;http://mjijackson.com/2008/01/22/shadowbox-js-media-viewer-1-0-beta/&quot;&gt;Michael Jackson&lt;/a&gt;:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
A few weeks ago, I was looking for a &lt;a href=&quot;http://www.huddletogether.com/projects/lightbox2/&quot;&gt;Lightbox&lt;/a&gt;-like script that would allow me to display more than just pictures. In addition to static images, my client required the ability to display various types of movies including &lt;a href=&quot;http://www.apple.com/quicktime/&quot;&gt;QuickTime&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/SWF&quot;&gt;SWF&lt;/a&gt;. The only script that fit the bill was &lt;a href=&quot;http://www.stickmanlabs.com/lightwindow/&quot;&gt;Lightwindow&lt;/a&gt;?a nice piece of work to be sure?but it required the &lt;a href=&quot;http://www.prototypejs.org&quot;&gt;Prototype&lt;/a&gt; + &lt;a href=&quot;http://script.aculo.us&quot;&gt;Scriptaculous&lt;/a&gt; combo and I was already using &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
Besides, I thought, it would be really great to have a full-featured media viewing application that was &lt;a href=&quot;http://snook.ca/archives/javascript/be_library_agnostic/&quot;&gt;library agnostic&lt;/a&gt;. Then, if I need to use a different framework for some particular reason, I can easily switch.&lt;br/&gt;&lt;br/&gt;
Thus was born &lt;a href=&quot;/shadowbox/&quot;&gt;Shadowbox&lt;/a&gt;?a cross-browser, cross-platform, cleanly-coded and fully-documented media viewer application built entirely in JavaScript. 
&lt;/p&gt;
&lt;p&gt;I&apos;ve been using Lightbox JS on this site for almost 2 years. The next time I need lightbox functionality for an application, I&apos;ll definitely try out Shadowbox. I dig the look and feel. I agree with Ajaxian commentors that rel=&quot;lightbox[name]&quot; would be awesome. If it&apos;s added, I could theoretically replace lightbox.js with shadowbox.js and I wouldn&apos;t have to make any other changes.
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://mjijackson.com/shadowbox/#demos&quot;&gt;View Shadowbox Demos &amp;raquo;&lt;/a&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/google_calendar_sync_for_blackberry</guid>
    <title>Google Calendar Sync for BlackBerry</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/google_calendar_sync_for_blackberry</link>
        <pubDate>Wed, 12 Dec 2007 13:41:49 -0700</pubDate>
    <category>The Web</category>
    <category>linkedin</category>
    <category>blackberry</category>
    <category>google</category>
            <description>&lt;a href=&quot;http://www.google.com/mobile/sync/index.html&quot;&gt;&lt;img src=&quot;//www.google.com/mobile/images/sync/overview_sync.gif&quot; alt=&quot;Google Sync&quot; width=&quot;250&quot; height=&quot;243&quot; class=&quot;picture&quot; style=&quot;border: 0&quot; /&gt;&lt;/a&gt;
A couple of days ago, I said the &lt;a href=&quot;http://raibledesigns.com/rd/entry/linkedin_s_new_homepage&quot;&gt;Network Updates feature on LinkedIn&apos;s New Homepage is kinda boring&lt;/a&gt;. I still agree with this, but I think the new &lt;strong&gt;LinkedIn News&lt;/strong&gt; provides some real value. Today my homepage had a link to &lt;a href=&quot;http://googlesystem.blogspot.com/2007/12/google-calendar-sync-for-blackberry.html&quot;&gt;Google Calendar Sync for BlackBerry&lt;/a&gt;. This is something I&apos;ve been looking for for quite some time.&lt;/p&gt;
&lt;p&gt;If you have a BlackBerry, you can install it from &lt;a href=&quot;http://m.google.com/sync&quot;&gt;http://m.google.com/sync&lt;/a&gt;. I use &lt;a href=&quot;http://spanningsync.com/&quot;&gt;Spanning Sync&lt;/a&gt; to allow me to synch my Google Calendar in iCal and it works awesome. Having a sync to my BlackBerry was the missing part that I really wanted - and now I have it. &lt;em&gt;Thanks LinkedIn!&lt;/em&gt; (and Google of course)
&lt;/p&gt;
&lt;p&gt;As far as LinkedIn&apos;s &lt;a href=&quot;http://www.linkedin.com/?beta&quot;&gt;New Homepage&lt;/a&gt;, I think the biggest improvement would be to add Atom/RSS Feeds so I could get all my homepage updates (news, network updates and widget updates) in NetNewsWire. I asked about this last week and they said this should be coming in Q1 2008.</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/linkedin_s_new_homepage</guid>
    <title>LinkedIn&apos;s New Homepage</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/linkedin_s_new_homepage</link>
        <pubDate>Mon, 10 Dec 2007 11:16:02 -0700</pubDate>
    <category>The Web</category>
    <category>linkedin</category>
    <category>beta</category>
            <description>Dion has a post about &lt;a href=&quot;http://almaer.com/blog/linkedin-beta-small-steps&quot;&gt;LinkedIn&apos;s New Homepage&lt;/a&gt;. In addition to Dion&apos;s post, this seems to be a popular topic on &lt;a href=&quot;http://www.techmeme.com/071210/h1420&quot;&gt;Techmeme&lt;/a&gt;. I&apos;m proud to say I played a small part in this project and enjoyed working with the fabulous &quot;Homepage Team&quot; that put this together. We celebrated the launch last week while I was out in Mountain View.
&lt;/p&gt;

&lt;p&gt;To learn more about LinkedIn&apos;s New Homepage and the News feature, see the &lt;a href=&quot;http://blog.linkedin.com/blog/2007/12/announcing-link.html&quot;&gt;LinkedIn Blog&lt;/a&gt;.

&lt;p&gt;Back to Dion&apos;s post. He says:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
The network connections portion shows me what is wrong with LinkedIn. On Facebook I can see interesting things that my friends have done. On LinkedIn, I see that a connection has added another 6 connections. Who cares?
&lt;/p&gt;
&lt;p&gt;I agree that Network Updates are kinda boring on LinkedIn. However, I don&apos;t find my Facebook News Feed very interesting either. Is your Facebook News Feed interesting? If so, why?&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;img src=&quot;//static.raibledesigns.com/repository/images/facebook-newsfeed.png&quot; width=&quot;442&quot; height=&quot;356&quot; alt=&quot;Facebook News Feed&quot; style=&quot;border: 1px solid silver&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;
</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/how_gmail_will_make_money</guid>
    <title>How Gmail will make money</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/how_gmail_will_make_money</link>
        <pubDate>Tue, 4 Dec 2007 22:40:08 -0700</pubDate>
    <category>The Web</category>
    <category>gmail</category>
            <description>I found the following when cruising through some options in my Gmail account today. Seems like a decent strategy to me.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm3.static.flickr.com/2325/2088471940_d53ff1d30d_o.png&quot; title=&quot;Gmail: Purchase Additional Storage&quot; rel=&quot;lightbox&quot;&gt;&lt;img src=&quot;//farm3.static.flickr.com/2325/2088471940_f75e2652ea.jpg&quot; width=&quot;500&quot; height=&quot;346&quot; alt=&quot;Gmail: Purchase Additional Storage&quot; style=&quot;border: 1px solid #eee&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;</description>          </item>
    <item>
    <guid isPermaLink="true">https://raibledesigns.com/rd/entry/account_maintenance</guid>
    <title>Account Maintenance</title>
    <dc:creator>Matt Raible</dc:creator>
    <link>https://raibledesigns.com/rd/entry/account_maintenance</link>
        <pubDate>Sun, 18 Nov 2007 14:48:36 -0700</pubDate>
    <category>The Web</category>
    <category>gmail</category>
            <description>Gmail has been giving me the following error page since Friday morning. On one hand it&apos;s frustrating that I can&apos;t check my e-mail. On the other, it&apos;s kinda nice having a couple of days off. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://static.raibledesigns.com/repository/images/accountmaintenance.png&quot; rel=&quot;lightbox&quot;&gt;&lt;img src=&quot;//farm3.static.flickr.com/2355/2044152747_1c38b2a6d3.jpg&quot; alt=&quot;Gmail Error&quot; width=&quot;500&quot; height=&quot;307&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;My parents are in town and we&apos;ve been cleaning gutters and raking leaves all day. It&apos;s a beautiful 69&amp;deg;F to make it all the more enjoyable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; It&apos;s been almost 4 days and still no resolution. My account is still out of order and most e-mails to me are likely bouncing. My advice: don&apos;t depend on Gmail for your main mail account. Luckily I haven&apos;t, so matt@raibledesigns.com should still work - mraible@gmail.com seems to be gone indefinitely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 2:&lt;/strong&gt; I finally got access again on Tuesday afternoon - 4.5 days later. With 268 unread messages in my inbox, I think I&apos;d like it turned off again. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;-)&quot; title=&quot;;-)&quot; /&gt;</description>          </item>
  </channel>
</rss>