728x90

참고 페이지 : #삽질 없는 개발은 없다

참고 페이지 : #Angular

 

Input Form에 대한 검증하는 기능을 추가하겠습니다.

총 3개의 Input이 있는데, 입력 값에 따라 Issue를 보여줄 수 있도록 합니다.

Web 실행화면

 

더보기

src/app/app.module.ts 수정

작업에 앞서서 먼저 Module를 추가해줍니다.

...
import { FormsModule, ReactiveFormsModule } from '@angular/forms';  // 코드 수정
...

const appRoutes : Routes = [
  ...
]

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    ReactiveFormsModule,  // 코드 추가
    ...
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
  ...
}

 

src/app/user/user.component.ts 수정

...
import { FormGroup, FormBuilder, Validators } from '@angular/forms';  // 코드 수정
...

@Component({
  ...
})
export class UserComponent implements OnInit {

  ...

  complexForm : FormGroup;	// 코드 추가

  constructor(
    public service: UserService,
    private toastr: ToastrService,
    private fbuilder : FormBuilder  // 코드 추가
  ) {
      this.service.getUsers()
      .subscribe((data) => {
        ...
      })

    this.service.selectedUser = {
      ...
    };
    
    // 코드 추가
    // 필드 당 여러개의 유효성 검사기 추가. 
    // 두개 이상 검사하려면 Validators.complose 함수로 검사 항목을 맵핑해야함
    this.complexForm = fbuilder.group({
      "name": [
        null, 
        Validators.compose([
          Validators.required,		// 입력 여부 체크
          Validators.minLength(2),	// 최소 글자수 2개
          Validators.maxLength(20),	// 최대 글자수 20개
          Validators.pattern('[ㄱ-ㅎ|가-힣|a-z|A-Z|0-9|\*]+$')	// 한글, 영문(대소문자), 숫자 포함 여부 체크
        ])
      ],
      "email": [
        null, 
        Validators.compose([
          Validators.required,		// 입력 여부 체크
          Validators.email			// 이메일 형식 체크
        ])
      ],
      "password": [
        null, 
        Validators.compose([
          Validators.required,		// 입력 여부 체크
          // 영문(대소문자), 숫자, 특수문자가 각각 1개 이상 포함하여야 하며, 6글자 이상 입력 여부 체크
          Validators.pattern('(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^*+=-]).{6,}$')
        ])
      ]
    })
  }

  ngOnInit(): void {
  }
  
  ...
}

 

Validators 함수 (링크)

class Validators {
  static min(min: number): ValidatorFn
  static max(max: number): ValidatorFn
  static required(control: AbstractControl): ValidationErrors | null
  static requiredTrue(control: AbstractControl): ValidationErrors | null
  static email(control: AbstractControl): ValidationErrors | null
  static minLength(minLength: number): ValidatorFn
  static maxLength(maxLength: number): ValidatorFn
  static pattern(pattern: string | RegExp): ValidatorFn
  static nullValidator(control: AbstractControl): ValidationErrors | null
  static compose(validators: ValidatorFn[]): ValidatorFn | null
  static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn | null
}

 

 

src/app/user/user.component.html 수정

<div class="container">
  <header>
    ...
  </header>

  <hr>

  <div class="row">
    <div class="col-md-12">
      <h3>Insert User Data</h3>
      <!-- 코드 수정 -->
      <form method="post" #form="ngForm" [formGroup]="complexForm"  (ngSubmit)="onSubmit(form)">
        <input type="hidden" name="id" [(ngModel)]="service.selectedUser.id">
        <div class="form-group">
          <label for="name">Name : </label>
          <!-- 코드 수정 -->
          <input type="text" name="name" id="name" class="form-control" placeholder="insert your name"
            [(ngModel)]="service.selectedUser.name" [formControl]="complexForm.controls['name']">
          <!-- 코드 추가 -->
          <div *ngIf="complexForm.controls['name'].touched && !complexForm.controls['name'].valid" class="alert alert-danger">

            <div *ngIf="complexForm.controls['name'].hasError('required')" >필수 입력사항입니다.</div>

            <div *ngIf="complexForm.controls['name'].hasError('pattern')" >특수문자 및 숫자를 입력하실 수 없습니다.</div>

            <div *ngIf="complexForm.controls['name'].hasError('minlength') || complexForm.controls['name'].hasError('maxlength')" >2~20자 이내로 입력해주세요.</div>

          </div>
        </div>
        <div class="form-group">
          <label for="email">Email : </label>
          <!-- 코드 수정 -->
          <input type="text" name="email" id="email" class="form-control" placeholder="insert your email"
            [(ngModel)]="service.selectedUser.email" [formControl]="complexForm.controls['email']">
          <!-- 코드 추가 -->
          <div *ngIf="complexForm.controls['email'].touched && !complexForm.controls['email'].valid" class="alert alert-danger">
            
            <div *ngIf="complexForm.controls['email'].hasError('required')" >필수 입력사항입니다.</div>

            <div *ngIf="complexForm.controls['email'].hasError('email')" >이메일 형식으로 입력해 주세요.</div>

          </div>

        </div>
        <div class="form-group">
          <label for="password">Password : </label>
          <!-- 코드 수정 -->
          <input type="text" name="password" id="password" class="form-control" placeholder="insert your password"
            [(ngModel)]="service.selectedUser.password" [formControl]="complexForm.controls['password']">
          <!-- 코드 추가 -->
          <div *ngIf="complexForm.controls['password'].touched && !complexForm.controls['password'].valid" class="alert alert-danger">
            
            <div *ngIf="complexForm.controls['password'].hasError('required')" >필수 입력사항입니다.</div>

            <div *ngIf="complexForm.controls['password'].hasError('pattern')" >영문(대소문자), 숫자, 특수문자(!@#$%^*+=-)를 조합하여 8자리 이상 입력해주세요.</div>

          </div>

        </div>

        <div class="form-row">
          <div class="d-grid gap-2 d-md-flex justify-content-md-end">
          	<!-- 코드 수정 -->
            <button class="btn btn-sm btn-block btn-primary col-lg-2" [disabled]="!complexForm.valid">
              submit &nbsp; <fa-icon icon="user-plus"></fa-icon>
            </button>

            <button class="btn btn-sm btn-block btn-secondary col-lg-2" (click)="clearForm()">
              clear &nbsp; <fa-icon icon="undo"></fa-icon>
            </button>
          </div>
        </div>
      </form>
    </div>
  </div>

  <div class="row">
    ...
  </div>


  <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    ...
  </div>

  <br>
  <footer>
    ...
  </footer>
</div>

 

실행화면

Web 실행화면 - 초기화면

초기 실행화면에서는 입력값에 대해 Required Error가 발생되었있기에,

submit 버튼이 비활성화 상태로 되어있습니다.

 

Web 실행화면 - 에러화면

입력폼을 가볍게(?) 터치하고 넘어가면, Required Error로 인해,

문구가 표시되는 것을 볼 수 있습니다.

 

Web 실행화면 - 에러화면

각각의 입력폼에 대한 Error로 표시되는 문구가 다르게 표시되는 이유는,

user.component.html에서 <div> 태그 내 *ngIf를 다르게 지정했기 때문입니다.

 

Web 실행화면 - 성공 화면

입력에 대한 검증에 맞추어 잘 입력해주시면,

Issue 문구도 사라지고, submit 버튼도 활성화 되어집니다.

 

728x90
Posted by 게으른거북
:
728x90

참고영상 : Consumindo uma (GO) API REST com Angular 7 Parte 6 # 30

 

더보기

위 방법이 마음에 안들어 다른 사이트를 참고하였습니다.

참고사이트 : NPM_ngx-toastr (링크)

 


NPM 설치

npm install ngx-toastr --save
npm install @angular/animations --save

 

src/style.css 수정

@import '~ngx-toastr/toastr.css';

 

src/app/app.module.ts 수정

...

// 코드 추가
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    FormsModule,
    FontAwesomeModule,

    // 코드 추가
    BrowserAnimationsModule,
    ToastrModule.forRoot()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
  ...
}

 

src/app/app.component.ts 수정

...

// 코드 추가
import { ToastrService } from 'ngx-toastr';

@Component({
  ...
})
export class AppComponent {
  ...
 
  // 코드 수정
  //constructor(public service: UserService) {
  constructor(public service: UserService, private toastr: ToastrService) {
    ...
  }
  
  // 코드 추가 : toastr을 간편하게 쓰기 위한 함수
  public onToastr(service: string, title: string, message: string) {
    switch(service) {
      case "success": {
        this.toastr.success(message, title, {timeOut: 2000});
        break;
      }
      case "info": {
        this.toastr.info(message, title, {timeOut: 2000});
        break;
      }
      case "warning": {
        this.toastr.warning(message, title, {timeOut: 2000});
        break;
      }
      case "error": {
        this.toastr.error(message, title, {timeOut: 2000});
        break;
      }
      default: {
        console.log("Toast Error");
        break;
      }
    }
  }

  public onForm() {
    ...
  }

  public onSubmit(form: FormGroup) {
 
    console.log(form.value)

    if( form.value.id == null) {

      this.service.postUser(form.value)
        .subscribe((resp) => {
          console.log(resp)

          if(resp["Status"] == 201) {
            this.clearForm();   

            this.service.getUsers()
              .subscribe((data) => {
                // 코드 추가
                this.onToastr("success", "Data Insert", "새로운 User가 등록되었습니다.");

                this.users = data
                this.onForm();
              });
          }
        });
    } else {
      
      this.service.putUser(form.value)
        .subscribe((resp) => {
          console.log(resp);

          if(resp["Status"] == 200) {
            // 코드 추가
            this.onToastr("info", "Data Edit", "User 정보가 수정되었습니다.");

            this.onForm();
            this.clearForm();
            this.updateList(form.value);
          }
        });
    }
  }

  public onEdit(id: string) {
    ...
  }

  public updateList(user: any) {
    ...
  }

  public deleteConfirm(id: string) {
    ...
  }

  public cancelDelete() {
    ...
  }

  public onDelete() { 

    if(this.userId != null) {
      this.service.deleteUser(this.userId)
        .subscribe((resp) => {
          console.log(resp);
  
          if(resp["Status"] == 200) {
            // 코드 추가
            this.onToastr("error", "Data Delete", "User 정보가 삭제되었습니다.");

            this.users = this.users.filter((user) => user.id != this.userId);
            
            this.cancelDelete();
            this.onForm();
          }
        });
    }
  }

  public clearForm() {
    ...
}

 

Web 결과화면
728x90
Posted by 게으른거북
:
728x90

참고영상 : Consumindo uma (GO) API REST com Angular 7 Parte 5 # 29

 

더보기

src/app/user.service.ts 수정

...

export class UserService {
  ...
 
  constructor(private http: HttpClient) {
 
    this.getUsers()
      .subscribe((data) => {

        // 코드 수정
        if(data.length > 0) {
          this.nextUserId = (data[ data.length -1].id +1);
          console.log("ID disponivel : " + this.nextUserId);
        }
      });
  }
 
  public getUsers() {
    ...
  }

  public getUser(id: string) {
    ...
  }

  public postUser(user: any) {
    ...
  }

  public putUser(user: any) {
    ...
  }

  public deleteUser(id: string) {
    ...
  }
}

 

src/app/app.component.ts 수정

...

export class AppComponent {
  ...

  // 코드 추가
  userId = null;  // 현재 지정된 user를 파악하기 위한 변수
  displayForm : boolean = false;  // user 데이터가 비었을때를 대비한 변수
 
  constructor(public service: UserService) {
    this.service.getUsers()
      .subscribe((data) => {
        this.users = data;
        console.log(this.users);

        // 코드 추가
        this.onForm();
      })

    this.service.selectedUser = {
      ...
    };

  }
  
  // 코드 추가 : user 데이터가 1건 이상이면, 테이블 표시
  public onForm() {
    
    if(this.users.length > 0) {
      this.displayForm = true;
      return;
    }

    this.displayForm = false;
  }

  public onSubmit(form: FormGroup) {
 
    console.log(form.value)

    if( form.value.id == null) {
      this.service.postUser(form.value)
        .subscribe((resp) => {
          console.log(resp)

          if(resp["Status"] == 201) {
            this.clearForm();   

            this.service.getUsers()
              .subscribe((data) => {
                this.users = data

                // 코드 추가
                this.onForm();
              });
          }
        });
    } else {
      this.service.putUser(form.value)
        .subscribe((resp) => {
          console.log(resp);

          if(resp["Status"] == 200) {
            // 코드 추가
            this.onForm();

            this.clearForm();
            this.updateList(form.value);
          }
        });
    }
  }

  public onEdit(id: string) {
    ...
  }

  public updateList(user: any) {
    ...
  }

  // 코드 추가 : 호출된 user의 id를 저장
  public deleteConfirm(id: string) {
    this.userId = id;
  }

  // 코드 추가 : 저장된 user의 id를 초기화
  public cancelDelete() {
    this.userId = null;
    console.log("Cancel User Delete");
  }

  // 코드 수정
  public onDelete() { 

    if(this.userId != null) {
      //this.service.deleteUser(id)
      this.service.deleteUser(this.userId)
        .subscribe((resp) => {
          console.log(resp);
  
          if(resp["Status"] == 200) {
  
            //this.users = this.users.filter((user) => user.id != id);
            this.users = this.users.filter((user) => user.id != this.userId);
            
            this.cancelDelete();
            this.onForm();
          }
        });
    }
  }

  public clearForm() {
    ...
  }

}

 

src/app/app.component.html

<div class="container">
  <header>
    ...
  </header>

  <hr>

  <div class="row">
    <div class="col-md-12">
      <h3>Insert User Data</h3>
      <form method="post" #form="ngForm" (ngSubmit)="onSubmit(form)">
        ...
      </form>
    </div>
  </div>

  <br>
  <div class="row">
    <!-- 코드 추가 : user 데이터가 없을때, 안내문 표시-->
    <div clas="col-md-12" *ngIf="!displayForm">
      <p class="alert alert-warning text-center" >
        호출 가능한 User 데이터가 없습니다. <br>
        새로운 User 를 등록해주세요.
      </p>
    </div>

    <!-- 코드 수정 : user 데이터가 1건 이상일 경우 내용 표시-->
    <div clas="col-md-12" *ngIf="displayForm">

      <h3>User Data List</h3>

      <table class="table table-bordered table-hover text-center">
        <thead>
          <tr>
            ...
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let user of users">
            ...
            <td>
              <!-- 코드 수정 : Call Modal-->
              <button type="button" class="btn btn-sm btn-danger col-block col-lg-8" (click)="onDelete(user.id)" data-bs-toggle="modal" data-bs-target="#exampleModal" (click)="deleteConfirm(user.id)">
                <fa-icon icon="user-minus"></fa-icon>
              </button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>


  <!-- 코드 추가 : Modal -->
  <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">User Data Delete</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="cancelDelete()"></button>
        </div>
        <div class="modal-body">
          회원 <strong class="text-danger"> #{{ userId }} </strong> 번의  정보를 삭제 하시겠습니까?
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" (click)="cancelDelete()">
            Cancle
          </button>

          <button type="button" class="btn btn-danger" data-bs-dismiss="modal" (click)="onDelete()">
            Delete
          </button>
        </div>
      </div>
    </div>
  </div>

  <br>
  <footer>
    ...
  </footer>
</div>

 

Web 결과화면 - No User Data

 

Web 결과화면 - Modal
728x90
Posted by 게으른거북
:
728x90

참고영상 : Consumindo uma (GO) API REST com Angular 7 Parte 5 # 29

 

더보기

src/app/app.module.ts 수정

버튼에 대한 아이콘 추가를 위해 아이콘 Module Import를 진행합니다.

...

// 코드 추가 : icon
import { faUserMinus } from '@fortawesome/free-solid-svg-icons';
import { faUserPlus } from '@fortawesome/free-solid-svg-icons';
import { faUndo } from '@fortawesome/free-solid-svg-icons';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
  
  constructor(library: FaIconLibrary) {
    // 코드 수정
    library.addIcons(faEdit, faUserMinus, faUserPlus, faUndo);
  }
}

 

src/app/user.service.ts 수정

...
export class UserService {
   ...
 
  constructor(private http: HttpClient) {
    ...
  }
 
  public getUsers() {
    ...
  }

  public getUser(id: string) {
    ...
  }

  public postUser(user: any) {
    ...
  }

  public putUser(user: any) {
    ...
  }

  // 코드 추가
  public deleteUser(id: string) {
    return this.http.delete(`${this.Uri}/${id}`);
  }
}

 

src/app/app.component.ts 수정

...

export class AppComponent {
  ...
 
  constructor(public service: UserService) {
    ...
  }

  public onSubmit(form: FormGroup) {
    ...
  }

  public onEdit(id: string) {
    ...
  }
  
  public updateList(user: any) {
    ...
  }

  // 코드 추가 : user 정보 삭제
  public onDelete(id: string) { 

    this.service.deleteUser(id)
      .subscribe((resp) => {
        console.log(resp);

        if(resp["Status"] == 200) {

          this.users = this.users.filter((user) => user.id != id);
        }
      });
  }

  public clearForm() {
    ...
}

 

src/app/app.component.html 수정

<div class="container">
  <header>
    ...
  </header>

  <hr>

  <div class="row">
    <div class="col-md-12">
      <h3>Insert User Data</h3>
      <form method="post" #form="ngForm" (ngSubmit)="onSubmit(form)">
        ...
 
        <div class="form-row">
          <div class="d-grid gap-2 d-md-flex justify-content-md-end">
            <!-- 코드 수정 : icon 추가 -->
            <button class="btn btn-sm btn-block btn-primary col-lg-2" [disabled]="!form.valid" >
              submit &nbsp; <fa-icon icon="user-plus"></fa-icon>
            </button>

            <!-- 코드 수정 : icon 추가 -->
            <button class="btn btn-sm btn-block btn-secondary col-lg-2" (click)="clearForm()">
              clear &nbsp; <fa-icon icon="undo"></fa-icon>
            </button>
          </div>
        </div>
      </form>
    </div>
  </div>

  <br>
  <div class="row">
    <div clas="col-md-12" *ngIf="users">

      <h3>User Data List</h3>
      
      <table class="table table-bordered table-hover text-center">
        <thead>
          <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Email</th>
            <th>Password</th>
            <th>Edit</th>
            <!-- 코드 수정 -->
            <th>Delete</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let user of users">
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.email }}</td>
            <td>{{ user.password }}</td>
            <td>
              <button type="button" class="btn btn-sm btn-info col-block col-lg-8" (click)="onEdit(user.id)">
                <fa-icon icon="edit"></fa-icon>
              </button>
            </td>
            <!-- 코드 수정 : delete 추가 -->
            <td>
              <button type="button" class="btn btn-sm btn-danger col-block col-lg-8" (click)="onDelete(user.id)">
                <fa-icon icon="user-minus"></fa-icon>
              </button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>

  <br>
  <footer>
    <p class="alert text-center"></p>
  </footer>
</div>

 

Web 결과화면
728x90
Posted by 게으른거북
:
728x90

참고영상 : Consumindo uma (GO) API REST com Angular 7 Parte 4 # 28

 

더보기

폰트 변경

Google Font (링크)

Google Font 페이지

 

index.html 에 추가

<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Montserrat" rel="stylesheet">

 

src/app/app.component.css 에 추가

.container {
    font-family: 'Montserrat', sans-serif;
}

 

src/app/app.service.ts 수정

...

export class UserService {
  ...
 
  constructor(private http: HttpClient) {
    ...
  }
 
  public getUsers() {
    ...
  }

  public getUser(id: string) {
    ...
  }

  public postUser(user: any) {
    ...
  }

  // 코드 추가
  public putUser(user: any) {

    let data = {
      "id"        :   user.id,
      "name"      :   user.name,
      "email"     :   user.email,
      "password"  :   user.password
    };

    return this.http.put(`${this.Uri}/${user.id}`, JSON.stringify(data));
  }
}

 

src/app/app.component.ts 수정

...

export class AppComponent {
  ...
 
  constructor(public service: UserService) {
    ...
  }

  public onSubmit(form: FormGroup) {
 
    console.log(form.value)

    if( form.value.id == null) {
      this.service.postUser(form.value)
        .subscribe((resp) => {
          console.log(resp)

          if(resp["Status"] == 201) {
            this.clearForm();   // 코드 추가
            this.service.getUsers()
              .subscribe((data) => this.users = data);
          }
        });
    } else {
      // 코드 추가 : user 데이터 수정
      this.service.putUser(form.value)
        .subscribe((resp) => {
          console.log(resp);

          if(resp["Status"] == 200) {
            this.clearForm();
            this.updateList(form.value);
          }
        });

    }
  }

  // 코드 추가 : user 데이터를 Form에 전달
  public onEdit(id: string) {

    this.service.getUser(id)
      .subscribe((data) => {
        this.service.selectedUser = data;
      });
  }
  
  // 코드 추가 : 테이블 내 user 데이터 갱신
  public updateList(user: any) {
    for(var i = 0; i < this.users.length; i++) {
      if(user.id == this.users[i].id) {
        this.users[i] = user;
        return;
      }
    }
  }

  // 코드 추가 : Form 비우기
  public clearForm() {
    this.service.selectedUser = {
      "id": null,
      "name": '',
      "email": '',
      "password": ''
    };
  }
}

 

Web 결과화면
728x90
Posted by 게으른거북
: