Template Driven
-HTML
form태그 (ngSubmit)='onAddItem(f)" #f="ngForm"
앵귤러 submit과, 로컬레퍼런스 f를 선언합니다.
Validation 속성 : pattern을 정규식, required가 있습니다.
<!-- child HTML for form -->
<form (ngSubmit)="onAddItem(f)" #f="ngForm">
<div class="row">
<div class="col-sm-2 form-group">
<label for="amount">Amount</label>
<input
type="number"
id="amount"
class="form-control"
name="amount"
ngModel
required
pattern="^[1-9]+[0-9]*$"
>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-success" type="submit" [disabled]="!f.valid">Add</button>
<button class="btn btn-danger" type="button">Delete</button>
<button class="btn btn-primary" type="button">Clear</button>
</div>
</div>
</form>
-TS
// child TS
onAddItem(form: NgForm) {
const value = form.value;
const newIngredient = new Ingredient(value.amount);
this.slService.addIngredient(newIngredient);
}
NgForm을 form.value로 값에 접근할 수 있습니다.
아이템 값 불러오기 - Index이용.
1. Array에서 인덱스 값을 가져옴.
2. service를 이용하여 인덱스 값을 넘김.
3. subscribe로 child에 값을 받음.
// service.ts
startedEditng = new Subject<number>();
서비스에 subscribe할 수 있도록 Subject를 만들고, index는 number이기 때문에 제네릭 타입을 number로 지정.
-HTML
<!-- parent HTML -->
<ul class="list-group">
<a
class="list-group-item"
style="cursor: pointer"
*ngFor="let ingredient of ingredients; let i = index"
(click)="onEditItem(i)"
>
{{ ingredient.name }} ({{ ingredient.amount }})
</a>
</ul>
*ngFor에 let i = index로 index를 지정함.
(click)이벤트를 이용하여 index값을 ts로 넘김.
// parent TS
onEditItem(index: number) {
this.slService.startedEditng.next(index);
}
child의 ngOnInit()에서 subscribe로 해당 HTML에 값을 입력합니다.
// child TS
@ViewChild('f') slForm: NgForm; // 랜더링에 맞는 view를 가져오기 위함.
subscription: Subscription;
editedItemIndex: number;
editedItem: Ingredient;
// editMode = false;
constructor(private slService: ShoppingListService) { }
ngOnInit() {
this.subscription = this.slService.startedEditng
.subscribe(
(index: number) => {
// this.editMode = true;
this.editedItemIndex = index;
this.editedItem = this.slService.getIngredient(index);
this.slForm.setValue({
name: this.editedItem.name,
amount: this.editedItem.amount
})
}
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
Subscription을 만들어 둡니다. (destroy를 위해...)
1. startedEditing을 subscribe합니다: 해당 parameter였던 index를 이용하여 값을 설정합니다.
2. index를 이용하여 service에서 Array[index]의 값을 받아옵니다.
ViewChild로 되어있는 NgForm에 SetValue()를 이용하여 값을 설정해 줍니다.
어레이 아이템 삭제하기 - view단에서 실수 요소 차단하기
아이템 값 불러오기 - Index이용. 에서 editedItemIndex와 같은 변수를 매소드의 const로 쓰지 않아서 이상하다고 생각하셨죠?
주석으로 된 editMode를 해제하고 아래와 같은 HTML을 생성합니다.
<button
class="btn btn-danger"
type="button"
(click)="onDelete()"
*ngIf="editMode"
>Delete</button>
ngIf로, editMode일 때만 활성화 하도록 합니다.
onDelete() {
this.slService.deleteIngredient(this.editedItemIndex);
this.onClear();
}
그렇게 되면, TS파일 내부에서 editMode를 따로 확인할 필요 없으면서, 클라이언트의 실수를 줄일 수 있습니다.
물론 코딩하는 스타일마다 다르지만 지금은 Template Driven입니다. 매우 유용한 스킬이라 생각됩니다.
ReactiveFormsModule
HTML에
FormGorup 바인딩 - [formGroup]="formName"
<form [formGroup]="recipeForm" (ngSubmit)="onSubmit()"> ... </form>
FormControl 바인딩 - formControlName 속성
<input
type="text"
id="name"
formControlName="name"
class="form-control">
버튼을 눌렀을 때, 중복이 submit이 될 때 - type="button"
<button
type="button"
class="btn btn-success"
(click)="onAddIngredient()"
>Add Ingredient</button>
Fixing a Bug
form array의 controls에 접속하는 코드에서 문제가 발생할 수 있습니다:
*ngFor="let ingredientCtrl of recipeForm.get('ingredients').controls; let i = index"
이 코드는 Angular 최신버전에서 실패 할겁니다.
.ts 파일에서 매소드를 만들어 controls를 얻을 수 있습니다.
getControls() {
return (<FormArray>this.recipeForm.get('ingredients')).controls;
}
템플릿에서는 이렇게 작성해 주셔야 합니다:
*ngFor="let ingredientCtrl of getControls(); let i = index"
대체방안으로, getter를 사용하는 캐스팅 구문을 사용할 수 있습니다:
get controls() {
return (this.recipeForm.get('ingredients') as FormArray).controls;
}
그리고 템플릿은 이렇게 변경해주세요:
*ngFor="let ingredientCtrl of controls; let i = index"
image 미리보기 기능
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="imagePath">Image URL</label>
<input
type="text"
id="imagePath"
formControlName="imagePath"
class="form-control"
#imagePath>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<img [src]="imagePath.value" class="img-responsive">
</div>
</div>
로컬 레퍼런스 #imagePath
[src]="imagePath.value" 이렇게 사용합니다.
FromArray의 모든 아이템 삭제하기
Angular 8에서, 모든 아이템을 제거하는 새로운 방식이 있습니다.
(<FormArray>this.recipeForm.get('ingredients')).clear();
clear()매쏘드는 FormControls(혹은 FormGorups)에 등록된 모든 곳을 돌면서 자동적으로 제거해 줍니다.
removeAt()으로 일일이 루프를 돌면서 할 필요 없습니다.
'프로그래밍 > Angular' 카테고리의 다른 글
Http (0) | 2019.08.20 |
---|---|
Pipe (0) | 2019.08.15 |
FormHandling - Reavice (0) | 2019.07.02 |
Handling Form - Template Driven (0) | 2019.07.02 |
Observable & Subject (0) | 2019.06.26 |