[ตอนที่ 10] Social App Workshop ด้วย ASP.NET Core 3 กับ Angular 9 การแก้ไขข้อมูล Profile

Line
Facebook
Twitter
Google

สารบัญเนื้อหา

  1. สร้าง Member Edit Component และ Route
  2. สร้าง Template สำหรับการแก้ไข Profile
  3. ใช้งาน CanDeactivate Route Guard
  4. สร้าง API สำหรับการอัพเดตข้อมูล Profile
  5. อัพเดตข้อมูล Profile

1. สร้าง Member Edit Component และ Route

สร้าง member-edit Component ภายใต้ members

import MemberEditComponent ใน app.module.ts

เพิ่ม Route ใน routes.ts

{ path: 'member/edit', component: MemberEditComponent },

กำหนด link ใน nav.component.html

<a href="#" class="dropdown-item" [routerLink]="['/member/edit']"><i class="fa fa-user"></i> Edit Profile</a>

สร้าง member-edit.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';
import { AuthService } from '../_services/auth.service';

@Injectable()
export class MemberEditResolver implements Resolve<User> {
    constructor(private userService: UserService, private router: Router,
                private authService: AuthService, private alertify: AlertifyService) {}

    resolve(route: ActivatedRouteSnapshot): Observable<User> {
        return this.userService.getUser(this.authService.decodeToken.nameid).pipe(
            catchError(error => {
                this.alertify.error('Problem retrieving your data');
                this.router.navigate(['/members']);
                return of(null);
            })
        );
    }
}

เพิ่ม MemberEditResolver ใน app.module.ts

เพิ่ม resolve: (user: MemberEditResolver) ใน routes.ts

เพิ่มการเรียกใช้งาน จาก member-edit.component.ts

import { Component, OnInit } from '@angular/core';
import { User } from 'src/app/_models/user';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-member-edit',
  templateUrl: './member-edit.component.html',
  styleUrls: ['./member-edit.component.css']
})
export class MemberEditComponent implements OnInit {
  user: User;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.data.subscribe(data => {
      this.user = data.user;
    });
  }

}

เพิ่มการแสดงผลชื่อของสมาชิกใน member-edit.component.html

<p>
  {{user.knownAs}}
</p>

ทดสอบรันและคลิกที่เมนู Edit Profile

2. สร้าง Template สำหรับการแก้ไข Profile

สร้าง template ใน member-edit.component.html

<div class="container mt-4">
  <div class="row">
    <div class="col-sm-4">
      <h1>Your Profile</h1>
    </div>
    <div class="col-sm-8">
      <div *ngIf="editForm.dirty" class="alert alert-info">
        <strong>Information: </strong>You have made change. Any unsaved change will be lost!
      </div>
    </div>
  </div>
  <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 [disabled]="!editForm.dirty" form="editForm" class="btn btn-success btn-block">Save Change</button>
          </div>
        </div>
      </div>
    </div>
    <div class="col-sm-8">
      <div class="tab-panel">
        <tabset class="member-tabset">
          <tab heading="Edit Profile">
            <form #editForm="ngForm" id="editForm" (ngSubmit)="updateUser()">
              <h4>Description</h4>
              <textarea name="introduction" rows="6" class="form-control" [(ngModel)]="user.introduction"></textarea>
              <h4>Looking for</h4>
              <textarea name="lookingFor" rows="6" class="form-control" [(ngModel)]="user.lookingFor"></textarea>
              <h4>Interests</h4>
              <textarea name="interests" rows="6" class="form-control" [(ngModel)]="user.interests"></textarea>
              <h4>Location Details</h4>
              <div class="form-inline">
                <label for="city">City</label>
                <input type="text" name="city" [(ngModel)]="user.city" class="form-control">
                <label for="country">Country</label>
                <input type="text" name="country" [(ngModel)]="user.country" class="form-control">
              </div>
            </form>
          </tab>
          <tab heading="Edit Photos">
            <p>Edit photo will go here</p>
          </tab>
        </tabset>
      </div>
    </div>
  </div>
</div>

member-edit.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-edit.component.ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { User } from 'src/app/_models/user';
import { ActivatedRoute } from '@angular/router';
import { AlertifyService } from 'src/app/_services/alertify.service';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-member-edit',
  templateUrl: './member-edit.component.html',
  styleUrls: ['./member-edit.component.css']
})
export class MemberEditComponent implements OnInit {
  @ViewChild('editForm') editForm: NgForm;
  user: User;

  constructor(private route: ActivatedRoute, private alertify: AlertifyService) { }

  ngOnInit() {
    this.route.data.subscribe(data => {
      this.user = data.user;
    });
  }

  updateUser() {
    console.log(this.user);
    this.alertify.success('Profile updated successfully');
    this.editForm.reset(this.user);
  }

}

ทดสอบรัน

3. ใช้งาน CanDeactivate Route Guard

ใช้ CanDeactivate เพื่อป้องกันการเปลี่ยนหน้า หรือปิดไปโดยที่ยังไม่ได้กดบันทึกข้อมูล

สร้าง prevent-unsaved-changes.guard.ts

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { MemberEditComponent } from '../members/member-edit/member-edit.component';

@Injectable()
export class PreventUnsavedChanges implements CanDeactivate<MemberEditComponent> {
    canDeactivate(component: MemberEditComponent) {
        if (component.editForm.dirty) {
            return confirm('Are you sure you want to continue? Any unsaved changes will be lost');
        }
        return true;
    }
}

เพิ่ม providers ใน app.module.ts

เปิดการใช้งาน CanDeactivate ใน routes.ts ในส่วนของ ‘member/edit’

canDeactivate: [PreventUnsavedChanges]

ทดสอบรัน ไปที่ Edit Profile ลองแก้ไขข้อมูล และเปลี่ยนหน้า จะพบว่ามีข้อความเตือนตามที่กำหนดไว้

สร้างฟังก์ชันเพื่อป้องกันการปิดหน้าต่างโดยไม่ได้บันทึกข้อมูล

@HostListener('window:beforeunload', ['$event'])
unloadNotification($event: any) {
  if (this.editForm.dirty) {
    return $event.returnValue = true;
  }
}

ลองรัน และเปิดหน้า Edit Profile แล้วทำการแก้ไขข้อมูล และปิดหน้าต่างไป

4. สร้าง API สำหรับการอัพเดตข้อมูล Profile

สร้าง API โดยเริ่มจากสร้าง DTOs สำหรับรับข้อมูลที่ต้องการอัพเดต

UserForUpdateDto.cs

namespace SocialApp_API.Dtos
{
    public class UserForUpdateDto
    {
        public string Introduction { get; set; } 
        public string LookingFor { get; set; }
        public string Interests { get; set; }
        public string City { get; set; }
        public string Country { get; set; }
    }
}

เพิ่ม Auto Mapper ใน AutoMapperProfiles.cs

CreateMap<UserForUpdateDto, User>();

เพิ่ม Method สำหรับอัพเดตข้อมูลใน UsersController.cs

[HttpPut("{id}")]
public async Task<IActionResult> UpdateUser(int id, UserForUpdateDto userForUpdateDto)
{
    if (id != int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value))
        return Unauthorized();

    var userFromRepo = await _repo.GetUser(id);
    
    _mapper.Map(userForUpdateDto, userFromRepo);

    if (await _repo.SaveAll())
        return NoContent();

    throw new System.Exception($"Updating user {id} failed on save");
}

5. อัพเดตข้อมูล Profile

เพิ่มฟังก์ชันอัพเดตข้อมูลสมาชิกใน user.service.ts

updateUser(id: number, user: User) {
  return this.http.put(this.baseUrl + 'users/' + id, user);
}
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);
  }

  updateUser(id: number, user: User) {
    return this.http.put(this.baseUrl + 'users/' + id, user);
  }

}

ปรับปรุงฟังก์ชันบันทึกข้อมูลสมาชิกใน member-edit.component.ts

updateUser() {
  this.userService.updateUser(this.authService.decodeToken.nameid, this.user).subscribe(next => {
    this.alertify.success('Profile updated successfully');
    this.editForm.reset(this.user);
  }, error => {
    this.alertify.error(error);
  });
}
import { Component, OnInit, ViewChild, HostListener } from '@angular/core';
import { User } from 'src/app/_models/user';
import { ActivatedRoute } from '@angular/router';
import { AlertifyService } from 'src/app/_services/alertify.service';
import { NgForm } from '@angular/forms';
import { UserService } from 'src/app/_services/user.service';
import { AuthService } from 'src/app/_services/auth.service';

@Component({
  selector: 'app-member-edit',
  templateUrl: './member-edit.component.html',
  styleUrls: ['./member-edit.component.css']
})
export class MemberEditComponent implements OnInit {
  @ViewChild('editForm') editForm: NgForm;
  user: User;
  @HostListener('window:beforeunload', ['$event'])
  unloadNotification($event: any) {
    if (this.editForm.dirty) {
      return $event.returnValue = true;
    }
  }

  constructor(private route: ActivatedRoute, private alertify: AlertifyService,
              private userService: UserService, private authService: AuthService) { }

  ngOnInit() {
    this.route.data.subscribe(data => {
      this.user = data.user;
    });
  }

  updateUser() {
    this.userService.updateUser(this.authService.decodeToken.nameid, this.user).subscribe(next => {
      this.alertify.success('Profile updated successfully');
      this.editForm.reset(this.user);
    }, error => {
      this.alertify.error(error);
    });
  }

}

ทดสอบรัน และลองแก้ไขข้อมูล

โปรดติดตามตอนต่อไป…

Line
Facebook
Twitter
Google
[ตอนที่ 16] Social App Workshop ด้วย ASP.NET Core 3 กับ Angular 9 การทำ Sorting
[ตอนที่ 15] Social App Workshop ด้วย ASP.NET Core 3 กับ Angular 9 การทำ Filtering
[ตอนที่ 14] Social App Workshop ด้วย ASP.NET Core 3 กับ Angular 9 การใช้งาน Paging
No Preview
[ตอนที่ 13] Social App Workshop ด้วย ASP.NET Core 3 กับ Angular 9 การกำหนดรูปแบบการแสดงผลวัน-เวลา
เตรียม Atom สำหรับ React Native #3
เตรียม Visual Studio Code สำหรับ React Native #2
การติดตั้ง React Native บน macOs #1
การกำหนดค่า TF_MIN_GPU_MULTIPROCESSOR_COUNT เพื่อให้ TensorFlow ใช้งาน GPU ทุกตัว
ติดตั้ง Ubuntu 17.04 ใช้งานร่วมกับ Windows 10
การติดตั้ง TensorFlow & Caffe บน Ubuntu 16.04