angular2-workshop
angular2-workshop copied to clipboard
Egghead.io - Angular 2 Workshop
{% raw %}
Angular 2 StarWars Party
Workshop Resources
Completed Exercises
The numbers folders 01-10 can be renamed at any time to catch up.
For example, rename 07 to src then restart your npm start to catch up to Exercise 7.
Exercise 00 - Setup
- Clone or fork this repository
- Make sure you have node.js installed
- Run
npm install -g webpack webpack-dev-server typings typescriptto install global dependencies - Navigate to your project folder from the command line
- Run
npm installto install dependencies - Run
npm startto fire up dev server - Open browser to
http://localhost:3000
Set up the StarWars server
- Install and run https://github.com/johnlindquist/swapi-json-server
- Open browser to
http://localhost:4000
Exercise 01 - Hello World
- Notice the
<app>tag inindex.html - Delete everything in
app.ts - Write a
@Componentwith a selector of'app' - Create a basic
template - Remember to
exportyourclass
Exercise 02 - Create a Component
- Create a
componentsdirectory - Add a file called
home.ts - Create a
@Componentandexportit - Import your component into
app.ts - Include the component with
directives:[Home] - Add the component to your
templatewith<home>
Exercise 03 - Handling Clicks and Refs
Logging an event
- Create a button in your
Homecomponent - Handle the click with a
(click)="onClick($event)" - Log out the event
Logging an Input
- Create an
input - Reference the
inputwith a#i - Pass the value with
#i.valueof the input to theonClick - Log out the value
Exercise 04 - Smart and Dumb Components
Inputs
- Add a
people = [{name:"John"}];to yourHomecomponent - Create a
PersonListcomponent - Add the
PersonListto yourHomecomponent - Create an
@Inputcalledpeople(remember to import it) - Push
peoplefromHomeintoPersonListwith[people]=people - Render the first person from the
@Inputwith{{people[0].name}}
Outputs
- Move the
inputand thebuttonto thePersonList - Create an
@Output()calledselect - Call
select.emit(value)in the button'sonClickhandler - Handle the
selectevent inHomewith(select)=onSelect($event) - Log out the input
Exercise 05 - Templates, Styles, and Built-in Directives
NgModel
- Add an
[(ngModel)]="name"to theinput - Add a
<span>{{name}}</span> - Type in the
inputto see the name appear in the<span>
<style>
- Add a
<style></style>tag to the template - Add a
.person { cursor: pointer; cursor: hand; }style - Add the
class=personto your span - Roll over your
<span>to see the hand cursor
*ngIf
- Add a
<i class="fa fa-star"></i> - Add an
*ngIf="name"to the<i> - Type in the input to see the
<i>appear
[ngClass]
- Add
(mouseover)="isOver = true" (mouseout)="isOver = false"tospan - Change the
classattribute toclass="fa"on the<i>element - Add
[ngClass]="{'fa-star':isOver, 'fa-star-o':!isOver}"to the<i> - Roll over the span to see the icon toggle
Exercise 06 - Repeating Elements with *ngFor
*ngFor
- Add more
peopleto theHome
people = [
{
name:"Luke Skywalker",
image: "http://localhost:4000/luke_skywalker.jpg"
},
{
name:"Darth Vader",
image: "http://localhost:4000/darth_vader.jpg"
},
{
name:"Leia Organa",
image: "http://localhost:4000/leia_organa.jpg"
}
];
Loop through each person using *ngFor on a simple element
- Create a
pelement inPersonListthat binds toperson.name - Add
*ngFor="let person of people"to theptag
Loop through each person using *ngFor on a custom component
- Create a
Cardcomponent using the code below as a reference - Give the
Cardan@Input()ofperson - Add the
Cardto thePersonList - Add
*ngFor="let person of people"to thecard - Update the
srcto[src]="person.image" - Show the
person.namein theh5with{{person.name}}
import {Component, Input} from '@angular/core';
@Component({
selector: 'card',
template: `<style>
.card{
display: flex;
flex-direction: column;
align-items: center;
}
.info{
display: flex;
flex-basis: 100px;
justify-content: center;
align-items: center;
flex-direction: column;
}
</style>
<div class="card">
<img [src]="person.image">
<div class="info">
<h5>{{person.name}}</h5>
<a class="btn btn-primary"><i class="fa fa-plus"></i> Add to Party</a>
</div>
</div>
`
})
export class Card{
@Input() person;
}
Exercise 07 - Move the Data to a Service
- Create
servicesdirectory - Create a
StarWars.tsfile - Use the
@Inject()decorator on aStarWarsclass - Move the
peoplefrom theHometo the service - Include the service in the
Homeproviders:[] - Inject the service
constructor(public starWars:StarWars){} - Use the service in the template
[people]="starWars.people"
Exercise 08 - Loading Data with Http
- Add
providers: [HTTP_PROVIDERS],to yourapp.ts - Import
import {Http} from '@angular/http';in your service - Inject
Httpin your serviceconstructor(private _http:Http){} - Delete the
peoplearray - In the constructor, assign the people to an
httprequest - Use
http.get()thenmapthen response usingres => res.json() mapthe images:luke_skywalker.jpgtohttp://locahost:4000/luke_skywalker.jpg
import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';
const API = 'http://localhost:4000';
@Injectable()
export class StarWars{
people;
constructor(private _http:Http){
this.people = _http.get(`${API}/people`)
.map(res => res.json() //get the response as json
.map(person =>
Object.assign(person, {image: `${API}/${person.image}`})
)
)
}
}
Use an | async pipe to load the data in the template
[people]="starWars.people | async"
Exercise 09 - Searching Data with a Pipe
Housekeeping
- Clean up
PersonListtemplate so onlyinputandcards remain
<input [(ngModel)]="name" type="text">
<div class="card-container">
<card
*ngFor="let person of people"
[person]="person">
</card>
</div>
Creating a Pipe
- Create a
pipesdirectory - Create a
search.tsfile - Create a
@Pipe()calledSearch - Create your own searching logic
import {Pipe} from '@angular/core';
@Pipe({
name: 'search'
})
export class Search{
transform(data, key, term = ""){
if(!data) return null;
return data.filter(item => {
return item[key]
.toLowerCase()
.includes(term.toLowerCase());
})
}
}
Using the Pipe
Add the Search to your PersonList
pipes:[Search],
- Add the
name("search") of theSearchpipe to the template - Use
'name'as thekeyto search on - Use the
namefrom the[(ngModel)]="name"as the term
let person of people | search:'name':name"
Exercise 10 - Adding Routes
- Add a
<base href="/">to yourindex.html<head> - Create a simple
Partycomponent with ahello worldtemplate - Import all the required Router classes into
app.ts
import {ROUTER_PROVIDERS, RouteConfig, RouterOutlet, RouterLink} from '@angular/router';
- Include
RouterOutletandRouterLinkin yourdirectives:[]
directives: [RouterOutlet, RouterLink],
- Replace
<home>the<router-outlet> - Decorate your
Appwith your routes
@RouteConfig([
{path: '/home', name: 'Home', component: Home, useAsDefault: true},
{path: '/party', name: 'Party', component: Party},
{path: '/**', redirectTo: ['Home'] }
])
- Create a nav with
[routerLink]
<ul class="nav nav-tabs">
<li class="nav-item">
<a [routerLink]="['Home']" class="nav-link">Home</a>
</li>
<li class="nav-item">
<a [routerLink]="['Party']" class="nav-link">Party</a>
</li>
</ul>
- Stylize the active route with a
.router-link-activestyle:
.router-link-active{
color: #55595c;
background-color: #fff;
border-color: #ddd #ddd transparent;
}
Challenges (Solutions Not Included!)
The following are for people who enjoy a challenge and working ahead.
Challenge
Create a second service with a route to manage who you "Add" to the Party.
Moderate Challenge
Add create, read, update, delete to the "Party" service so you can add, remove, edit people to the party. Also make sure you can't add the same Person to the party twice.
Difficult Challenge
Create a "PersonDetail" component and route so that when you click on the Person image, it navigates to a route displaying Person's detail including all the images of starships they've flown :wink:
Difficult Challenge
Build an "Autocomplete" search box from the Star Wars api. Prior RxJS knowledge is recommended.
Super Duper Tough Challenge
Build a full Create, Read, Update, Delete app (the api supports it!) with the people from Star Wars including all of the features above! {% endraw %}