Mục tiêu

Ứng dụng blog- nắm bắt được về các thao tác với HttpClient trong Angular.

Mô tả- Ứng dụng blog

Ứng dụng blog đơn giản

Hướng dẫn

Bước 1: Khởi tạo project với Angular CLI (nếu đã có project thì có thể bỏ qua)

Các bạn chạy lệnh npm i typescript@2.9.2 -D để cài đặt thư viện typescript nếu phiên bản hiện tại < 2.9.2

Bước 2: Khởi tạo một service tên là post, 3 component tên là blog, blog-detail, blog-edit, interface post

ng g s post

ng g c blog

ng g c blog-edit

ng g c blog-detail

ng g interface post

Bước 3: Thay phần template của component vừa tạo bằng đoạn code sau:

file blog.component.html:

<h2>Simple Blog</h2>

<form [formGroup]="postForm" (ngSubmit)="onSubmit()">
    <div>
        <label>
            Title
            <br>
            <input type="text" formControlName="title">
        </label>
    </div>
    <div *ngIf="postForm.get('title').invalid && postForm.get('title').touched" style="color: red">
        Title is required and min length is 10
    </div>
    <div>
        <label>
            body
            <br>
            <textarea formControlName="body" cols="30" rows="10"></textarea>
        </label>
    </div>
    <div *ngIf="postForm.get('body').invalid && postForm.get('body').touched" style="color: red">
        Body is required and min length is 10
    </div>
    <button type="submit">Submit</button>
</form>

<ul>
    <li *ngFor="let post of postList; index as i">
        <h3>
            <a [routerLink]="['/blog', post.id]">
                Post Id: {{post.id}} - {{post.title}}
            </a>
        </h3>
        <a [routerLink]="['/blog', post.id, 'edit']">Edit</a>

        <button (click)="deletePost(i)">Delete</button>
    </li>
</ul>

blog-detail.component.html:

<div *ngIf="post; else nothing">
    <h4>
        {{post.title}}
    </h4>
    <p>
        {{post.body}}
    </p>
</div>

<ng-template #nothing>
    <p>Nothing to show</p>
</ng-template>

blog-edit.component.html:

<form [formGroup]="postForm" (ngSubmit)="onSubmit()">
    <div>
        <label>
            Title
            <br>
            <input type="text" formControlName="title">
        </label>
    </div>
    <div *ngIf="postForm.get('title').invalid && postForm.get('title').touched" style="color: red">
        Title is required and min length is 10
    </div>
    <div>
        <label>
            body
            <br>
            <textarea formControlName="body" cols="30" rows="10"></textarea>
        </label>
    </div>
    <div *ngIf="postForm.get('body').invalid && postForm.get('body').touched" style="color: red">
        Body is required and min length is 10
    </div>
    <button type="submit">Submit</button>
</form>

Bước 4: Code phần logic cho component

blog.component.ts:

import { Component, OnInit } from '@angular/core';
import { PostService } from '../post.service';
import { IPost } from '../post';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
    selector: 'app-blog',
    templateUrl: './blog.component.html',
    styleUrls: ['./blog.component.scss']
})
export class BlogComponent implements OnInit {
    postList: IPost[] = [];
    postForm: FormGroup;
    constructor(
        private postService: PostService,
    private fb: FormBuilder
) {}

ngOnInit() {
    this.postForm = this.fb.group({
        title: ['', [Validators.required, Validators.minLength(10)]],
        body: ['', [Validators.required, Validators.minLength(10)]],
    });
    this.postService
        .getPosts()
        .subscribe(next => (this.postList = next), error => (this.postList = []));
}

onSubmit() {
    if (this.postForm.valid) {
        const {value} = this.postForm;
        this.postService.createPost(value)
            .subscribe(next => {
                this.postList.unshift(next);
                this.postForm.reset({
                    title: '',
                    body: ''
                });
            }, error => console.log(error));
    }
}

deletePost(i) {
    const post = this.postList[i];
    this.postService.deletePost(post.id).subscribe(() => {
        this.postList = this.postList.filter(t => t.id !== post.id);
    });
}
}


blog-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PostService } from '../post.service';
import { IPost } from '../post';

@Component({
    selector: 'app-blog-detail',
    templateUrl: './blog-detail.component.html',
    styleUrls: ['./blog-detail.component.scss']
})
export class BlogDetailComponent implements OnInit {
    post: IPost;
    constructor(
        private route: ActivatedRoute,
    private postService: PostService
) {}

ngOnInit() {
    const id = +this.route.snapshot.paramMap.get('id');
    this.postService.getPostById(id).subscribe(
        next => (this.post = next),
        error => {
            console.log(error);
            this.post = null;
        }
    );
}
}

blog-edit.component.ts:

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { IPost } from '../post';
import { PostService } from '../post.service';

@Component({
    selector: 'app-blog-edit',
    templateUrl: './blog-edit.component.html',
    styleUrls: ['./blog-edit.component.scss']
})
export class BlogEditComponent implements OnInit {
    post: IPost;
    postForm: FormGroup;
    constructor(
        private route: ActivatedRoute,
    private postService: PostService,
    private fb: FormBuilder,
    private router: Router
) {}

ngOnInit() {
    this.postForm = this.fb.group({
        title: ['', [Validators.required, Validators.minLength(10)]],
        body: ['', [Validators.required, Validators.minLength(10)]]
    });
    const id = +this.route.snapshot.paramMap.get('id');
    this.postService.getPostById(id).subscribe(
        next => {
            this.post = next;
            this.postForm.patchValue(this.post);
        },
        error => {
            console.log(error);
            this.post = null;
        }
    );
}

onSubmit() {
    if (this.postForm.valid) {
        const { value } = this.postForm;
        const data = {
            ...this.post,
            ...value
        };
        this.postService.updatePost(data).subscribe(
            next => {
                this.router.navigate(['/blog']);
            },
            error => console.log(error)
        );
    }
}
}

Bước 5: Cài đặt post service để call API:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { IPost } from './post';

@Injectable({
    providedIn: 'root'
})
export class PostService {
    private readonly API_URL = 'http://jsonplaceholder.typicode.com/posts';
    constructor(private http: HttpClient) { }
getPosts(count = 10): Observable<IPost[]> {
    return this.http.get<IPost[]>(this.API_URL).pipe(
        map(response => response.filter((post, i) => i < count))
    );
}
getPostById(id: number): Observable<IPost> {
    return this.http.get<IPost>(`${this.API_URL}/${id}`);
}
createPost(post: Partial<IPost>): Observable<IPost> {
    return this.http.post<IPost>(this.API_URL, post);
}
deletePost(id: number): Observable<any> {
    return this.http.delete(`${this.API_URL}/${id}`);
}
updatePost(post: IPost): Observable<IPost> {
    return this.http.patch<IPost>(`${this.API_URL}/${post.id}`, post);
}
}

Bước 6: Cài đặt post interface:

export interface IPost {
    userId: number;
    id: number;
    title: string;
    body: string;
}

Bước 7: Tạo mới file app-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BlogComponent } from './blog/blog.component';
import { BlogDetailComponent } from './blog-detail/blog-detail.component';
import { BlogEditComponent } from './blog-edit/blog-edit.component';

const routes: Routes = [{
    path: 'blog',
    component: BlogComponent
}, {
    path: 'blog/:id',
    component: BlogDetailComponent
}, {
    path: 'blog/:id/edit',
    component: BlogEditComponent
}];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule {}

Bước 8: Thêm vào app.module.ts:

import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BlogDetailComponent } from './blog-detail/blog-detail.component';
import { BlogComponent } from './blog/blog.component';
import { BlogEditComponent } from './blog-edit/blog-edit.component';

@NgModule({
    declarations: [
        AppComponent,
        BlogComponent,
        BlogDetailComponent,
        BlogEditComponent
    ],
    imports: [
        BrowserModule,
        ReactiveFormsModule,
        HttpClientModule,
        AppRoutingModule
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

Bước 9: Thêm vào app.component.html:

<nav>
    <a routerLink="/blog">Blog</a>
</nav>
<router-outlet></router-outlet>

Bước 10: Kiểm tra kết quả hiển thị trên trình duyệt localhost:4200/blog

User có thể click chuyển trang qua lại, có thể tạo mới post, có thể edit, delete post

Mã nguồn tham khảo: https://github.com/codegym-vn/angular-training/tree/master/angular-http/src/app

Trên đây CodeGym đã cùng với bạn luyện tập các thao tác với HttpClient trong Angular. Hãy chụp ảnh màn hình và nộp bài thực hành của bạn trên CodeGymX để cùng nhau luyện tập nhé!

Exit mobile version