Angular - @Input, @output, ngFor和ngIf

  • 806
  • 0
  • 2018-05-15

實現@Input, @output, ngFor和ngIf 四個功能

情境說明:

在app.component.ts 內定義要提供其他Component共用的 物件(TodoItem)陣列 :todoItmes,

把這個物件傳遞到 component :todo-items 讓他呈現在畫面上,

在component :todo-items 畫面上操作刪除的時候,

要binding 回 app.component.ts內提供的Function 將app.component.ts內定義的物件陣列內的值給移除

最後,透過add-form.component.ts 畫面上的新增對話框可以新增TodoItem至 app.component.ts內的物件陣列

實際新增的動作一樣是由 app.component.ts 提供。

實作:

在app.component.ts內定義要共用的TodoItem 物件陣列 :todoItmes,

todoItems: TodoItem[] = [
    {
      id: 1,
      value: 'Todo Item NO.1',
      done: false
    },
    {
      id: 2,
      value: 'Todo Item NO.2',
      done: true
    },
    {
      id: 3,
      value: 'Todo Item NO.3',
      done: false
    }
  ];

將AppComponent內定義好的物件陣列傳給todo-Items.component

開啟app.component.html,們將items視為一個屬性,並且把todoItems傳進去。

-- 使用@Input接收傳入Component的資料

接下來我們打開src/app/todo-items/todo-items.component.ts,


import { Component, OnInit, Input } from '@angular/core';
import { TodoItem } from '../shared/todo-item';

@Component({
  selector: 'app-todo-items',
  templateUrl: './todo-items.component.html',
  styleUrls: ['./todo-items.component.css']
})
export class TodoItemsComponent implements OnInit {

  constructor() { }

  //使用@Input接收傳入 todo-Items這個 Component的陣列資料
  @Input() items: TodoItem[];

  //如果要將items 更改別名,可用這個寫法
  //@Input("items") NewItemsName:TodoItem[];

  ngOnInit() {
  }

}

使用ngFor在todo-items.component.html內來列舉剛剛收到的資料

使用ngIf來顯示/隱藏資料

使用[(ngModel)] 來綁定item.done(雙向連動),畫面上點選這個checkbox的時候,定義在app.component.ts內的物件陣列會連動改變資料

<ul>
  <li *ngFor="let item of items">
    <label for="chk_{{item.id}}">
      <input type="checkbox" name="" id="chk_{{item.id}}" [(ngModel)]="item.done" [checked]="item.done">{{item.value}}
    </label>
    <a href="#">&nbsp; Delete</a>
    <span *ngIf="item.done">(已完成)</span>
  </li>
</ul>

 

 

使用@Output傳遞事件

子元件要透過 event binding 告訴父元件事件被觸發了

首先我們先在app.component.ts(父元件)中寫好要加入TodoItem的函數

addTodoItem(text) {
    this.todoItems.push({
      id: (new Date()).getTime(),
      value: text,
      done: false
    });
  };

打開add-form.component.ts(子元件),要使用@Output之前需要先import 兩個參考 Output, EventEmitter

使用 @Output() 的時候 要先import Output

使用EventEmitter 的時候,要先import EventEmitter (會有好幾個,一定要從@angular/core 匯入)

//要使用Output 之前要記得import Output, EventEmitter
import { Component, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-add-form',
  templateUrl: './add-form.component.html',
  styleUrls: ['./add-form.component.css']
})
export class AddFormComponent implements OnInit {

  placeHolderText = "請輸入待辦事項!";
  todoText = '';

  constructor() { }
  
  //先定義addTodoItem這個component的property是一個要輸出(output)的東西
  @Output() AddTodoItem = new EventEmitter();

  //Button:btnAdd的click事件
  addTodo($event) { 
    //實際觸發Output的事件,是定義再btnAdd的click事件內   
    this.AddTodoItem.emit(this.todoText);
  }

  ngOnInit() {
  }
}

藉由AddTodoItem.emit(this.todoText)來把事件發射出去,並且參數內容為this.todoText(畫面上輸入的內容),

 

接著到app.component.html 內修改 <app-add-form></app-add-form>

<app-add-form (AddTodoItem)="addTodoItem($event)"></app-add-form>

加入這行 (AddTodoItem)="addTodoItem($event)"

左邊的AddTodoItem 是 event binding 綁定addTodoItem事件到 add-form.component.ts(子元件) 裡面一個輸出(@Output())的物件

右邊的addTodoItem($event) 是app.component.ts(父元件) 內剛剛定義好要加入TodoItem的函數

傳遞到 app.component.ts的函數addTodoItem,將資料新增。

這樣,就可以自行新增資料

===

接著我想實作刪除TodoItem

先在app.component.ts(父元件) 中寫好要加入要刪除TodoItem的函數

delTodoItem(paraObject) {
    this.todoItems = this.todoItems.filter(function (ele) {
      return (ele !== paraObject);
      //return ((ele.id != paraObject.id) && (ele.value != paraObject.value));
    });
  };

接著到todo-items.component.ts (子元件) 內定義@Output() 參數

@Output() DeleteTodoItem = new EventEmitter();

修改子元件的template: todo-items.component.html ,新增Delete 的Click 事件

<ul>
  <li *ngFor="let item of items">
    <label for="chk_{{item.id}}">
      <input type="checkbox" name="" id="chk_{{item.id}}" [(ngModel)]="item.done" [checked]="item.done">{{item.value}}
    </label>
    <a href="#" (click)="delTodoItem(item)">&nbsp; Delete</a>
    <span *ngIf="item.done">(已完成)</span>
  </li>
</ul>

再回到todo-items.component.ts 內定義click function:delTodoItem

 delTodoItem(item: TodoItem) {
    this.DeleteTodoItem.emit(item);
  }

最後將Output參數綁定到app-todo-item

<app-todo-items [items]="todoItems" (DeleteTodoItem)="delTodoItem($event)"></app-todo-items>

這樣就可以把todo-Item 刪除了

結論:

簡單的說 app.component.ts 、app.component.html 會去控制我新增的三個component

這個範例會用到的todoItems這個陣列,實體是放在app.component.ts內,

透過property binding 將陣列傳給child component 去顯示,

如果child component要新增或是移除陣列內的物件,可透過event binding 來搭配app.component.ts內的function控制這個陣列的資料

 

補充:

關於@Input()

加上中括號的話代表綁定的是一個變數,如果沒有加的話代表綁定的是字串!

舉例來說:

<app-todo-items [items]="todoItems" iText="DemoInput"></app-todo-items>

todoItems 是一個變數,items 這個@Input() 接收到的是這個變數的資料(有可能是陣列,看todoItems 這個變數怎麼定義)

iText這個@Input() 接到的資料是字串 DemoInput