반응형

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

+ Recent posts