สารบัญเนื้อหา
- สร้าง Interfaces โดยใช้ Typescript
- สร้าง Angular Services
- ส่งข้อมูล Members ไปยัง Member List Component
- สร้าง Member Card เพื่อแสดงในรายการ Members
- เพิ่ม Effect Animation ใน Member Card
- ใช้ Auth0 JwtModule ในการส่ง Token เมื่อดึงข้อมูลโดยอัตโนมัติ
- สร้าง Detail View Component เพื่อแสดงรายละเอียดของ Members
- ออกแบบ Template เพื่อแสดงรายละเอียดของ Members
- ใช้งาน Tap ในการแสดงรายละเอียดของ Members
- ใช้งาน Route Resolvers เพื่อรับส่งข้อมูล
- เพิ่ม Photo Gallery
1. สร้าง Interfaces โดยใช้ Typescript
สร้าง Interfaces สำหรับโครงสร้างของ Users และ Photos โดยจัดเก็บภายใต้ _models ดังนี้
_models/photo.cs
export interface Photo {
id: number;
url: string;
description: string;
dateAdded: Date;
isMain: boolean;
}
_models/user.ts
import { Photo } from './photo';
export interface User {
id: number;
username: string;
knowAs: string;
age: number;
gender: string;
created: Date;
lastActive: Date;
photoUrl: string;
city: string;
country: string;
interests?: string;
introduction?: string;
lookingFor?: string;
photos?: Photo[];
}
2. สร้าง Angular Services
เพิ่ม Environment ใน environments/environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:5000/api/'
};
แล้วทำการปรับปรุง Url ของ API ใน auth.service.ts เป็น
baseUrl = environment.apiUrl + 'auth/';
สร้าง User Services ภายใต้ _services ชื่อ user.service.ts
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../_models/user';
const httpOptions = {
headers: new HttpHeaders({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
};
@Injectable({
providedIn: 'root'
})
export class UserService {
baseUrl = environment.apiUrl;
constructor(private http: HttpClient) { }
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.baseUrl + 'users', httpOptions);
}
getUser(id): Observable<User> {
return this.http.get<User>(this.baseUrl + 'users/' + id, httpOptions);
}
}
3. ส่งข้อมูล Members ไปยัง Member List Component
ปรับปรุงโค้ดใน member-list.component.ts เพื่อโหลดข้อมูลจาก API ผ่าน Service ที่ได้สร้างไว้ในหัวข้อก่อนหน้านี้
import { Component, OnInit } from '@angular/core';
import { User } from '../_models/user';
import { UserService } from '../_services/user.service';
import { AlertifyService } from '../_services/alertify.service';
@Component({
selector: 'app-member-list',
templateUrl: './member-list.component.html',
styleUrls: ['./member-list.component.css']
})
export class MemberListComponent implements OnInit {
users: User[];
constructor(private userService: UserService, private alertify: AlertifyService) { }
ngOnInit() {
this.loadUsers();
}
loadUsers() {
this.userService.getUsers().subscribe((users: User[]) => {
this.users = users;
}, error => {
this.alertify.error(error);
});
}
}
ปรัปปรุงโค้ดใน member-list.component.html เพื่อแสดงข้อมูล Users ที่ดึงได้จาก API
<div class="container">
<div class="row">
<div class="col-lg-2 col-md-3 col-sm-6">
<p *ngFor="let user of users">{{user.knownAs}}</p>
</div>
</div>
</div>
4. สร้าง Member Card เพื่อแสดงในรายการ Members
เนื่องจากเราต้องมีสิ่งที่เกี่ยวกับ member หลาย ๆ อย่าง แต่ตอนนี้ components ที่สร้างไว้มี member-list component อยู่ เราจึงควรที่จะสร้างหมวดหมู่จัดเก็บสิ่งเดียวกันไว้ด้วยกัน ซึ่งในที่นี้เราจะสร้างโฟลเดอร์ไว้จัดเก็บ component ที่เกี่ยวกับ member ไว้ด้วยกัน โดยการสร้างโฟลเดอร์ชื่อ members และย้าย member-list เข้าไปไว้ในนั้นซะ
***เมื่อย้ายแล้วต้องเปลี่ยน path ที่ import ให้ถูกต้องทุกจุดด้วยนะ (แนะนำให้ vs-code refactoring ให้ทีเดียวเลย)
สร้าง component เพิ่มชื่อ member-card และจัดเก็บไว้ใน members โดยเมื่อสร้างแล้ว เราต้องทำการ import ใน app.module.ts เอง
member-card.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { User } from 'src/app/_models/user';
@Component({
selector: 'app-member-card',
templateUrl: './member-card.component.html',
styleUrls: ['./member-card.component.css']
})
export class MemberCardComponent implements OnInit {
@Input() user: User;
constructor() { }
ngOnInit() {
}
}
member-card.component.html
<div class="card mb-4">
<div class="card-img-wrapper">
<img src="{{user.photoUrl}}" alt="{{user.knownAs}}" class="card-img-top">
</div>
<div class="card-body p1">
<h6 class="card-title text-center mb-1">
<i class="fa fa-user"></i> {{user.knownAs}}, {{user.age}}
</h6>
<p class="card-text text-muted text-center">{{user.city}}</p>
</div>
</div>
ปรับปรุงโค้ดใน member-list.component.html
<div class="container mt-5">
<div class="row">
<div *ngFor="let user of users" class="col-lg-2 col-md-3 col-sm-6">
<app-member-card [user]="user"></app-member-card>
</div>
</div>
</div>
ทดสอบรัน และจะพบหน้าจอดังนี้
5. เพิ่ม Effect Animation ใน Member Card
เพิ่ม effect ให้ member card เราดูน่าใช้งานมากขึ้น โดยกำหนดใน member-card.component.css
.card:hover img {
transform: scale(1.2, 1.2);
transition-duration: 500ms;
transition-timing-function: ease-out;
opacity: 0.7;
}
.card img {
transform: scale(1.0, 1.0);
transition-duration: 500ms;
transition-timing-function: ease-out;
}
.card-img-wrapper {
overflow: hidden;
}
เพิ่มปุ่มดูรายละเอียด ปุ่มถูกใจ และปุ่มส่งข้อความส่วนตัว โดยเพิ่มเติมและปรับปรุงโค้ด ดังนี้
member-card.component.html
<div class="card mb-4">
<div class="card-img-wrapper">
<img src="{{user.photoUrl}}" alt="{{user.knownAs}}" class="card-img-top">
<ul class="list-inline member-icons anumate text-center">
<li class="list-inline-item"><button class="btn btn-primary btn-sm"><i class="fa fa-user"></i></button></li>
<li class="list-inline-item"><button class="btn btn-primary btn-sm"><i class="fa fa-heart"></i></button></li>
<li class="list-inline-item"><button class="btn btn-primary btn-sm"><i class="fa fa-envelope"></i></button></li>
</ul>
</div>
<div class="card-body p1">
<h6 class="card-title text-center mb-1">
<i class="fa fa-user"></i> {{user.knownAs}}, {{user.age}}
</h6>
<p class="card-text text-muted text-center">{{user.city}}</p>
</div>
</div>
member-card.component.css
.card:hover img {
transform: scale(1.2, 1.2);
transition-duration: 500ms;
transition-timing-function: ease-out;
opacity: 0.7;
}
.card img {
transform: scale(1.0, 1.0);
transition-duration: 500ms;
transition-timing-function: ease-out;
}
.card-img-wrapper {
overflow: hidden;
position: relative;
}
.member-icons {
position: absolute;
bottom: -30%;
left: 0;
right: 0;
margin-right: auto;
margin-left: auto;
opacity: 0;
}
.card-img-wrapper:hover .member-icons {
bottom: 0;
opacity: 1;
}
.animate {
transition: all 0.3s ease-in-out;
}
ทดสอบรัน และดูผลที่แตกต่าง
6. ใช้ Auth0 JwtModule ในการส่ง Token เมื่อดึงข้อมูลโดยอัตโนมัติ
ในการเรียก API ตอนนี้เราทำการใส่ token เข้าไปใน header เองเมื่อทำการเรียก ในขั้นตอนนี้เราจะใช้ตัวช่วยในการใส่ token ให้ ซึ่งก็คือ auth0 นั่นเอง
import JwtModule จาก Auth0 ใน app.module.ts
import { JwtModule } from '@auth0/angular-jwt';
เพิ่มฟังก์ชัน tokenGetter ไว้ที่ส่วนบนของ app.module.ts
export function tokenGetter() {
return localStorage.getItem('token');
}
ปรับปรุงการ import JwtModule เป็น
JwtModule.forRoot({
config: {
tokenGetter,
whitelistedDomains: ['localhost:5000'],
blacklistedRoutes: ['localhost:5000/api/auth']
}
})
app.module.ts ที่ปรับปรุงแล้ว
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { RouterModule } from '@angular/router';
import { JwtModule } from '@auth0/angular-jwt';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { AuthService } from './_services/auth.service';
import { NavComponent } from './nav/nav.component';
import { HomeComponent } from './home/home.component';
import { RegisterComponent } from './register/register.component';
import { ErrorInterceptorProvider } from './_services/error.interceptor';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ListsComponent } from './lists/lists.component';
import { MessagesComponent } from './messages/messages.component';
import { appRoutes } from './routes';
import { MemberListComponent } from './members/member-list/member-list.component';
import { MemberCardComponent } from './members/member-card/member-card.component';
export function tokenGetter() {
return localStorage.getItem('token');
}
@NgModule({
declarations: [
AppComponent,
LoginComponent,
NavComponent,
HomeComponent,
RegisterComponent,
ListsComponent,
MessagesComponent,
MemberListComponent,
MemberCardComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
BsDropdownModule.forRoot(),
BrowserAnimationsModule,
RouterModule.forRoot(appRoutes),
JwtModule.forRoot({
config: {
tokenGetter,
whitelistedDomains: ['localhost:5000'],
blacklistedRoutes: ['localhost:5000/api/auth']
}
})
],
providers: [
AuthService,
ErrorInterceptorProvider
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
ลบ httpOptions ใน user.service.ts ออก
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../_models/user';
@Injectable({
providedIn: 'root'
})
export class UserService {
baseUrl = environment.apiUrl;
constructor(private http: HttpClient) { }
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.baseUrl + 'users');
}
getUser(id): Observable<User> {
return this.http.get<User>(this.baseUrl + 'users/' + id);
}
}
7. สร้าง Detail View Component เพื่อแสดงรายละเอียดของ Members
สร้าง member-detail component ภายใต้ members และทำการ import MemberDetailComponent ใน app.module.ts
member-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { User } from 'src/app/_models/user';
import { UserService } from 'src/app/_services/user.service';
import { AlertifyService } from 'src/app/_services/alertify.service';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-member-detail',
templateUrl: './member-detail.component.html',
styleUrls: ['./member-detail.component.css']
})
export class MemberDetailComponent implements OnInit {
user: User;
constructor(private userService: UserService,
private alertify: AlertifyService,
private route: ActivatedRoute) { }
ngOnInit() {
this.loadUser();
}
// members/{id}
loadUser() {
this.userService.getUser(+this.route.snapshot.params.id).subscribe((user: User) => {
this.user = user;
}, error => {
this.alertify.error(error);
});
}
}
member-detail.component.html
<p>
{{user.knownAs}}
</p>
เพิ่ม Route ใน routes.ts
{ path: 'members/:id', component: MemberDetailComponent },
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ListsComponent } from './lists/lists.component';
import { MessagesComponent } from './messages/messages.component';
import { MemberListComponent } from './members/member-list/member-list.component';
import { AuthGuard } from './_guards/auth.guard';
import { MemberDetailComponent } from './members/member-detail/member-detail.component';
export const appRoutes: Routes = [
{ path: 'home', component: HomeComponent },
{
path: '',
runGuardsAndResolvers: 'always',
canActivate: [AuthGuard],
children: [
{ path: 'members', component: MemberListComponent },
{ path: 'members/:id', component: MemberDetailComponent },
{ path: 'messages', component: MessagesComponent },
{ path: 'lists', component: ListsComponent }
]
},
{ path: '**', redirectTo: 'home', pathMatch: 'full' },
];
เพิ่ม routerLink ใน member-card
<div class="card mb-4">
<div class="card-img-wrapper">
<img src="{{user.photoUrl}}" alt="{{user.knownAs}}" class="card-img-top">
<ul class="list-inline member-icons anumate text-center">
<li class="list-inline-item">
<button class="btn btn-primary btn-sm" [routerLink]="['/members/', user.id]">
<i class="fa fa-user"></i>
</button>
</li>
<li class="list-inline-item"><button class="btn btn-primary btn-sm"><i class="fa fa-heart"></i></button></li>
<li class="list-inline-item"><button class="btn btn-primary btn-sm"><i class="fa fa-envelope"></i></button></li>
</ul>
</div>
<div class="card-body p1">
<h6 class="card-title text-center mb-1">
<i class="fa fa-user"></i> {{user.knownAs}}, {{user.age}}
</h6>
<p class="card-text text-muted text-center">{{user.city}}</p>
</div>
</div>
ทดสอบรัน และลองคลิกรายละเอียดดู จะพบว่าทำงานได้แล้ว แต่ที่ colsole มี error
ทำการปรับ member-detail.component.html ใส่ ? ที่ user เพื่อป้องกันกรณีที่ user ไม่มีข้อมูลมา จะพบว่า error หายไปแล้ว
<p>
{{user?.knownAs}}
</p>
8. ออกแบบ Template เพื่อแสดงรายละเอียดของ Members
member-detail.component.html
<div class="container mt-4">
<div class="row">
<div class="col-sm-4">
<div class="card">
<img src="{{user?.photoUrl}}" alt="{{user?.knownAs}}" class="card-img-top img-thumbnail">
<div class="card-body">
<div>
<strong>Location:</strong>
<p>{{user?.city}}, {{user?.country}}</p>
</div>
<div>
<strong>Age:</strong>
<p>{{user?.age}}</p>
</div>
<div>
<strong>Last Active:</strong>
<p>{{user?.lastActive}}</p>
</div>
<div>
<strong>Member since:</strong>
<p>{{user?.created}}</p>
</div>
</div>
<div class="card-footer">
<div class="btn-group d-flex">
<button class="btn btn-primary w-100">Like</button>
<button class="btn btn-success w-100">Message</button>
</div>
</div>
</div>
</div>
<div class="col-sm-8">
</div>
</div>
</div>
member-detail.component.css
.img-thumbnail {
margin: 25px;
width: 85%;
height: 85%;
}
.card-body {
padding: 0 25px;
}
.card-footer {
padding: 10px 15px;
background-color: #fff;
border-top: none;
}
ลองรันและลองดูหน้ารายละเอียดของ member
9. ใช้งาน Tap ในการแสดงรายละเอียดของ Members
import TabsModule
TabsModule.forRoot(),
import { TabsModule } from 'ngx-bootstrap/tabs';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { RouterModule } from '@angular/router';
import { JwtModule } from '@auth0/angular-jwt';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { AuthService } from './_services/auth.service';
import { NavComponent } from './nav/nav.component';
import { HomeComponent } from './home/home.component';
import { RegisterComponent } from './register/register.component';
import { ErrorInterceptorProvider } from './_services/error.interceptor';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ListsComponent } from './lists/lists.component';
import { MessagesComponent } from './messages/messages.component';
import { appRoutes } from './routes';
import { MemberListComponent } from './members/member-list/member-list.component';
import { MemberCardComponent } from './members/member-card/member-card.component';
import { MemberDetailComponent } from './members/member-detail/member-detail.component';
export function tokenGetter() {
return localStorage.getItem('token');
}
@NgModule({
declarations: [
AppComponent,
LoginComponent,
NavComponent,
HomeComponent,
RegisterComponent,
ListsComponent,
MessagesComponent,
MemberListComponent,
MemberCardComponent,
MemberDetailComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
BsDropdownModule.forRoot(),
TabsModule.forRoot(),
BrowserAnimationsModule,
RouterModule.forRoot(appRoutes),
JwtModule.forRoot({
config: {
tokenGetter,
whitelistedDomains: ['localhost:5000'],
blacklistedRoutes: ['localhost:5000/api/auth']
}
})
],
exports: [
TabsModule
],
providers: [
AuthService,
ErrorInterceptorProvider
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
เพิ่มโค้ด html tabs ใน member-detail.component.html
<div class="tab-panel">
<tabset class="member-tabset">
<tab heading="About {{user?.knownAs}}">
<h4>Description</h4>
<p>{{user?.introduction}}</p>
<h4>Looking for</h4>
<p>{{user?.lookingFor}}</p>
</tab>
<tab heading="Interests">
<h4>Interests</h4>
<p>{{user?.interests}}</p>
</tab>
<tab heading="Photos">
<p>Photos will go here</p>
</tab>
<tab heading="Messages">
<p>Messages will go here</p>
</tab>
</tabset>
</div>
<div class="container mt-4">
<div class="row">
<div class="col-sm-4">
<div class="card">
<img src="{{user?.photoUrl}}" alt="{{user?.knownAs}}" class="card-img-top img-thumbnail">
<div class="card-body">
<div>
<strong>Location:</strong>
<p>{{user?.city}}, {{user?.country}}</p>
</div>
<div>
<strong>Age:</strong>
<p>{{user?.age}}</p>
</div>
<div>
<strong>Last Active:</strong>
<p>{{user?.lastActive}}</p>
</div>
<div>
<strong>Member since:</strong>
<p>{{user?.created}}</p>
</div>
</div>
<div class="card-footer">
<div class="btn-group d-flex">
<button class="btn btn-primary w-100">Like</button>
<button class="btn btn-success w-100">Message</button>
</div>
</div>
</div>
</div>
<div class="col-sm-8">
<div class="tab-panel">
<tabset class="member-tabset">
<tab heading="About {{user?.knownAs}}">
<h4>Description</h4>
<p>{{user?.introduction}}</p>
<h4>Looking for</h4>
<p>{{user?.lookingFor}}</p>
</tab>
<tab heading="Interests">
<h4>Interests</h4>
<p>{{user?.interests}}</p>
</tab>
<tab heading="Photos">
<p>Photos will go here</p>
</tab>
<tab heading="Messages">
<p>Messages will go here</p>
</tab>
</tabset>
</div>
</div>
</div>
</div>
เพิ่ม css ใน styles.css
.tab-panel {
border: 1px solid #ddd;
padding: 10px;
border-radius: 4px;
}
.nav-tabs > li.open,
.member-tabset > .nav-tabs > li:hover {
border-bottom: 4px solid #fbcdcf;
}
.member-tabset > .nav-tabs > li.open > a,
.member-tabset > .nav-tabs > li:hover > a {
border: 0;
background: none !important;
color: #333333;
}
.member-tabset > .nav-tabs > li.open > a > i,
.member-tabset > .nav-tabs > li:hover > a > i {
color: #a6a6a6;
}
.member-tabset > .nav-tabs > li.open .dropdown-menu,
.member-tabset > .nav-tabs > li:hover .dropdown-menu {
margin-top: 0px;
}
.member-tabset > .nav-tabs > li.active {
border-bottom: 4px solid #e95420;
position: relative;
}
.member-tabset > .nav-tabs > li.active > a {
border: 0 !important;
color: #333333;
}
.member-tabset > .nav-tabs > li.active > a > i {
color: #404040;
}
.member-tabset > .tab-content {
margin-top: -3px;
background-color: #fff;
border: 0;
border-top: 1px solid #eee;
padding: 15px 0;
}
ลองรันดูอีกครั้ง
10. ใช้งาน Route Resolvers เพื่อรับส่งข้อมูล
สร้าง Resolver ที่ _resolvers/member-detail.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router';
import { User } from '../_models/user';
import { UserService } from '../_services/user.service';
import { AlertifyService } from '../_services/alertify.service';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class MemberDetailResolver implements Resolve<User> {
constructor(private userService: UserService,private router: Router, private alertify: AlertifyService) {}
resolve(route: ActivatedRouteSnapshot): Observable<User> {
return this.userService.getUser(route.params.id).pipe(
catchError(error => {
this.alertify.error('Problem retrieving data');
this.router.navigate(['/members']);
return of(null);
})
);
}
}
ทำการเพิ่ม providers ใน app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { RouterModule } from '@angular/router';
import { JwtModule } from '@auth0/angular-jwt';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { AuthService } from './_services/auth.service';
import { NavComponent } from './nav/nav.component';
import { HomeComponent } from './home/home.component';
import { RegisterComponent } from './register/register.component';
import { ErrorInterceptorProvider } from './_services/error.interceptor';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ListsComponent } from './lists/lists.component';
import { MessagesComponent } from './messages/messages.component';
import { appRoutes } from './routes';
import { MemberListComponent } from './members/member-list/member-list.component';
import { MemberCardComponent } from './members/member-card/member-card.component';
import { MemberDetailComponent } from './members/member-detail/member-detail.component';
import { MemberDetailResolver } from './_resolvers/member-detail.resolver';
export function tokenGetter() {
return localStorage.getItem('token');
}
@NgModule({
declarations: [
AppComponent,
LoginComponent,
NavComponent,
HomeComponent,
RegisterComponent,
ListsComponent,
MessagesComponent,
MemberListComponent,
MemberCardComponent,
MemberDetailComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
BsDropdownModule.forRoot(),
TabsModule.forRoot(),
BrowserAnimationsModule,
RouterModule.forRoot(appRoutes),
JwtModule.forRoot({
config: {
tokenGetter,
whitelistedDomains: ['localhost:5000'],
blacklistedRoutes: ['localhost:5000/api/auth']
}
})
],
exports: [
TabsModule
],
providers: [
AuthService,
ErrorInterceptorProvider,
MemberDetailResolver
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
กำหนดให้ Route ใช้งาน Resolver ที่สร้างขึ้นใน routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ListsComponent } from './lists/lists.component';
import { MessagesComponent } from './messages/messages.component';
import { MemberListComponent } from './members/member-list/member-list.component';
import { AuthGuard } from './_guards/auth.guard';
import { MemberDetailComponent } from './members/member-detail/member-detail.component';
import { MemberDetailResolver } from './_resolvers/member-detail.resolver';
export const appRoutes: Routes = [
{ path: 'home', component: HomeComponent },
{
path: '',
runGuardsAndResolvers: 'always',
canActivate: [AuthGuard],
children: [
{ path: 'members', component: MemberListComponent },
{ path: 'members/:id', component: MemberDetailComponent,
resolve: {user: MemberDetailResolver}},
{ path: 'messages', component: MessagesComponent },
{ path: 'lists', component: ListsComponent }
]
},
{ path: '**', redirectTo: 'home', pathMatch: 'full' },
];
เรียกใช Resolver ใน onInit ของ member-detail.component.ts
this.route.data.subscribe(data => {
this.user = data.user;
});
import { Component, OnInit } from '@angular/core';
import { User } from 'src/app/_models/user';
import { UserService } from 'src/app/_services/user.service';
import { AlertifyService } from 'src/app/_services/alertify.service';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-member-detail',
templateUrl: './member-detail.component.html',
styleUrls: ['./member-detail.component.css']
})
export class MemberDetailComponent implements OnInit {
user: User;
constructor(private userService: UserService,
private alertify: AlertifyService,
private route: ActivatedRoute) { }
ngOnInit() {
this.route.data.subscribe(data => {
this.user = data.user;
});
}
// members/{id}
loadUser() {
this.userService.getUser(+this.route.snapshot.params.id).subscribe((user: User) => {
this.user = user;
}, error => {
this.alertify.error(error);
});
}
}
ทีนี้ก็สามารถลบ ? ใน member-detail.component.html ได้แล้ว เพราะข้อมูลจะมาแน่ ๆ แล้ว
<div class="container mt-4">
<div class="row">
<div class="col-sm-4">
<div class="card">
<img src="{{user.photoUrl}}" alt="{{user.knownAs}}" class="card-img-top img-thumbnail">
<div class="card-body">
<div>
<strong>Location:</strong>
<p>{{user.city}}, {{user?.country}}</p>
</div>
<div>
<strong>Age:</strong>
<p>{{user.age}}</p>
</div>
<div>
<strong>Last Active:</strong>
<p>{{user.lastActive}}</p>
</div>
<div>
<strong>Member since:</strong>
<p>{{user.created}}</p>
</div>
</div>
<div class="card-footer">
<div class="btn-group d-flex">
<button class="btn btn-primary w-100">Like</button>
<button class="btn btn-success w-100">Message</button>
</div>
</div>
</div>
</div>
<div class="col-sm-8">
<div class="tab-panel">
<tabset class="member-tabset">
<tab heading="About {{user.knownAs}}">
<h4>Description</h4>
<p>{{user.introduction}}</p>
<h4>Looking for</h4>
<p>{{user.lookingFor}}</p>
</tab>
<tab heading="Interests">
<h4>Interests</h4>
<p>{{user.interests}}</p>
</tab>
<tab heading="Photos">
<p>Photos will go here</p>
</tab>
<tab heading="Messages">
<p>Messages will go here</p>
</tab>
</tabset>
</div>
</div>
</div>
</div>
ทดสอบรัน และยังคงสามารถทำงานได้เช่นเดิม
สร้าง Resolver อีกตัวหนึ่ง เพื่อใช้งานกับ member-list.component ชื่อ member-list.resolver.ts โดยคัดลอกจากไฟล์เดิมได้เลย
member-list.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router';
import { User } from '../_models/user';
import { UserService } from '../_services/user.service';
import { AlertifyService } from '../_services/alertify.service';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class MemberListResolver implements Resolve<User[]> {
constructor(private userService: UserService,private router: Router, private alertify: AlertifyService) {}
resolve(route: ActivatedRouteSnapshot): Observable<User[]> {
return this.userService.getUsers().pipe(
catchError(error => {
this.alertify.error('Problem retrieving data');
this.router.navigate(['/home']);
return of(null);
})
);
}
}
routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ListsComponent } from './lists/lists.component';
import { MessagesComponent } from './messages/messages.component';
import { MemberListComponent } from './members/member-list/member-list.component';
import { AuthGuard } from './_guards/auth.guard';
import { MemberDetailComponent } from './members/member-detail/member-detail.component';
import { MemberDetailResolver } from './_resolvers/member-detail.resolver';
import { MemberListResolver } from './_resolvers/member-list.resolver';
export const appRoutes: Routes = [
{ path: 'home', component: HomeComponent },
{
path: '',
runGuardsAndResolvers: 'always',
canActivate: [AuthGuard],
children: [
{ path: 'members', component: MemberListComponent,
resolve: {users: MemberListResolver}},
{ path: 'members/:id', component: MemberDetailComponent,
resolve: {user: MemberDetailResolver}},
{ path: 'messages', component: MessagesComponent },
{ path: 'lists', component: ListsComponent }
]
},
{ path: '**', redirectTo: 'home', pathMatch: 'full' },
];
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { RouterModule } from '@angular/router';
import { JwtModule } from '@auth0/angular-jwt';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { AuthService } from './_services/auth.service';
import { NavComponent } from './nav/nav.component';
import { HomeComponent } from './home/home.component';
import { RegisterComponent } from './register/register.component';
import { ErrorInterceptorProvider } from './_services/error.interceptor';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ListsComponent } from './lists/lists.component';
import { MessagesComponent } from './messages/messages.component';
import { appRoutes } from './routes';
import { MemberListComponent } from './members/member-list/member-list.component';
import { MemberCardComponent } from './members/member-card/member-card.component';
import { MemberDetailComponent } from './members/member-detail/member-detail.component';
import { MemberDetailResolver } from './_resolvers/member-detail.resolver';
import { MemberListResolver } from './_resolvers/member-list.resolver';
export function tokenGetter() {
return localStorage.getItem('token');
}
@NgModule({
declarations: [
AppComponent,
LoginComponent,
NavComponent,
HomeComponent,
RegisterComponent,
ListsComponent,
MessagesComponent,
MemberListComponent,
MemberCardComponent,
MemberDetailComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
BsDropdownModule.forRoot(),
TabsModule.forRoot(),
BrowserAnimationsModule,
RouterModule.forRoot(appRoutes),
JwtModule.forRoot({
config: {
tokenGetter,
whitelistedDomains: ['localhost:5000'],
blacklistedRoutes: ['localhost:5000/api/auth']
}
})
],
exports: [
TabsModule
],
providers: [
AuthService,
ErrorInterceptorProvider,
MemberDetailResolver,
MemberListResolver
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
member-list.component.ts
import { Component, OnInit } from '@angular/core';
import { User } from '../../_models/user';
import { UserService } from '../../_services/user.service';
import { AlertifyService } from '../../_services/alertify.service';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-member-list',
templateUrl: './member-list.component.html',
styleUrls: ['./member-list.component.css']
})
export class MemberListComponent implements OnInit {
users: User[];
constructor(private userService: UserService, private alertify: AlertifyService, private route: ActivatedRoute) { }
ngOnInit() {
this.route.data.subscribe(data => {
this.users = data.users;
});
}
loadUsers() {
this.userService.getUsers().subscribe((users: User[]) => {
this.users = users;
}, error => {
this.alertify.error(error);
});
}
}
ทดสอบรัน และต้องสามารถทำงานได้เหมือนเดิม แต่ข้อมูลเราจะต้องมาเสมอทุกจังหวะแล้ว
11. เพิ่ม Photo Gallery
ติดตั้ง ngx-gallery โดยใช้คำสั่ง
npm install @kolkov/ngx-gallery
import NgxGalleryModule ใน app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { RouterModule } from '@angular/router';
import { JwtModule } from '@auth0/angular-jwt';
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { AuthService } from './_services/auth.service';
import { NavComponent } from './nav/nav.component';
import { HomeComponent } from './home/home.component';
import { RegisterComponent } from './register/register.component';
import { ErrorInterceptorProvider } from './_services/error.interceptor';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ListsComponent } from './lists/lists.component';
import { MessagesComponent } from './messages/messages.component';
import { appRoutes } from './routes';
import { MemberListComponent } from './members/member-list/member-list.component';
import { MemberCardComponent } from './members/member-card/member-card.component';
import { MemberDetailComponent } from './members/member-detail/member-detail.component';
import { MemberDetailResolver } from './_resolvers/member-detail.resolver';
import { MemberListResolver } from './_resolvers/member-list.resolver';
export function tokenGetter() {
return localStorage.getItem('token');
}
@NgModule({
declarations: [
AppComponent,
LoginComponent,
NavComponent,
HomeComponent,
RegisterComponent,
ListsComponent,
MessagesComponent,
MemberListComponent,
MemberCardComponent,
MemberDetailComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
BsDropdownModule.forRoot(),
TabsModule.forRoot(),
BrowserAnimationsModule,
RouterModule.forRoot(appRoutes),
NgxGalleryModule,
JwtModule.forRoot({
config: {
tokenGetter,
whitelistedDomains: ['localhost:5000'],
blacklistedRoutes: ['localhost:5000/api/auth']
}
})
],
exports: [
TabsModule
],
providers: [
AuthService,
ErrorInterceptorProvider,
MemberDetailResolver,
MemberListResolver
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
กำหนดการแสดงผล และรูปภาพใน member-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { User } from 'src/app/_models/user';
import { UserService } from 'src/app/_services/user.service';
import { AlertifyService } from 'src/app/_services/alertify.service';
import { ActivatedRoute } from '@angular/router';
import { NgxGalleryOptions, NgxGalleryImage, NgxGalleryAnimation } from '@kolkov/ngx-gallery';
@Component({
selector: 'app-member-detail',
templateUrl: './member-detail.component.html',
styleUrls: ['./member-detail.component.css']
})
export class MemberDetailComponent implements OnInit {
user: User;
galleryOptions: NgxGalleryOptions[];
galleryImages: NgxGalleryImage[];
constructor(private userService: UserService,
private alertify: AlertifyService,
private route: ActivatedRoute) { }
ngOnInit() {
this.route.data.subscribe(data => {
this.user = data.user;
});
this.galleryOptions = [
{
width: '500px',
height: '500px',
imagePercent: 100,
thumbnailsColumns: 4,
imageAnimation: NgxGalleryAnimation.Slide,
preview: false
}
];
this.galleryImages = this.getImages();
}
getImages() {
const imageUrls = [];
for (const photo of this.user.photos) {
imageUrls.push({
small: photo.url,
medium: photo.url,
large: photo.url,
description: photo.description
});
}
return imageUrls;
}
}
นำ ngx-gallery ไปแสดงรูปภาพใน member-detail.component.html
<ngx-gallery [options]="galleryOptions" [images]="galleryImages" style="display: inline-block; margin-bottom: 20px;"></ngx-gallery>
<div class="container mt-4">
<div class="row">
<div class="col-sm-4">
<div class="card">
<img src="{{user.photoUrl}}" alt="{{user.knownAs}}" class="card-img-top img-thumbnail">
<div class="card-body">
<div>
<strong>Location:</strong>
<p>{{user.city}}, {{user?.country}}</p>
</div>
<div>
<strong>Age:</strong>
<p>{{user.age}}</p>
</div>
<div>
<strong>Last Active:</strong>
<p>{{user.lastActive}}</p>
</div>
<div>
<strong>Member since:</strong>
<p>{{user.created}}</p>
</div>
</div>
<div class="card-footer">
<div class="btn-group d-flex">
<button class="btn btn-primary w-100">Like</button>
<button class="btn btn-success w-100">Message</button>
</div>
</div>
</div>
</div>
<div class="col-sm-8">
<div class="tab-panel">
<tabset class="member-tabset">
<tab heading="About {{user.knownAs}}">
<h4>Description</h4>
<p>{{user.introduction}}</p>
<h4>Looking for</h4>
<p>{{user.lookingFor}}</p>
</tab>
<tab heading="Interests">
<h4>Interests</h4>
<p>{{user.interests}}</p>
</tab>
<tab heading="Photos">
<ngx-gallery [options]="galleryOptions" [images]="galleryImages" style="display: inline-block; margin-bottom: 20px;"></ngx-gallery>
</tab>
<tab heading="Messages">
<p>Messages will go here</p>
</tab>
</tabset>
</div>
</div>
</div>
</div>
โปรดติดตามตอนต่อไป…