Just the "R" in CRUD...
As a lonely dog, I want to see a list of all of the dogs that are available so that I can see my dating options.
- Version History:
- Angular JS (or Angular 1.x)
- Angular 2 (complete re-write for components)
- Angular 4 (backwards compatible with Angular 2)
- TypeScript: static types, developed by Microsoft, superset of JS
- Building blocks: components, services, directives, events
- Framework: Bigger, more bloated than React or Vue
Create new project:
$ npm install @angular/cli -g
$ ng new APP_NAME && cd APP_NAME
@symbol? These are scoped modules. They are used for grouping similar packages.
Why use the CLI?
Zero-config boilerplate that works out of the box. You do not need to configure webpack or the Typescript compiler to build an app. It provides a nice structure along with linting and testing. It provides sane, helpful errors. Review the package.json as well as the .angular-cli.json for more info.
Sanity check:
$ ng serveThe CLI initializes a new Git repo!
As a lonely dog, I want to see a list of all of the dogs that are available so that I can see my dating options.
Services are used to encapsulate common functionality for reuse across components. Perfect for storing AJAX requests. Skinny components, fat services!
Create a new service:
$ ng g service services/dogTo use, this service, add it to the providers within app.module.ts:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { DogService } from './dog.service';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [DogService],
bootstrap: [AppComponent]
})
export class AppModule {}So, the AppModule is used to bootstrap the Angular app. The @NgModule decorator takes metadata that lets Angular know how to run the app. Everything that we create will be added to this object.
Update the service itself:
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
const baseURL = 'https://barkwire-api.herokuapp.com';
@Injectable()
export class DogService {
constructor(private http: Http) {
console.log('Service is wired up correctly!')
}
getAllDogs() {
return new Promise((resolve, reject) => {
this.http.get(`${baseURL}/dogs`)
.map(res => res.json())
.subscribe(res => {
resolve(res.dogs);
}, (err) => {
reject(err);
});
});
}
}Here, we defined a class called DogService, which takes the Http module, and then we created a method called getAllDogs, which makes an AJAX request to the Node app and returns an observeable sequence. The @Injectable() decorator turns this class into a dependency that we can inject into a component...
Create a new component:
$ ng g component components/dogThis sets up the component files and folders and even adds it to app.module.ts! Update the component itself:
import { Component, OnInit } from '@angular/core';
import { DogService } from '../../services/dog.service';
// component meta data
@Component({
selector: 'app-dog',
templateUrl: './dog.component.html',
styleUrls: ['./dog.component.css']
})
// component class
export class DogComponent implements OnInit {
dogs: any; // why "any"? lazy
// inject data service dependency
constructor(private dogService: DogService) { }
ngOnInit() {
this.getDogList();
}
getDogList() {
// resolve the promise
this.dogService.getAllDogs().then((res) => {
console.log(res)
this.dogs = res;
}, (err) => {
console.log(err);
});
}
}Update dog.component.html:
<ul>
<li *ngFor="let dog of dogs" class="dogs">
{{ dog.name }}
</li>
</ul>This uses the ngFor microsyntax, which is some nice sugar so we don't have to use an ng-template
To link a component to a specific route we need to configure the Angular router...
Update app.module.ts:
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HashLocationStrategy, LocationStrategy } from '@angular/common';
import { AppComponent } from './app.component';
import { DogService } from './services/dog.service';
import { DogComponent } from './components/dog/dog.component';
const appRoutes: Routes = [
{ path: '', redirectTo: 'dogs', pathMatch: 'full' },
{ path: 'dogs', component: DogComponent }
];
@NgModule({
declarations: [
AppComponent,
DogComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(appRoutes)
],
providers: [
DogService,
{provide: LocationStrategy, useClass: HashLocationStrategy}
],
bootstrap: [AppComponent]
})
export class AppModule { }Update app.component.html:
<router-outlet></router-outlet>Run ng serve. Fix any errors. Test it out in the browser.
Add the following styles to style.css:
@import url("http://meyerweb.com/eric/tools/css/reset/reset.css");
@import url("https://fonts.googleapis.com/css?family=Indie+Flower%7COpen+Sans");
body {
color: #333;
background-color: #eee;
font-family: "Open Sans";
}
input, label {
display: block;
width: 100%;
}
label {
font-style: italic;
margin-bottom: 0.25rem;
}
input {
margin-bottom: 1rem;
font-size: 2rem;
border: none;
}
a {
text-decoration: none;
color: #333;
}
header {
background-color: #4db36f;
padding: 1rem;
box-shadow: 0 1px 1px #999;
}
header h1 {
font-size: 2rem;
color: #fff;
font-family: "Indie Flower";
}
main {
padding: 3rem;
position: relative;
min-height: calc(100vh - 112px);
}
main h2 {
font-size: 2rem;
font-weight: 700;
margin-top: 0.5rem;
margin-bottom: 1rem;
}
footer {
padding: 1rem;
background-color: #333;
color: #eee;
}
.dogs {
display: flex;
flex-flow: row wrap;
}
.dog-listing {
max-width: 30rem;
margin: 1rem;
box-shadow: 2px 2px 2px #999;
transition: all 0.2s;
}
.dog-listing:hover {
transform: scale(1.03);
}
.dog-listing h3 {
font-weight: 700;
font-size: 1.2rem;
margin-bottom: 0.5rem;
background-color: #6f4db3;
padding: 1rem;
color: #eee;
}
.dog-listing figure {
margin-right: 1rem;
padding: 1rem;
}
.dog-listing figure img {
max-width: 160px;
}
.dog-listing figure figcaption {
font-style: italic;
}
.dog-listing section {
display: flex;
}
.dog-listing p {
padding: 1rem;
line-height: 1.5;
}Update index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>BarkWire</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<header>
<h1><a href="/">BarkWire</a></h1>
</header>
<main>
<app-root></app-root>
</main>
<footer>
<small>© Barkwire Inc.</small>
</footer>
</body>
</html>Update dog-component.html:
<ul class="dogs">
<li *ngFor="let dog of dogs" class="dogs">
<section class="dog-listing">
<h3 class="name">{{dog.name}}</h3>
<section>
<figure>
<img src="{{dog.imageUrl}}" alt="{{dog.name}}" />
<figcaption>{{dog.imageCaption}}</figcaption>
</figure>
<p>{{dog.description}}</p>
</section>
</section>
</li>
</ul>