Skip to content

Commit 7178d5b

Browse files
committed
End of lesson 12
1 parent 9b5d178 commit 7178d5b

10 files changed

+141
-7
lines changed

APM-Start/src/app/app.component.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ import { Component } from "@angular/core";
33
@Component({
44
selector: 'pm-root',
55
template: `
6-
<div>
7-
<h1>{{pageTitle}}</h1>
8-
<pm-products>My first component</pm-products>
6+
<nav class='navbar navbar-expand navbar-light bg-light'>
7+
<a class='bavbar-brand'>{{pageTitle}}</a>
8+
<ul class='nav nav-pills'>
9+
<li><a class='nav-link' routerLink='/welcome'>Home</a></li>
10+
<li><a class='nav-link' routerLink='/products'>Product List</a></li>
11+
</ul>
12+
</nav>
13+
<div class='container'>
14+
<router-outlet></router-outlet>
915
</div>
1016
`
1117
})

APM-Start/src/app/app.module.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,34 @@ import { AppComponent } from './app.component';
77
import { ProductListComponent } from './products/product-list.component';
88
import { StarComponent } from './shared/star.component';
99
import { ConvertToSpacesPipe } from './shared/convert-to-spaces.pipe';
10+
import { ProductDetailComponent } from './products/product-detail.component';
11+
import { WelcomeComponent } from './home/welcome.component';
12+
import { RouterModule } from '@angular/router';
13+
import { ProductDetailGuard } from './products/product-detail.guard';
1014

1115
@NgModule({
1216
declarations: [
1317
AppComponent,
1418
ProductListComponent,
1519
ConvertToSpacesPipe,
16-
StarComponent
20+
StarComponent,
21+
ProductDetailComponent,
22+
WelcomeComponent
1723
],
1824
imports: [
1925
BrowserModule,
2026
FormsModule,
21-
HttpClientModule
27+
HttpClientModule,
28+
RouterModule.forRoot([
29+
{ path: 'products', component: ProductListComponent},
30+
{
31+
path: 'products/:id',
32+
component: ProductDetailComponent,
33+
canActivate: [ProductDetailGuard]
34+
},
35+
{ path: 'welcome', component: WelcomeComponent },
36+
{ path: '**', redirectTo: 'welcome', pathMatch: 'full'} //
37+
])
2238
],
2339
bootstrap: [AppComponent]
2440
})

APM-Start/src/app/products/product-detail.component.css

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<div class='card'>
2+
<div class='card-header'>
3+
{{pageTitle + ': ' + product?.productName}}
4+
</div>
5+
<div class='card-footer'>
6+
<button class='btn btn-outlline-secondary'
7+
style='width:80px'
8+
(click)='onBack()'>
9+
<i class='fa fa-chevron-left'></i>Back
10+
</button>
11+
</div>
12+
</div>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { ProductDetailComponent } from './product-detail.component';
4+
5+
describe('ProductDetailComponent', () => {
6+
let component: ProductDetailComponent;
7+
let fixture: ComponentFixture<ProductDetailComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
declarations: [ ProductDetailComponent ]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(ProductDetailComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { ActivatedRoute, Router } from '@angular/router';
3+
import { IProduct } from './product';
4+
5+
@Component({
6+
selector: 'pm-product-detail',
7+
templateUrl: './product-detail.component.html',
8+
styleUrls: ['./product-detail.component.css']
9+
})
10+
export class ProductDetailComponent implements OnInit {
11+
product: IProduct | undefined;
12+
constructor(private route: ActivatedRoute, private router: Router) {}
13+
14+
pageTitle: string = 'Product Details';
15+
16+
ngOnInit(): void {
17+
const id = Number(this.route.snapshot.paramMap.get('id'));
18+
this.pageTitle += `: ${id}`;
19+
20+
// TODO remove hard coded product:
21+
this.product = {
22+
'productId': id,
23+
'productName': 'Blah',
24+
'productCode': '010202',
25+
'releaseDate': 'March 19, 2021',
26+
'description': 'Leaf Rake',
27+
'price': 19.95,
28+
'starRating': 3.2,
29+
'imageUrl': 'assets/images/leaf_rake.png'
30+
};
31+
}
32+
33+
onBack(): void {
34+
this.router.navigate(['/products']);
35+
}
36+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { ProductDetailGuard } from './product-detail.guard';
4+
5+
describe('ProductDetailGuard', () => {
6+
let guard: ProductDetailGuard;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
guard = TestBed.inject(ProductDetailGuard);
11+
});
12+
13+
it('should be created', () => {
14+
expect(guard).toBeTruthy();
15+
});
16+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Injectable } from '@angular/core';
2+
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
3+
import { Observable } from 'rxjs';
4+
5+
@Injectable({
6+
providedIn: 'root'
7+
})
8+
export class ProductDetailGuard implements CanActivate {
9+
constructor(private router: Router) {}
10+
11+
canActivate(
12+
route: ActivatedRouteSnapshot,
13+
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
14+
const id = Number(route.paramMap.get('id'));
15+
if(isNaN(id) || id < 1) {
16+
alert('Invalid product id');
17+
this.router.navigate(['/products']);
18+
return false;
19+
}
20+
return true;
21+
}
22+
}

APM-Start/src/app/products/product-list.component.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ <h4>Filtered by: </h4>
3131
</tr>
3232
<tr *ngFor="let product of filteredProducts">
3333
<td><img *ngIf='showImage' [src]='product.imageUrl' [title]='product.productName' [style.width.px]='imageWidth' [style.margin.px]='imageMargin'></td>
34-
<td>{{product.productName}}</td>
34+
<td>
35+
<a [routerLink]="['/products', product.productId]">
36+
{{product.productName}}
37+
</a>
38+
</td>
3539
<td>{{product.productCode | lowercase | convertToSpaces:'-'}}</td>
3640
<td>{{product.releaseDate}}</td>
3741
<td>{{product.price | currency: 'GBP' : 'symbol' : '1.2-2'}}</td>

APM-Start/src/app/products/product-list.component.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { IProduct } from "./product";
44
import { ProductService } from "./product.service";
55

66
@Component({
7-
selector: 'pm-products',
87
templateUrl: './product-list.component.html',
98
styleUrls: ['./product-list.component.css']
109
})

0 commit comments

Comments
 (0)