{"id":16257374,"url":"https://github.com/johnlindquist/angular2-workshop","last_synced_at":"2026-02-20T17:30:54.883Z","repository":{"id":136548834,"uuid":"57468206","full_name":"johnlindquist/angular2-workshop","owner":"johnlindquist","description":"Egghead.io - Angular 2 Workshop","archived":false,"fork":false,"pushed_at":"2016-07-11T15:37:48.000Z","size":60,"stargazers_count":17,"open_issues_count":4,"forks_count":13,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-28T03:14:58.487Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnlindquist.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-05-01T00:53:42.000Z","updated_at":"2023-08-12T06:16:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"ec1cabb3-0947-4dbd-9576-1317ab2ad877","html_url":"https://github.com/johnlindquist/angular2-workshop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/johnlindquist/angular2-workshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fangular2-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fangular2-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fangular2-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fangular2-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnlindquist","download_url":"https://codeload.github.com/johnlindquist/angular2-workshop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnlindquist%2Fangular2-workshop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29658364,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T16:33:43.953Z","status":"ssl_error","status_checked_at":"2026-02-20T16:33:43.598Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-10T15:48:03.424Z","updated_at":"2026-02-20T17:30:54.849Z","avatar_url":"https://github.com/johnlindquist.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"{% raw %}\n\n## Angular 2 StarWars Party\n\n### Workshop Resources\n- [Angular 2 Cheat Sheet](https://angular.io/docs/ts/latest/cheatsheet.html)\n- [Bootstrap 4 Cheat Sheet](http://hackerthemes.com/bootstrap-cheatsheet/)\n- [Font Awesome Cheat Sheet](https://fortawesome.github.io/Font-Awesome/icons/)\n\n### Completed Exercises\nThe numbers folders 01-10 can be renamed at any time to catch up.\nFor example, rename `07` to `src` then restart your `npm start` to catch up to Exercise 7.\n\n### Exercise 00 - Setup\n- Clone or fork this repository\n- Make sure you have [node.js](https://nodejs.org/) installed\n- Run `npm install -g webpack webpack-dev-server typings typescript` to install global dependencies\n- Navigate to your project folder from the command line\n- Run `npm install` to install dependencies\n- Run `npm start` to fire up dev server\n- Open browser to [`http://localhost:3000`](http://localhost:3000)\n\n#### Set up the StarWars server\n- Install and run [https://github.com/johnlindquist/swapi-json-server](https://github.com/johnlindquist/swapi-json-server)\n- Open browser to [`http://localhost:4000`](http://localhost:4000)\n\n### Exercise 01 - Hello World\n- Notice the `\u003capp\u003e` tag in `index.html`\n- Delete everything in `app.ts`\n- Write a `@Component` with a selector of `'app'`\n- Create a basic `template`\n- Remember to `export` your `class`\n\n### Exercise 02 - Create a Component\n- Create a `components` directory\n- Add a file called `home.ts`\n- Create a `@Component` and `export` it\n- Import your component into `app.ts`\n- Include the component with `directives:[Home]`\n- Add the component to your `template` with `\u003chome\u003e`\n\n### Exercise 03 - Handling Clicks and Refs\n#### Logging an event\n- Create a button in your `Home` component\n- Handle the click with a `(click)=\"onClick($event)\"`\n- Log out the event\n\n#### Logging an Input\n- Create an `input`\n- Reference the `input` with a `#i`\n- Pass the value with `#i.value` of the input to the `onClick`\n- Log out the value\n\n### Exercise 04 - Smart and Dumb Components\n#### Inputs\n- Add a `people = [{name:\"John\"}];` to your `Home` component\n- Create a `PersonList` component\n- Add the `PersonList` to your `Home` component\n- Create an `@Input` called `people` (remember to import it)\n- Push `people` from `Home` into `PersonList` with `[people]=people`\n- Render the first person from the `@Input` with `{{people[0].name}}`\n\n#### Outputs\n- Move the `input` and the `button` to the `PersonList`\n- Create an `@Output()` called `select`\n- Call `select.emit(value)` in the button's `onClick` handler\n- Handle the `select` event in`Home` with `(select)=onSelect($event)`\n- Log out the input\n\n### Exercise 05 - Templates, Styles, and Built-in Directives\n#### `NgModel`\n- Add an `[(ngModel)]=\"name\"` to the `input`\n- Add a `\u003cspan\u003e{{name}}\u003c/span\u003e`\n- Type in the `input` to see the name appear in the `\u003cspan\u003e`\n\n#### `\u003cstyle\u003e`\n- Add a `\u003cstyle\u003e\u003c/style\u003e` tag to the template\n- Add a `.person { cursor: pointer; cursor: hand; }` style\n- Add the `class=person` to your span\n- Roll over your `\u003cspan\u003e` to see the hand cursor\n\n#### `*ngIf`\n- Add a `\u003ci class=\"fa fa-star\"\u003e\u003c/i\u003e`\n- Add an `*ngIf=\"name\"` to the `\u003ci\u003e`\n- Type in the input to see the `\u003ci\u003e` appear\n\n#### `[ngClass]`\n- Add `(mouseover)=\"isOver = true\" (mouseout)=\"isOver = false\"` to `span`\n- Change the `class` attribute to `class=\"fa\"` on the `\u003ci\u003e` element\n- Add `[ngClass]=\"{'fa-star':isOver, 'fa-star-o':!isOver}\"` to the `\u003ci\u003e`\n- Roll over the span to see the icon toggle\n\n### Exercise 06 - Repeating Elements with *ngFor\n\n#### `*ngFor`\n- Add more `people` to the `Home`\n```js\n  people = [\n    {\n      name:\"Luke Skywalker\",\n      image: \"http://localhost:4000/luke_skywalker.jpg\"\n    },\n    {\n      name:\"Darth Vader\",\n      image: \"http://localhost:4000/darth_vader.jpg\"\n    },\n    {\n      name:\"Leia Organa\",\n      image: \"http://localhost:4000/leia_organa.jpg\"\n    }\n  ];\n```\n#### Loop through each person using `*ngFor` on a simple element\n- Create a `p` element in `PersonList` that binds to `person.name`\n- Add `*ngFor=\"let person of people\"` to the `p` tag\n\n#### Loop through each person using `*ngFor` on a custom component\n- Create a `Card` component using the code below as a reference\n- Give the `Card` an `@Input()` of `person`\n- Add the `Card` to the `PersonList`\n- Add `*ngFor=\"let person of people\"` to the `card`\n- Update the `src` to `[src]=\"person.image\"`\n- Show the `person.name` in the `h5` with `{{person.name}}`\n\n```js\n\nimport {Component, Input} from '@angular/core';\n@Component({\n  selector: 'card',\n  template: `\u003cstyle\u003e\n  .card{\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n  }\n  \n  .info{\n    display: flex;\n    flex-basis: 100px;\n    justify-content: center;\n    align-items: center;\n    flex-direction: column;\n  }\n\u003c/style\u003e\n\u003cdiv class=\"card\"\u003e\n    \u003cimg [src]=\"person.image\"\u003e\n    \u003cdiv class=\"info\"\u003e\n      \u003ch5\u003e{{person.name}}\u003c/h5\u003e\n      \u003ca class=\"btn btn-primary\"\u003e\u003ci class=\"fa fa-plus\"\u003e\u003c/i\u003e Add to Party\u003c/a\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n`\n})\nexport class Card{\n  @Input() person;\n}\n\n```\n\n### Exercise 07 - Move the Data to a Service\n- Create `services` directory\n- Create a `StarWars.ts` file\n- Use the `@Inject()` decorator on a `StarWars` class\n- Move the `people` from the `Home` to the service\n- Include the service in the `Home` `providers:[]`\n- Inject the service `constructor(public starWars:StarWars){}`\n- Use the service in the template `[people]=\"starWars.people\"`\n\n\n### Exercise 08 - Loading Data with Http\n- Add `providers: [HTTP_PROVIDERS],` to your `app.ts`\n- Import `import {Http} from '@angular/http';` in your service\n- Inject `Http` in your service `constructor(private _http:Http){}`\n- Delete the `people` array\n- In the constructor, assign the people to an `http` request\n- Use `http.get()` then `map` then response using `res =\u003e res.json()`\n- `map` the images: `luke_skywalker.jpg` to `http://locahost:4000/luke_skywalker.jpg`\n\n```js\nimport {Injectable} from '@angular/core';\nimport {Http} from '@angular/http';\nimport 'rxjs/add/operator/map';\n\nconst API = 'http://localhost:4000';\n\n@Injectable()\nexport class StarWars{\n  people;\n  constructor(private _http:Http){\n    this.people = _http.get(`${API}/people`)\n      .map(res =\u003e res.json() //get the response as json\n        .map(person =\u003e \n          Object.assign(person, {image: `${API}/${person.image}`})\n        )\n      )\n  }\n}\n\n```\n\nUse an `| async` pipe to load the data in the template\n```js\n  [people]=\"starWars.people | async\"\n```\n\n### Exercise 09 - Searching Data with a Pipe\n#### Housekeeping\n- Clean up `PersonList` template so only `input` and `card`s remain\n\n```js\n\u003cinput [(ngModel)]=\"name\" type=\"text\"\u003e\n\n\u003cdiv class=\"card-container\"\u003e\n  \u003ccard\n    *ngFor=\"let person of people\"\n    [person]=\"person\"\u003e\n  \u003c/card\u003e\n\u003c/div\u003e\n```\n\n#### Creating a Pipe\n- Create a `pipes` directory\n- Create a `search.ts` file\n- Create a `@Pipe()` called `Search`\n- Create your own searching logic\n```js\nimport {Pipe} from '@angular/core';\n@Pipe({\n  name: 'search'\n})\nexport class Search{\n  transform(data, key, term = \"\"){\n    if(!data) return null;\n    return data.filter(item =\u003e {\n      return item[key]\n        .toLowerCase()\n        .includes(term.toLowerCase());\n    })\n  }\n}\n```\n\n#### Using the Pipe\nAdd the `Search` to your `PersonList`\n```js\npipes:[Search],\n```\n\n- Add the `name` (\"search\") of the `Search` pipe to the template\n- Use `'name'` as the `key` to search on\n- Use the `name` from the `[(ngModel)]=\"name\"` as the term\n```js\n  let person of people | search:'name':name\"\n```\n\n\n### Exercise 10 - Adding Routes\n- Add a `\u003cbase href=\"/\"\u003e` to your `index.html` `\u003chead\u003e`\n- Create a simple `Party` component with a `hello world` template\n- Import all the required Router classes into `app.ts`\n```js\nimport {ROUTER_PROVIDERS, RouteConfig, RouterOutlet, RouterLink} from '@angular/router';\n```\n- Include `RouterOutlet` and `RouterLink` in your `directives:[]`\n```js\n  directives: [RouterOutlet, RouterLink],\n```\n- Replace `\u003chome\u003e` the `\u003crouter-outlet\u003e`\n- Decorate your `App` with your routes\n```js\n@RouteConfig([\n  {path: '/home', name: 'Home', component: Home, useAsDefault: true},\n  {path: '/party', name: 'Party', component: Party},\n  {path: '/**', redirectTo: ['Home'] }\n])\n``` \n- Create a nav with `[routerLink]`\n```js\n\u003cul class=\"nav nav-tabs\"\u003e\n    \u003cli class=\"nav-item\"\u003e\n      \u003ca [routerLink]=\"['Home']\" class=\"nav-link\"\u003eHome\u003c/a\u003e\n    \u003c/li\u003e\n    \u003cli class=\"nav-item\"\u003e\n      \u003ca [routerLink]=\"['Party']\" class=\"nav-link\"\u003eParty\u003c/a\u003e\n    \u003c/li\u003e\n  \u003c/ul\u003e\n```\n- Stylize the active route with a `.router-link-active` style:\n```css\n.router-link-active{\n  color: #55595c;\n  background-color: #fff;\n  border-color: #ddd #ddd transparent;\n}\n```\n\n## Challenges (Solutions Not Included!)\nThe following are for people who enjoy a challenge and working ahead.\n\n### Challenge\nCreate a second service with a route to manage who you \"Add\" to the Party.\n\n### Moderate Challenge\nAdd create, read, update, delete to the \"Party\" service\nso you can add, remove, edit people to the party. Also make\nsure you can't add the same Person to the party twice.\n\n### Difficult Challenge\nCreate a \"PersonDetail\" component and route so that when you\nclick on the Person image, it navigates to a route displaying\nPerson's detail *including* all the images of starships they've\nflown :wink:\n\n### Difficult Challenge\nBuild an \"Autocomplete\" search box from the Star Wars api.\nPrior RxJS knowledge is recommended.\n\n### Super Duper Tough Challenge\nBuild a full Create, Read, Update, Delete app (the api supports it!)\nwith the people from Star Wars including all of the features above!\n{% endraw %}\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnlindquist%2Fangular2-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnlindquist%2Fangular2-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnlindquist%2Fangular2-workshop/lists"}