이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.
이 게시물의 소스코드는 기본사이트 만들기 / Loading Bar 만들기(Angular Material)에서 이어집니다.
angular-site.git 을 clone 한 적이 있는 경우: 터미널에서 해당 폴더로 이동 후 아래 명령어들을 붙여넣기합니다. 폴더 내 모든 코드가 이 게시물의 코드로 교체됩니다. 이를 원치 않으시면 이 방법을 선택하지 마세요.
angular-site.git 을 clone 한 적이 없는 경우: 터미널에서 코드를 다운 받을 폴더로 이동한 후 아래 명령어들을 붙여넣기하여 angular-site.git 을 clone 합니다.
- Github에서 소스코드 보기: https://github.com/a-mean-blogger/angular-site/tree/c2aab3757ae136185b3e58635d05535f16c81a0a
기본 사이트 만들기의 마지막 강의 입니다. user의 조회, 수정, 삭제 기능을 추가하여 CURD를 완성합시다.
이번 강의에서 추가되는 코드는 다음과 같습니다.
즉 새로운 내용이 없으므로 복습이라고 생각하시면 되겠습니다.
주황색은 변경된 파일, 녹색은 새로 생성된 파일, 회색은 변화가 없는 파일입니다.
아래 명령어로 user-edit, user-show component를 생성해 줍시다.
$ ng g component user-edit --spec false $ ng g component user-show -spec false
--spec false
를 options으로 사용하면 .spec.ts를 생성하지 않습니다.
Resolve는 ng 명령어로 만들 수 없으므로 user.resolve.ts를 직접 만들어 줍시다.
user.resolve.ts 부터 살펴봅시다.
// src/app/user.resolve.ts import { Injectable } from '@angular/core'; import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; import { UserService } from './user.service'; import { User } from './user'; @Injectable() export class UserResolve implements Resolve<User> { constructor( private userService: UserService, ) {} resolve(route: ActivatedRouteSnapshot) { return this.userService.show(route.params['username']); } }
Users.resolve.ts랑 비교해서 user service에서 index가 아니라 show를 호출한다는 점이 다릅니다. 또한 username을 route param에서 가져오고 있습니다. 나중에 route 부분에서 username param을 제공하는 부분을 살펴보겠습니다.
다음은 user-show.component입니다.
// src/app/user-show/user-show.component.ts import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { User } from '../user'; import { AuthService } from '../auth.service'; @Component({ selector: 'app-user-show', templateUrl: './user-show.component.html', styleUrls: ['./user-show.component.css'] }) export class UserShowComponent implements OnInit { user: User; constructor( private route: ActivatedRoute, public authService: AuthService, ) { this.user = this.route.snapshot.data['user']; } ngOnInit() { } }
user를 route에서 가져오고 있습니다.
<!-- src/app/user-show/user-show.component.html --> <div class="page page-users"> <div class="buttons"> <a [routerLink]="['/','users']" class="btn btn-default">Back</a> <a *ngIf="authService.isLoggedIn() && authService.getCurrentUser()._id == user._id" [routerLink]="['/','users', user.username, 'edit']"class="btn btn-default">Edit</a> </div> <form class="user-form form-horizontal"> <div class="contentBox"> <h3 class="contentBoxTop">{{user.username}}</h3> <fieldset disabled> <div class="form-group"> <label for="name" class="col-sm-3">Name</label> <div class="col-sm-9"> <input class="form-control" type="text" id="name" name="name" [value]="user.name"> </div> </div> <div class="form-group"> <label for="email" class="col-sm-3">Email</label> <div class="col-sm-9"> <input class="form-control" type="text" id="email" name="email" [value]="user.email"> </div> </div> </fieldset> </div> </form> </div>
user.show.compoent.css는 비어있습니다.
다음은 user-edit.component를 살펴봅시다.
// src/app/user-edit/user-edit.component.ts import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { User } from '../user'; import { ApiResponse } from '../api-response'; import { UtilService } from '../util.service'; import { UserService } from '../user.service'; import { AuthService } from '../auth.service'; @Component({ selector: 'app-user-edit', templateUrl: './user-edit.component.html', styleUrls: ['./user-edit.component.css'] }) export class UserEditComponent implements OnInit { user: User; errorResponse: ApiResponse; form: FormGroup; formErrors = { 'currentPassword':'', 'username':'', 'name':'', 'email':'', 'newPassword':'', 'passwordConfirmation':'', }; formErrorMessages = { 'username': { 'required': 'Username is required!', 'pattern': 'Should be 4-12 characters!', }, 'currentPassword': { 'required': 'Username is required!', }, 'name': { 'required': 'Name is required!', 'pattern': 'Should be 4-12 characters!', }, 'email': { 'pattern': 'Should be a vaild email address!', }, 'newPassword': { 'pattern': 'Should be minimum 8 characters of alphabet and number combination!', }, 'passwordConfirmation': { 'match': 'Password Confirmation does not matched!', }, }; buildForm(): void { this.form = this.formBuilder.group({ currentPassword:["", [Validators.required]], username:[this.user.username, [Validators.required, Validators.pattern(/^.{4,12}$/)]], name:[this.user.name, [Validators.required, Validators.pattern(/^.{4,12}$/)]], email:[this.user.email, [Validators.pattern(/^[a-zA-Z0-9._%+-][email protected][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)]], newPassword:["", [Validators.pattern(/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,16}$/)]], passwordConfirmation:[""], }, { validator: this.customValidation, }); this.form.valueChanges.subscribe(data => { this.utilService.updateFormErrors(this.form, this.formErrors, this.formErrorMessages); }); }; customValidation(group: FormGroup) { var password = group.get('newPassword'); var passwordConfirmation = group.get('passwordConfirmation'); if(password.dirty && passwordConfirmation.dirty && password.value != passwordConfirmation.value){ passwordConfirmation.setErrors({'match': true}); } } constructor( private route: ActivatedRoute, private router: Router, private formBuilder: FormBuilder, private utilService: UtilService, private userService: UserService, public authService: AuthService, ) { this.user = this.route.snapshot.data['user']; this.buildForm(); } ngOnInit() { } submit() { this.utilService.makeFormDirtyAndUpdateErrors(this.form, this.formErrors, this.formErrorMessages); if(this.form.valid){ this.userService.update(this.user.username, this.form.value) .then(data =>{ this.router.navigate(['/', 'users', this.user.username]); }) .catch(response =>{ this.errorResponse = response; this.utilService.handleFormSubmitError(this.errorResponse, this.form, this.formErrors); }); } } delete() { var answer = confirm("Do you want to delete your account?"); if(answer){ this.userService.destroy(this.user.username) .then(data =>{ this.authService.logout(); }) .catch(response =>{ this.errorResponse = response; this.utilService.handleFormSubmitError(this.errorResponse, this.form, this.formErrors); }); } } }
user-new와 비교해서 form 처리하는 내용이 다르고, 마지막에 user service의 destroy함수를 호출하는 delete 함수가 있습니다.
<!-- src/app/user-edit/user-edit.component.html --> <div class="page page-users"> <div class="buttons"> <a [routerLink]="['/','users',user.username]" class="btn btn-default">Back</a> <span *ngIf="authService.isLoggedIn() && authService.getCurrentUser()._id == user._id" (click)="delete()" class="btn btn-default delete">Delete</span> </div> <form class="user-form form-horizontal" [formGroup]="form" (ngSubmit)="submit()" class="login-form form-horizontal" > <div class="contentBox"> <h3 class="contentBoxTop">Edit User</h3> <fieldset> <div class="form-group" [ngClass]="{'has-error': formErrors.currentPassword}"> <label for="currentPassword" class="col-sm-12 control-label">Current Password*</label> <div class="col-sm-9 col-sm-offset-3"> <input class="form-control" type="password" formControlName="currentPassword" id="currentPassword"> <span *ngIf="formErrors.currentPassword" class="help-block">{{formErrors.currentPassword}}</span> </div> </div> <hr/> <div class="form-group" [ngClass]="{'has-error': formErrors.username}"> <label for="username" class="col-sm-3 control-label">Username*</label> <div class="col-sm-9"> <input class="form-control" type="text" formControlName="username" id="username"> <span *ngIf="formErrors.username" class="help-block">{{formErrors.username}}</span> </div> </div> <div class="form-group" [ngClass]="{'has-error': formErrors.name}"> <label for="name" class="col-sm-3 control-label">Name*</label> <div class="col-sm-9"> <input class="form-control" type="text" formControlName="name" id="name"> <span *ngIf="formErrors.name" class="help-block">{{formErrors.name}}</span> </div> </div> <div class="form-group" [ngClass]="{'has-error': formErrors.email}"> <label for="email" class="col-sm-3 control-label">Email</label> <div class="col-sm-9"> <input class="form-control" type="text" formControlName="email" id="email"> <span *ngIf="formErrors.email" class="help-block">{{formErrors.email}}</span> </div> </div> <div class="form-group" [ngClass]="{'has-error': formErrors.newPassword}"> <label for="newPassword" class="col-sm-12 control-label">New Password</label> <div class="col-sm-9 col-sm-offset-3"> <input class="form-control" type="password" formControlName="newPassword" id="newPassword"> <span *ngIf="formErrors.newPassword" class="help-block">{{formErrors.newPassword}}</span> </div> </div> <div class="form-group" [ngClass]="{'has-error': formErrors.passwordConfirmation}"> <label for="passwordConfirmation" class="col-sm-12 control-label">Password Confirmation</label> <div class="col-sm-9 col-sm-offset-3"> <input class="form-control" type="password" formControlName="passwordConfirmation" id="passwordConfirmation"> <span *ngIf="formErrors.passwordConfirmation" class="help-block">{{formErrors.passwordConfirmation}}</span> </div> </div> <p> <small>*Required</small> </p> </fieldset> <div *ngIf="errorResponse?.message" class="alert alert-danger"> {{errorResponse?.message}} </div> </div> <div class="buttons"> <button type="submit" class="btn btn-default">Submit</button> </div> </form> </div>
user-new.component.htmld에서 수정하여 이 template을 만들었습니다.
/* src/app/user-edit/user-edit.component.css */ .buttons .delete { color: #860505; float: right; }
다음으로 route과 메인 module을 살펴봅시다.
// src/app/app-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AuthGuard } from './auth.guard'; import { UsersResolve } from './users.resolve'; import { UserResolve } from './user.resolve'; import { WelcomeComponent } from './welcome/welcome.component'; import { Error404Component } from './error404/error404.component'; import { LoginComponent } from './login/login.component'; import { UserNewComponent } from './user-new/user-new.component'; import { UserIndexComponent } from './user-index/user-index.component'; import { UserShowComponent } from './user-show/user-show.component'; import { UserEditComponent } from './user-edit/user-edit.component'; const routes: Routes = [ { path: '', component: WelcomeComponent }, { path: 'login', component: LoginComponent }, { path: 'users/new', component: UserNewComponent }, { path: 'users', canActivate: [AuthGuard], children: [ { path: '', component: UserIndexComponent, resolve: { users: UsersResolve, } }, { path: ':username', component: UserShowComponent, resolve: { user: UserResolve } }, { path: ':username/edit', component: UserEditComponent, resolve: { user: UserResolve } }, ] }, { path: '**', component: Error404Component }, ]; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule {}
//...생략 import { UsersResolve } from './users.resolve'; import { UserResolve } from './user.resolve'; //...생략 import { UserIndexComponent } from './user-index/user-index.component'; import { UserShowComponent } from './user-show/user-show.component'; import { UserEditComponent } from './user-edit/user-edit.component'; @NgModule({ declarations: [ //...생략 UserIndexComponent, UserShowComponent, UserEditComponent, ], //...생략 providers: [ //...생략 UsersResolve, UserResolve, ], bootstrap: [AppComponent] }) export class AppModule { }
이번에 생성한 UserResolve, UserShowComponent, UserEditComponent들을 route과 메인 module의 위치에 넣어줍니다.
Angular-CLI 명령어 사용해서 서버를 실행합시다.
ng serve --open
로그인한 후 Users 메뉴를 누르고 username을 눌러봅시다.
해당 user에 대한 정보가 나오고, 로그인 된 본인의 username을 누르면 edit버튼이 보입니다. 이 버튼을 누릅시다.
자신의 정보를 수정하거나 삭제할 수 있습니다.
이것으로 Angular 2 기본사이트 강의가 끝이 났습니다. 수고하셨습니다! 혹시나 이 코드를 기본으로 해서 웹사이트를 제작하고 싶으신 분은 그냥 가져가서 쓰셔도 됩니다.
댓글
이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기