二。Angular -- TODOS

Angular的开发模型更接近于传统强类型语言的模式,加上官方内建的组件和类库比较完整,学习曲线要低一些。

Posted by Azr on January 2, 2019

Angular 是一个开发平台。它能帮你更轻松的构建Web 应用。Angular 集声明式模板、依赖注入、端到端工具和一些最佳实践于一身,为你解决开发方面的各种挑战。

功能描述

TODOS – 任务展示

app.component.html

<div>
  <h1 class="tit">TODOS</h1>
  <div class="todos">
    <ul>
      <li *ngFor="let todo of todos; trackBy: trackByTodo">
        <span></span>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#">x</a>
      </li>
    </ul>
  </div>
</div>

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todos = [
    {id: 1, name: '吃饭', done: true},
    {id: 2, name: '睡觉', done: false},
    {id: 4, name: '1吃饭', done: true},
    {id: 5, name: '2睡觉', done: false},
    {id: 6, name: '3吃饭', done: true},
    {id: 7, name: '4睡觉', done: false}
  ];

  // trackBy方法
  trackByTodo(index, todo) {
    return todo.id;
  }
}

TODOS – 任务添加

app.component.html

<div>
  <h1 class="tit">TODOS</h1>
  <header>
    <input [(ngModel)]="todoName" type="text">
    <button (click)="addTodo()">添加</button>
  </header>

  <div class="todos">
    <ul>
      <li *ngFor="let todo of todos; trackBy: trackByTodo">
        <span></span>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#">x</a>
      </li>
    </ul>
  </div>
</div>

app.component.ts

import { Component } from '@angular/core'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todos = [
    {id: 1, name: '吃饭', done: true},
    {id: 2, name: '睡觉', done: false},
    {id: 4, name: '1吃饭', done: true},
    {id: 5, name: '2睡觉', done: false},
    {id: 6, name: '3吃饭', done: true},
    {id: 7, name: '4睡觉', done: false}
  ]
  private todo: any

  // trackBy方法
  trackByTodo(index, todo) {
    return todo.id
  }

  // 任务名称
  todoName = ''

  addTodo() {
    // 非空操作
    if (this.todoName.trim() === '') {
      return
    }

    // id
    let id
    if (this.todos.length === 0) {
      return 1
    } else {
      id = this.todos[this.todos.length - 1 ].id - 1
    }

    // 放入todos数组
    this.todos.push({
      id,
      name: this.todoName,
      done: false,
    })
    this.todoName = ''
  }
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

// 导入FormsModule
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    // 导出FormsModule
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

###TODOS – 任务删除

app.component.html

<div>
  <h1 class="tit">TODOS</h1>
  <div class="todos">
    <ul>
      <li *ngFor="let todo of todos; trackBy: trackByTodo">
        <span></span>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" (click)="delTodo($event, todo.id)">x</a>
      </li>
    </ul>
  </div>
</div>

app.component.ts

import { Component } from '@angular/core'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todos = [
    {id: 1, name: '吃饭', done: true},
    {id: 2, name: '睡觉', done: false},
    {id: 4, name: '1吃饭', done: true},
    {id: 5, name: '2睡觉', done: false},
    {id: 6, name: '3吃饭', done: true},
    {id: 7, name: '4睡觉', done: false}
  ]
  private todo: any

  // trackBy方法
  trackByTodo(index, todo) {
    return todo.id
  }


  // 删除
  delTodo(e,id) {
    e.preventDefault()
    this.todos = this.todos.filter(todo => todo.id !== id)
  }
}

TODOS – 任务完成状态切换

app.component.html

<div>
  <h1 class="tit">TODOS</h1>
  <div class="todos">
    <ul>
      <li *ngFor="let todo of todos; trackBy: trackByTodo">
        <span [class.done]="todo.done" (click)="changeTodo(todo.id)"></span>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" (click)="delTodo($event, todo.id)">x</a>
      </li>
    </ul>
  </div>
</div>

app.component.ts

import { Component } from '@angular/core'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todos = [
    {id: 1, name: '吃饭', done: true},
    {id: 2, name: '睡觉', done: false},
    {id: 4, name: '1吃饭', done: true},
    {id: 5, name: '2睡觉', done: false},
    {id: 6, name: '3吃饭', done: true},
    {id: 7, name: '4睡觉', done: false}
  ]
  private todo: any

  // trackBy方法
  trackByTodo(index, todo) {
    return todo.id
  }


  // 任务完成状态切换
  changeTodo(id) {
    const curTodo = this.todos.find(todo => todo.id === id)
    curTodo.done = !curTodo.done
  }
}

组件交互

命令

$ ng g c child

child.component.html

<h5>子组件</h5>

app.component.html

<h5>父组件</h5>
<app-child></app-child>

页面显示

父组件传递给子组件—页面显示

父组件传递给子组件

child.component.html

<div>
  <h5>子组件</h5>
  <p>接收到父组件的数据:</p>
</div>

child.component.ts

// 需要导入装饰器@Input
import { Component, OnInit, Input } from '@angular/core';

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

  constructor() { }

  ngOnInit() {
  }
// 使用装饰器@Input修饰了skill属性,这就意味着child-component被使用时期望收到一个名为skill的属性 -- 公开属性
  @Input()
    skill

}

app.component.html

<div>
  <h1></h1>
  <h5>父组件</h5>
  <app-child skill="parent"></app-child>
</div>

页面显示:

基本数据类型—父组件传值

  1. 在子组件中,需要导入装饰器@Input
  2. 需要使用装饰器@Input修饰了skill属性,这就意味着child-component被使用时期望收到一个名为skill的属性
  3. 此时传入的是基本数据类型,子组件中无论做任何操作都不会影响到父组件 child.component.html
<div>
  <h5>子组件</h5>
  <p>接收到父组件的数据:</p>
</div>

child.component.ts

// 需要导入装饰器@Input
import { Component, OnInit, Input } from '@angular/core';

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

  constructor() { }

  ngOnInit() {
  }
// 使用装饰器@Input修饰了skill属性,这就意味着child-component被使用时期望收到一个名为skill的属性
  @Input()
    skill

}

app.component.html

<div>
  <h1></h1>
  <h5>父组件</h5>
  <app-child [skill]="parent"></app-child>
</div>

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = '父子通讯';
  parent = 'parent111';
}

页面显示:

引用数据类型—父组件传值

  1. 引用数据类型,要注意子组件中对数据的操作可能会对父组件产生影响
  2. 注意[skill]语法标识了数据流向:父组件流入子组件,即单向数据绑定

父-->子

子组件传递给父组件

子—>父

child.component.html

<div>
  <h5>子组件 \(^o^)/~</h5>
  <button (click)="handleClick()">触发子组件的事件</button>
</div>

child.component.ts

// 导入装饰器@Output和EventEmitter
import { Component, OnInit, Output, EventEmitter } from '@angular/core';

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

  constructor() { }

  ngOnInit() {
  }
// 使用装饰器@Output修饰了getMoney属性,并为其赋了初值为EventEmitter的实例
  @Output()
  getMoney = new EventEmitter()

  handleClick() {
    // 触发事件
    this.getMoney.emit('子组件说xxxx')
  }
}

app.component.html

<div>
  <h5>父组件</h5>
  
  <app-child (getMoney)="giveMoney($event)"></app-child>
    
   <h5>子组件说: <h5>
</div>

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  msg = ''
  giveMoney(data) {
    console.log('父组件中的方法',data)
    this.msg = data
  }
}
  1. 导入装饰器@OutputEventEmitter
  2. 由子组件去创建一个事件getMoney
  3. 由父组件来绑定这个事件(getMoney)="giveMoney($event)"
  4. 然后触发giveMoney的方法。

总结

双向数据绑定:父组件将数据传入子组件中,子组件改变数据时来通知父组件来进行同步

数据流向是单向的,数据是从父组件单向流入子组件中,父组件数据的更新是通过子组件的事件通知以后再更新。

双向数据绑定 = 单向数据绑定 + 事件

<input type='text' name='userName' [(ngModel)]="userName">

<input type='text' name='userName' [ngModel]="userName" (ngModelChange)="userName=$event">

child-component组件写双向数据绑定

<app-child (getMoney)="giveMoney($event)"></app-child>

TODOS升级

升级说明

  1. 分离为专门的组件
  2. 抽离为模块
  3. 组件

TODOS组件

  1. 组件职责说明

    • 父组件(todo) 提供待办事项数据
    • 子组件(todo-header) 任务添加
    • 子组件(todo-list) 任务展示、删除、状态切换

    注:组件是独立的,其它组件不能毫无限制的访问

创建模块和组件

创建模板:

$ ng g m todos

创建组件:

$ ng g c todos/todo
$ ng g c todos/todo-header
$ ng g c todos/todo-list

在根模块中使用模块

需要在app.module.ts中导入导出TodosModule模块。

import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { AppComponent } from './app.component'
// 导入TodosModule
import { TodosModule } from './todos/todos.module'

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    //  导出TodosModule
    TodosModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

报错以下内容就是没有进行导出。

Uncaught Error: Template parse errors:
'app-todo' is not a known element:
..........

app.component.html

<app-todo></app-todo>

将之前TODOS的组件移到todo的组件中。

todo.component.html

<div>
  <h1>TODOS</h1>
  <header>
    <input [(ngModel)]="todoName" type="text">
    <button (click)="addTodo()">添加</button>
  </header>

  <div class="todos">
    <ul>
      <li *ngFor="let todo of todos; trackBy: trackByTodo">
        <span [class.done]="todo.done" (click)="changeTodo(todo.id)"></span>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" (click)="delTodo($event, todo.id)">x</a>
      </li>
    </ul>
  </div>
</div>

todo.component.ts

import { Component, OnInit } from '@angular/core';

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

  constructor() { }


  todos = [
    {id: 1, name: '吃饭', done: true},
    {id: 2, name: '睡觉', done: false},
    {id: 4, name: '1吃饭', done: true},
    {id: 5, name: '2睡觉', done: false},
    {id: 6, name: '3吃饭', done: true},
    {id: 7, name: '4睡觉', done: false}
  ]
  private todo: any

  // trackBy方法
  trackByTodo(index, todo) {
    return todo.id
  }

  // 任务名称
  todoName = ''
  addTodo() {
    // 非空操作
    if (this.todoName.trim() === '') {
      return
    }

    // id
    let id
    if (this.todos.length === 0) {
      return 1
    } else {
      id = this.todos[this.todos.length - 1 ].id - 1
    }

    // 放入todos数组
    this.todos.push({
      id,
      name: this.todoName,
      done: false,
    })
    this.todoName = ''
  }

  // 删除
  delTodo(e,id) {
    e.preventDefault()
    this.todos = this.todos.filter(todo => todo.id !== id)
  }

  // 任务完成状态切换
  changeTodo(id) {
    const curTodo = this.todos.find(todo => todo.id === id)
    curTodo.done = !curTodo.done
  }

  
  ngOnInit() {
  }

}

报错以下内容就是没有进行导入FormsModule

compiler.js:1021 Uncaught Error: Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'. ("
........

抽离todo-header组件

  1. 整理基本结构。

    todo-header.component.html

    <header>
      <input [(ngModel)]="todoName" type="text">
      <button (click)="addTodo()">添加</button>
    </header>
    

    在HTML中有todoNameaddTodo两个方法,从todo.component.ts中剪切过来。

    todo-header.component.ts中提供一个事件,如下:

     @Output()
      add = new EventEmitter()
    

    todo-header.component.ts

    import { Component, OnInit, Output, EventEmitter } from '@angular/core';
    ... 
    export class TodoHeaderComponent implements OnInit {
       
      constructor() { }
       
      // 任务名称
      todoName = ''
       
      @Output()
      add = new EventEmitter()
           
           
      addTodo() {
        // 非空操作
        if (this.todoName.trim() === '') {
          return
        }
       
        // id
        let id
        if (this.todos.length === 0) {
          return 1
        } else {
          id = this.todos[this.todos.length - 1 ].id - 1
        }
       
        // 放入todos数组
        this.todos.push({
          id,
          name: this.todoName,
          done: false,
        })
        this.todoName = ''
      }
       
      ngOnInit() {
      }
    }
    

    需要在父组件todo.component.html中去注册一下这个事件。

    <app-todo-header (add)="addTodo($event)"></app-todo-header>
    

    在父组件todo.component.ts中重新提供一下addTodo

    ..... 
    // 子组件传递的todoName
     addTodo(todoName) {
      }
    .....
    

    在子组件todo-header.component.ts中,需要向父组件传递todoName

    ..... 
     addTodo() {
         this.add.emit(this.todoName)
      }
    .....
    

    看下职责区分,哪些应该是父组件完成的,哪些应该是子组件完成的。

    父组件todo.component.ts

    ..... 
    addTodo(todoName) {
       
        // id
        let id
        if (this.todos.length === 0) {
          return 1
        } else {
          id = this.todos[this.todos.length - 1 ].id - 1
        }
       
        // 放入todos数组
        this.todos.push({
          id,
          name: todoName,
          done: false,
        })
      }
    .....
    

    在子组件todo-header.component.ts中`

    ..... 
     addTodo() {
           
        if (this.todoName.trim() === '') {
            return
          }
       
        this.add.emit(this.todoName)
       
        this.todoName = ''
      }
    .....
    
  2. TodoName

  3. addTodo

  4. 添加任务抽离为两个部分

    1. 清空等放在子组件中,提供数据,效验数据等
    2. 父组件完成数据
<div>


  <div class="todos">
    <ul>
      <li *ngFor="let todo of todos; trackBy: trackByTodo">
        <span [class.done]="todo.done" (click)="changeTodo(todo.id)"></span>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" (click)="delTodo($event, todo.id)">x</a>
      </li>
    </ul>
  </div>
</div>

todo-header.component.ts

import { Component, OnInit } from '@angular/core';

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

  constructor() { }

  ngOnInit() {
  }
}

既,代码

todo.component.html

<div>
  <h1>TODOS</h1>
  <app-todo-header (add)="addTodo($event)"></app-todo-header>


  <div class="todos">
    <ul>
      <li *ngFor="let todo of todos; trackBy: trackByTodo">
        <span [class.done]="todo.done" (click)="changeTodo(todo.id)"></span>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" (click)="delTodo($event, todo.id)">x</a>
      </li>
    </ul>
  </div>
</div>

todo.component.ts

import { Component, OnInit } from '@angular/core';

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

  constructor() { }


  todos = [
    {id: 1, name: '吃饭', done: true},
    {id: 2, name: '睡觉', done: false},
    {id: 4, name: '1吃饭', done: true},
    {id: 5, name: '2睡觉', done: false},
    {id: 6, name: '3吃饭', done: true},
    {id: 7, name: '4睡觉', done: false}
  ]
  private todo: any

  // trackBy方法
  trackByTodo(index, todo) {
    return todo.id
  }

  // 任务名称
  todoName = ''

  addTodo(todoName) {

    // id
    let id
    if (this.todos.length === 0) {
      return 1
    } else {
      id = this.todos[this.todos.length - 1 ].id - 1
    }

    // 放入todos数组
    this.todos.push({
      id,
      name: todoName,
      done: false,
    })


  }

  // 删除
  delTodo(e,id) {
    e.preventDefault()
    this.todos = this.todos.filter(todo => todo.id !== id)
  }

  // 任务完成状态切换
  changeTodo(id) {
    const curTodo = this.todos.find(todo => todo.id === id)
    curTodo.done = !curTodo.done
  }

  
  ngOnInit() {
  }

}

todo-header.component.html

<header>
  <input [(ngModel)]="todoName" type="text">
  <button (click)="addTodo()">添加</button>
</header>

todo-header.component.ts

import { Component, OnInit, Output, EventEmitter } from '@angular/core';

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

  constructor() { }


  // 任务名称
  todoName = ''

  @Output()
  add = new EventEmitter()

    

  addTodo() {
    
    if (this.todoName.trim() === '') {
        return
      }

    this.add.emit(this.todoName)

    this.todoName = ''
  }

  ngOnInit() {
  }

}

抽离todo-list组件

先将HTML结构贴过来

todo-list.component.html

<div class="todos">
    <ul>
      <li *ngFor="let todo of todos; trackBy: trackByTodo">
        <span [class.done]="todo.done" (click)="changeTodo(todo.id)"></span>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" (click)="delTodo($event, todo.id)">x</a>
      </li>
    </ul>
  </div>

todo-list.component.css

.done {
  text-decoration: line-through;
  color: gray;
}

在父组件todo.component.html中展示出来

<div>
  <h1>TODOS</h1>
  <app-todo-header (add)="addTodo($event)"></app-todo-header>
  <app-todo-list></app-todo-list> 
</div>

父组件是负责提供数据的,负责内部的展示,需要将父组件的数据传递给子组件,需要子组件去公开一个数据

todo-list.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { TodosModule } from '../todos.module';

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

  constructor() { }

  @Input()
  todos

  
  ngOnInit() {
  }

}

父组件中提供数据

todo.component.html

<div>
  <h1>TODOS</h1>
  <app-todo-header (add)="addTodo($event)"></app-todo-header>
  <app-todo-list [todos]="todos"></app-todo-list> 
</div>

这样,就将子组件的todos传递给了父组件的todos。[tosos]是和子组件的todos相对应的。后面的todos是父组件的数据。页面就会展示出来数据。

trackBy方法和delTodo还有changeTodo直接复制过来。

todo-list.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { TodosModule } from '../todos.module';

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

  constructor() { }

  @Input()
  todos

    // trackBy方法
    trackByTodo(index, todo) {
      return todo.id
    }

      // 删除
  delTodo(e,id) {
    e.preventDefault()
    this.todos = this.todos.filter(todo => todo.id !== id)
  }

  // 任务完成状态切换
  changeTodo(id) {
    const curTodo = this.todos.find(todo => todo.id === id)
    curTodo.done = !curTodo.done
  }


  ngOnInit() {
  }

}

todo.component.ts

import { Component, OnInit } from '@angular/core';

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

  constructor() { }


  todos = [
    {id: 1, name: '吃饭', done: true},
    {id: 2, name: '睡觉', done: false},
    {id: 4, name: '1吃饭', done: true},
    {id: 5, name: '2睡觉', done: false},
    {id: 6, name: '3吃饭', done: true},
    {id: 7, name: '4睡觉', done: false}
  ]
  private todo: any



  // 任务名称
  todoName = ''

  addTodo(todoName) {

    // id
    let id
    if (this.todos.length === 0) {
      return 1
    } else {
      id = this.todos[this.todos.length - 1 ].id - 1
    }

    // 放入todos数组
    this.todos.push({
      id,
      name: todoName,
      done: false,
    })

  }
  ngOnInit() {
  }
}

页面没有办法进行添加的操作。

子组件操作的数据仅仅是父组件的数据,现在的数据源是混乱的。

子组件应该是只能操作父组件的数据。

在子组件中添加事件,让数据可以实现双向数据绑定

todo-list.component.ts

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { TodosModule } from '../todos.module';

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

  constructor() { }

  @Input()
  todos

  // 添加事件 让数据可以实现双向数据绑定
  @Output()
  del = new EventEmitter()

  @Output()
  change = new EventEmitter()
  
  // trackBy方法
  trackByTodo(index, todo) {
    return todo.id
  }  


  // 删除
  delTodo(e,id) {
    e.preventDefault()
    this.todos = this.todos.filter(todo => todo.id !== id)
  }

  // 任务完成状态切换
  changeTodo(id) {
    const curTodo = this.todos.find(todo => todo.id === id)
    curTodo.done = !curTodo.done
  }

  
  ngOnInit() {
  }

}

然后去父组件中绑定事件

todo.component.html

<div>
  <h1>TODOS</h1>
  <app-todo-header (add)="addTodo($event)"></app-todo-header>
 <app-todo-list [todos]="todos" (del)="delTodo($event)" (change)="changeTodo($event)"
  ></app-todo-list>
</div>

去子组件中触发事件。

todo-list.component.ts

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { TodosModule } from '../todos.module';

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

  constructor() { }

  @Input()
  todos

  @Output()
  del = new EventEmitter()

  @Output()
  change = new EventEmitter()

  // trackBy方法
  trackByTodo(index, todo) {
    return todo.id
  }  


  // 删除
  delTodo(e,id) {
    e.preventDefault()
    this.del.emit(id)
  }

  // 任务完成状态切换
  changeTodo(id) {
    this.change.emit(id)
  }

  
  ngOnInit() {
  }

}

todo.component.ts

import { Component, OnInit } from '@angular/core';

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

  constructor() { }


  todos = [
    {id: 1, name: '吃饭', done: true},
    {id: 2, name: '睡觉', done: false},
    {id: 4, name: '1吃饭', done: true},
    {id: 5, name: '2睡觉', done: false},
    {id: 6, name: '3吃饭', done: true},
    {id: 7, name: '4睡觉', done: false}
  ]
  private todo: any



  // 任务名称
  todoName = ''

  addTodo(todoName) {

    // id
    let id
    if (this.todos.length === 0) {
      return 1
    } else {
      id = this.todos[this.todos.length - 1 ].id - 1
    }

    // 放入todos数组
    this.todos.push({
      id,
      name: todoName,
      done: false,
    })

  }

  // 删除
  delTodo(e,id) {
    this.todos = this.todos.filter(todo => todo.id !== id)
  }
  

  // 任务完成状态切换
  changeTodo(id) {
    const curTodo = this.todos.find(todo => todo.id === id)
    curTodo.done = !curTodo.done
  }

  ngOnInit() {
  }

}

本文首次发布于 Azr的博客, 作者 @azrrrrr ,转载请保留原文链接.

原文链接: http://amor9.cn/2019/01/02/ng2/