티스토리 뷰
Spring Boot + Angular : Shopping Site 만들기 v4 - 3 - 카트 상세 페이지 및 FormGroup 주문페이지 작성 - Rest Repository with JPA
Korean Eagle 2020. 6. 3. 00:330. 이 포스트에는 주문 페이지를 작성한다.
1. 주문 페이지를 위한 컴포넌트를 생성하고 라우팅테이블을 업데이트 한다.
$ ng generate component components/checkout
const routes: Routes = [
{ path: 'checkout', component: CheckoutComponent },
{ path: 'cart-details', component: CartDetailsComponent },
{ path: 'search/:keyword', component: ProductListComponent },
{ path: 'category/:id/:name', component: ProductListComponent },
{ path: 'category', component: ProductListComponent },
{ path: 'products/:id', component: ProductDetailsComponent },
{ path: 'products', component: ProductListComponent },
{ path: '', component: ProductListComponent },
{ path: '**', component: ProductListComponent }
]
2. form control을 사용하기 위한 모듈을 import한다.
2-1 이 포스트에서는 FormGroup을 사용하기 때문에 ReactiveFormsModule을 app.module.ts에 추가한다.
2-2 FormsModule도 같이 import해야 일반 form 테그에 에러가 생기지 않는다.
2-2-1 기본적으로 ReactiveFormsModule을 import하면 모든 form에 FormGroup이 지정되어야 하기 때문이다.
2-2-2 따라서 FormsModule도 같이 import하여 그런 경우가 허용되도록 해야 한다.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Component } from '@angular/core';
import { AppComponent } from './app.component';
import { ProductListComponent } from './components/product-list/product-list.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule } from '@angular/common/http';
import { ProductCategoryMenuComponent } from './components/product-category-menu/product-category-menu.component';
import { ProductSearchComponent } from './components/product-search/product-search.component'
import { RouterModule, Routes } from '@angular/router';
import { ProductDetailsComponent } from './components/product-details/product-details.component';
import { CartInfoComponent } from './components/cart-info/cart-info.component';
import { CartDetailsComponent } from './components/cart-details/cart-details.component';
import { CheckoutComponent } from './components/checkout/checkout.component'
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
const routes: Routes = [
{ path: 'checkout', component: CheckoutComponent },
{ path: 'cart-details', component: CartDetailsComponent },
{ path: 'search/:keyword', component: ProductListComponent },
{ path: 'category/:id/:name', component: ProductListComponent },
{ path: 'category', component: ProductListComponent },
{ path: 'products/:id', component: ProductDetailsComponent },
{ path: 'products', component: ProductListComponent },
{ path: '', component: ProductListComponent },
{ path: '**', component: ProductListComponent }
]
@NgModule({
declarations: [
AppComponent,
ProductListComponent,
ProductCategoryMenuComponent,
ProductSearchComponent,
ProductDetailsComponent,
CartInfoComponent,
CartDetailsComponent,
CheckoutComponent
],
imports: [
BrowserModule,
HttpClientModule,
ReactiveFormsModule,
FormsModule,
RouterModule.forRoot(routes),
NgbModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
3. 화면구성하기 위해 다음과 같이 checkout.component.html을 작성한다.
3-1 길긴한데 내용이 별로 없다. FormGroup과 form테그를 매핑하는 부분이 대부분이다.
3-2 구조는 하나의 checkoutFormcontrol 내에 customer, creditCard, shippingAddress, billingAddress이 정의되었다.
3-3 각 요소들도 모두 FormGroup이기 때문에 중첩구조를 이룬다.
3-4 form테그에는 최상위 FormGroup을 지정하고
3-4-1 각 formGroup마다 별도의 formGroupName, formControlName이 지정된다.
3-5 중간에 shippingAddress와 billingAddress가 동일할 경우 복사하는 이벤트를 위해 onChangeCheckBox가 있다.
3-6 제일 아래를 보면 총가격과 총수량을 표출하므로 이 컴포넌트도 CartService가 필요하다는 것을 알수 있다.
3-7 카드 만료기간을 1~12개월을 표시하기 위해 단순한 반복을 구현하기 위해 range라는 메소드를 작성하였다.
<div class="section-content section-content-p30">
<div class="container">
<form [formGroup]="checkoutFormGroup" (ngSubmit)="onSubmit()">
<div class="form-area" formGroupName="customer">
<h3>Customer</h3>
<div class="row">
<div class="col">
<div class="input-space">
<input type="text" placeholder="First Name" formControlName="firstName">
</div>
</div>
<div class="col">
<div class="input-space">
<input type="text" placeholder="Last Name" formControlName="lastName">
</div>
</div>
</div>
<div class="row">
<div class="clearfix"></div>
<div class="col">
<div class="input-space">
<input type="text" placeholder="Email Address" formControlName="email">
</div>
</div>
</div>
</div>
<!--Form 2-->
<div class="form-area" formGroupName="shippingAddress">
<h3>Shipping Address</h3>
<div class="row">
<div class="col-md-12">
<div class="input-space">
<input type="text" placeholder="Street" formControlName="street">
</div>
</div>
<div class="col-md-12">
<div class="input-space">
<input type="text" placeholder="City" formControlName="city">
</div>
</div>
<div class="col-md-12">
<div class="input-space">
<select formControlName="state">
<option>State / Province</option>
<option>Gyeunggi-do</option>
<option>Seoul</option>
<option>Busan</option>
<option>Daegu</option>
</select>
</div>
</div>
<div class="col-md-12">
<div class="input-space">
<input type="text" placeholder="Zip Code / Postal Code" formControlName="zip">
</div>
</div>
</div>
</div>
<div class="input-space">
<label class="au-checkbox">
<input type="checkbox" (change)="onChangeCheckbox($event.target.checked)">
<span class="au-checkmark"></span> Bill Address same as Shipping Adress
</label>
</div>
<div class="form-area" formGroupName="billingAddress">
<h3>Billing Address</h3>
<div class="row">
<div class="col-md-12">
<div class="input-space">
<input type="text" placeholder="Street" formControlName="street">
</div>
</div>
<div class="col-md-12">
<div class="input-space">
<input type="text" placeholder="City" formControlName="city">
</div>
</div>
<div class="col-md-12">
<div class="input-space">
<select formControlName="state">
<option>State / Province</option>
<option>Gyeunggi-do</option>
<option>Seoul</option>
<option>Busan</option>
<option>Daegu</option>
</select>
</div>
</div>
<div class="col-md-12">
<div class="input-space">
<input type="text" placeholder="Zip Code / Postal Code" formControlName="zip">
</div>
</div>
</div>
</div>
<div class="form-area" formGroupName="creditCard">
<h3>Credit Card</h3>
<div class="row">
<div class="col-md-12">
<div class="input-space">
<select>
<option>Visa</option>
<option>Master</option>
<option>Union Pay</option>
<option>Paypal</option>
</select>
</div>
</div>
<div class="col-md-12">
<div class="input-space">
<input type="text" placeholder="Name on card" formControlName="nameOnCard">
</div>
</div>
<div class="clearfix"></div>
<div class="col-md-8">
<div class="input-space">
<input type="text" placeholder="Card number" formControlName="cardNumber">
</div>
</div>
<div class="col-md-4">
<div class="input-space">
<input type="text" placeholder="CVV2 number" formControlName="cvv">
</div>
</div>
<div class="clearfix"></div>
<div class="col-md-2"> <label class="date">Expiration Date</label></div>
<div class="col-md-5">
<div class="input-space">
<select placeholder="Month">
<option *ngFor="let item of range(12); let i = index">{{i+1}}</option>
</select>
</div>
</div>
<div class="col-md-5">
<div class="input-space">
<select>
<option>2020</option>
<option *ngFor="let item of range(5); let i = index">{{i+2020}}</option>
</select>
</div>
</div>
</div>
</div>
<div class="form-area">
<h3>Review Your Order</h3>
<b>Total Quantity: {{totalQuantity}}</b>
<h4>Shipping: FREE</h4>
<B>Total Price: {{totalPrice | currency: 'USD'}}</B>
</div>
<div class="text-center">
<button class="btn btn-info">Purchase</button>
</div>
</form>
</div>
</div>
4. 마지막으로 checkout.component.ts 클래스이다.
4-1 가장 중요한 부분은 역시 FormGroup 정의 부분이고 FormBuilder 객체를 주입받아서 사용하고 있다.
4-2 총가격과 총수량 정보를 받기 위해 CartService를 주입받고 있고 초기화 때 구독 후 값을 요청하고 있다.
4-3 range 메소드는 단순히 인자의 숫자 크기의 배열을 생성하여 반환한다. 즉 그 숫자만큼 반복하기 위한 함수이다.
4-4 onChangeCheckbox는 체크박스의 체크 여부 값을 받아서 checked되었을 때 복사하는 기능을 구현하였다.
4-5 자세한 메소드와 속성은 아래 링크를 참조한다.
Angular
angular.io
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { CartService } from 'src/app/services/cart.service';
@Component({
selector: 'app-checkout',
templateUrl: './checkout.component.html',
styleUrls: ['./checkout.component.css']
})
export class CheckoutComponent implements OnInit {
_range: number
checkoutFormGroup: FormGroup
totalPrice: number = 0.00
totalQuantity: number = 0
constructor(
private cartService: CartService,
private formBuilder: FormBuilder) { }
ngOnInit(): void {
this.cartService.totalPrice.subscribe(data=> this.totalPrice = data)
this.cartService.totalQuantity.subscribe(data=> this.totalQuantity = data)
this.cartService.computeTotals()
this.checkoutFormGroup = this.formBuilder.group({
customer: this.formBuilder.group({
firstName: [''],
lastName: [''],
email: ['']
}),
shippingAddress: this.formBuilder.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
billingAddress: this.formBuilder.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
creditCard: this.formBuilder.group({
cardType: [''],
nameOnCard: [''],
cardNumber: [''],
cvv: [''],
expirationMonth: [''],
expirationYear: ['']
})
})
}
range(i: number) {
return new Array(i)
}
onChangeCheckbox(checked: boolean) {
if (checked === true) {
this.checkoutFormGroup.get('billingAddress').setValue(this.checkoutFormGroup.get('shippingAddress').value)
} else {
this.checkoutFormGroup.get('billingAddress').reset()
}
}
onSubmit() {
console.log(this.checkoutFormGroup.get("customer").value);
console.log(this.checkoutFormGroup.get("shippingAddress").value);
console.log(this.checkoutFormGroup.get("billingAddress").value);
console.log(this.checkoutFormGroup.get("creditCard").value);
}
}
5. 결과화면
'Demos > Shopping mall' 카테고리의 다른 글
- Total
- Today
- Yesterday
- 도커 개발환경 참고
- AWS ARN 구조
- Immuability에 관한 설명
- 자바스크립트 멀티 비동기 함수 호출 참고
- WSDL 참고
- SOAP 컨슈머 참고
- MySql dump 사용법
- AWS Lambda with Addon
- NFC 드라이버 linux 설치
- electron IPC
- mifare classic 강의
- go module 관련 상세한 정보
- C 메모리 찍어보기
- C++ Addon 마이그레이션
- JAX WS Header 관련 stackoverflow
- SOAP Custom Header 설정 참고
- SOAP Custom Header
- SOAP BindingProvider
- dispatcher 사용하여 설정
- vagrant kvm으로 사용하기
- git fork, pull request to the …
- vagrant libvirt bridge network
- python, js의 async, await의 차이
- go JSON struct 생성
- Netflix Kinesis 활용 분석
- docker credential problem
- private subnet에서 outbound IP 확…
- 안드로이드 coroutine
- kotlin with, apply, also 등
- 안드로이드 초기로딩이 안되는 경우
- navigation 데이터 보내기
- 레이스 컨디션 navController
- raylib
- 자바
- login
- RestTemplate
- one-to-many
- 설정
- Security
- Spring Security
- Many-To-Many
- Angular
- Spring
- hibernate
- 설정하기
- 상속
- spring boot
- Validation
- one-to-one
- jsp
- crud
- 로그인
- WebMvc
- 스프링부트
- XML
- 매핑
- form
- mapping
- 하이버네이트
- MYSQL
- 외부파일
- 스프링
- Rest