第五课:创建checklist与列表项

目前为止我们做了很多的设置和架构工作,但是本课中我们开始制作骨架部分。我们将加入创建checklist,查看checklist以及给他们添加项(同时也提供编辑项和列表的操作)的途径。这将是一个大动作,建议你先准备点咖啡。

checklist

第一件要做的事情是添加创建和展示checklist所需的所有东西。意思是添加到类定义,修改之前创建的模板来展示真实的清单数据。
先从设置类定义开始。
> 修改 src/pages/home/home.ts为如下:

  1. import { Component } from '@angular/core';
  2. import { NavController, AlertController, Platform } from 'ionic-angular';
  3. import { ChecklistPage } from '../checklist/checklist';
  4. import { ChecklistModel } from '../../models/checklist-model';
  5. import { Data } from '../../providers/data';
  6. import { Keyboard } from 'ionic-native';
  7. @Component({
  8. selector: 'page-home',
  9. templateUrl: 'home.html'
  10. })
  11. export class HomePage {
  12. checklists: ChecklistModel[] = [];
  13. constructor(public nav: NavController, public dataService: Data, public alertCtrl: AlertController, public platform: Platform) {
  14. }
  15. ionViewDidLoad() {
  16. }
  17. addChecklist(): void {
  18. }
  19. renameChecklist(checklist): void {
  20. }
  21. viewChecklist(checklist): void {
  22. }
  23. removeChecklist(checklist): void{
  24. }
  25. save(): void{
  26. }
  27. }

在这里我们从Ionic库里面导入了不少东西。NavController你可以已经很熟悉了,但是AlertController可能就不那么熟悉了。AlertController允许我们向用户展示各种警告框,包括基本的提示框,带输入的提示框,确认框等等。我们以他作为添加新checklist的途径。
同时我们也导入了我们的ChecklistPage,我们稍后会实现他,最重要的是我们导入了上节课制作的Checklist Model。
我们也导入了之前生成的Data提供者,但是他的具体功能后续才会实现。最后,我们从Ionic Native导入了‘Keyboard’,这样我们可以用他来确保稍后用户添加了checklist之后能够关闭软键盘。
在构造器中通过给NavController和Data添加public修饰符,我们就可以在类里通过this.navthis.dataService来访问他们了。基本上他是以下内容的简短写法:

  1. constructor(nav: NavController, dataService: Data){
  2. this.nav = nav;
  3. this.dataService = dataService;
  4. }

之前你应该看到过。
我们也声明了一个checklists数组,类定义里面可以通过this.checklists来访问他。类定义里面的大量的函数将在后续一个一个的讲解。

addChecklist

这个方法提供给用户创建一个新的checklist。他会启动一个提示框,然后使用其中输入的信息来创建一个新的checklist(会用到我们之前创建的数据模型)。
> 修改 addChecklist 函数如下:

  1. addChecklist(): void {
  2. let prompt = this.alertCtrl.create({
  3. title: 'New Checklist',
  4. message: 'Enter the name of your new checklist below:',
  5. inputs: [
  6. {
  7. name: 'name'
  8. }
  9. ],
  10. buttons: [
  11. {
  12. text: 'Cancel'
  13. },
  14. {
  15. text: 'Save',
  16. handler: data => {
  17. let newChecklist = new ChecklistModel(data.name, []);
  18. this.checklists.push(newChecklist);
  19. newChecklist.checklist.subscribe(update => {
  20. this.save();
  21. });
  22. this.save();
  23. }
  24. }
  25. ]
  26. });
  27. prompt.present();
  28. }

我们给用户展示了一个提示框,提示框包含了一个输入域,两个按钮Cancel和Save。取消按钮除了关闭提示框之外,不做其他事情,但是保存按钮我们给他添加了一个处理器用于传递输入的数据。
在处理器里面,我们先通过传入输入的名字到一个checklist model里生成了一个新的checklist,然后将他push到咱们的this.checklists数组。然后我们订阅了我们给数据模型添加的observable来监听checklist的修改,然后其中会触发save函数。注意,我们这里调用了两次save,一个是observable触发的,另一个是直接触发的(因为我们添加了一个新的checklist)。
最后,我们通过present方法呈现了这个提示框。
如果你再次看模板文件的时候,会发现我们在add按钮点击的时候已经调用了这个函数。

  1. <button (click)="addChecklist()"><ion-icon name="add-circle"></ion-icon></button>

renameChecklist

接下来我们来定义renameChecklist函数,看名字就知道是给checklist重命名的。
> 修改 renameChecklist 函数如下:

  1. renameChecklist(checklist): void {
  2. let prompt = this.alertCtrl.create({
  3. title: 'Rename Checklist',
  4. message: 'Enter the new name of this checklist below:',
  5. inputs: [
  6. {
  7. name: 'name'
  8. }
  9. ],
  10. buttons: [
  11. {
  12. text: 'Cancel'
  13. },
  14. {
  15. text: 'Save',
  16. handler: data => {
  17. let index = this.checklists.indexOf(checklist);
  18. if(index > -1){
  19. this.checklists[index].setTitle(data.name);
  20. this.save();
  21. }
  22. }
  23. }
  24. ]
  25. });
  26. prompt.present();
  27. }

第一眼看起来和addChecklist函数很想,因为他本来就很像哇。用了同样的提示框,同样的输入框,童颜的按钮,只是处理器稍有区别而已。
注意,这个函数有参数传入,也就是我们将要改名的checklist的引用。我们稍后修改模板来传入此参数,但是此刻我们假装他已经传入进来就可以了。
我们使用此checklist的引用在this.checklists中查找然后将他设置为新的标题,然后触发save。
你可能会记得我们已经在模板中设置了一个点击处理器来调用这个函数:

  1. <button light (click)="renameChecklist(checklist)"><ion-icon name="clipboard"></ion-icon></button>

removeChecklist

接下来我们来加入删除checklist的能力。
> 修改 removeChecklist 函数如下:

  1. removeChecklist(checklist): void{
  2. let index = this.checklists.indexOf(checklist);
  3. if(index > -1){
  4. this.checklists.splice(index, 1);
  5. this.save();
  6. }
  7. }

这个函数简单多了,因为他没有用户输入需求,我们只需要处理掉这个checklist就可以了。跟之前做的一样,我们传入了一个checklist引用然后在this.checklists找到他。之后通过数组的splice方法简单的将他移除掉然后触发save就完成了。
以下是模板中触发此函数的相关代码:

  1. <button danger (click)="removeChecklist(checklist)"><ion-icon name="trash"></ion-icon> Delete</button>

viewChecklist

我们现在可以创建和修改咱们的checklist了,但是我们也得可以看checklist的详情不是。我们将要通过咱们的NavController来push压入一个新页面然后传入我们选择的checklist。
> 修改 viewChecklist 函数为如下:

  1. viewChecklist(checklist): void {
  2. this.nav.push(ChecklistPage, {
  3. checklist: checklist
  4. });
  5. }

我们将之前导入的ChecklistPage(还没完成)传入到 push 方法,以及想要传递给页面的其他参数,也就是我们要显示详情的checklist。在页面中,我们就可以通过NavParams来获取这些数据了。

save

这可能是现在来讲最奇怪的一个方法了,我们实际现在不会去实现。后续的保存和加载数据才是他的本命课程,我们那个时候才会再来看他。

为了把所有事情联合起来,我们需要完善home页面的模板。我们现在可以用自己的数据了,但是我们还是什么都看不到。
> 修改 src/pages/home/home.html 为如下:

  1. <ion-header>
  2. <ion-navbar color="secondary">
  3. <ion-title>
  4. <img src = "assets/images/logo.png" />
  5. </ion-title>
  6. <ion-buttons end>
  7. <button ion-button icon-only (click)="addChecklist()"><ion-icon name="add-circle"></ion-icon></button>
  8. </ion-buttons>
  9. </ion-navbar>
  10. </ion-header>
  11. <ion-content>
  12. <ion-list no-lines>
  13. <ion-item-sliding *ngFor="let checklist of checklists">
  14. <button ion-item (click)="viewChecklist(checklist)">
  15. {{checklist.title}}
  16. <span>{{checklist.items.length}} items</span>
  17. </button>
  18. <ion-item-options>
  19. <button ion-button icon-only color="light" (click)="renameChecklist(checklist)"><ion-icon
  20. name="clipboard"></ion-icon></button>
  21. <button ion-button icon-only color="danger" (click)="removeChecklist(checklist)"><ion-icon
  22. name="trash"></ion-icon></button>
  23. </ion-item-options>
  24. </ion-item-sliding>
  25. </ion-list>
  26. </ion-content>

这里我们做了很多有趣的事情,但是最需要注意的是 ngFor 循环:

  1. <ion-item-sliding *ngFor="let checklist of checklists">

这个循环的作用是循环我们的this.checklist数组并为他们在列表中创建一个滑动项。记住,在ngFor前面使用的 * 语法是Angular 2中用于创建内置模板的快捷方式,所以我们实际上是为数组中的每个元素创建一个模板。每次新建模板的时候他都会包含指定项的信息,这样在 ngFor 里面的任意处我们都可以获取指定checklist的数据用作渲染,如下:

  1. {{checklist.title}}

同时也请注意 ngFor 循环的 checklist 变量前面都有 let 修饰符。在Angular 2中使用let修饰符让我们可以创建一个局部变量,这样我们可以将这个局部变量的引用传入到之前创建的函数中。为了便于理解,如果我们用以下方法替换的话:

  1. <ion-item-sliding *ngFor="let check of checklists">

我们就可以这样去渲染数据:

  1. {{check.title}}

可以这样穿个函数:

  1. removeChecklist(check)

这样我们算是完成了home页面了,现在你可以去添加,编辑和删除checklist了,也可以显示指定checklist(现在没有任何实际内容,所以算是用不了)的详情页了。
此时当你运行ionic serve的时候,你应该可以看到这样的画面:
预览

重点注意如果想要创建一个新的项然后展示他的详情页的话,必须从checklist.html移除此行

  1. <ion-checkbox [checked]="item.checked" (click)="toggleItem(item)"></ion-checkbox>

后续课程里面我们只会在有项数据的时候才加入他,但是目前为止,由于没有数据所以在尝试访问数据的时候会报错。
所有这些后面都会讲到,如果你现在想正常预览应用的话得先做这些额外的步骤。

Checklist 项

现在我们可以触发checklist详情页了,我们最好加入点内容,给用户提供创建和修改单个checklist数据项的途径。这个部分中,我们将实现自己的Checklist Page,它将通过home页面启动,会提供一些相关数据给他用于展示checklist。
我们从设置类定义开始。
> 修改 src/pages/checklist/checklist.ts 为如下:

  1. import { Component } from '@angular/core';
  2. import { NavController, NavParams, AlertController } from 'ionic-angular';
  3. @Component({
  4. selector: 'page-checklist',
  5. templateUrl: 'checklist.html'
  6. })
  7. export class ChecklistPage {
  8. checklist: any;
  9. constructor(public nav: NavController, public navParams: NavParams, public
  10. alertCtrl: AlertController){
  11. this.checklist = this.navParams.get('checklist');
  12. }
  13. addItem(): void {
  14. }
  15. toggleItem(item): void {
  16. }
  17. removeItem(item): void {
  18. }
  19. renameItem(item): void {
  20. }
  21. uncheckItems(): void {
  22. }
  23. }

这里需要学习的点不多,唯一需要注意的是NavParams的使用。当我们传递数据到另一个页面的时候,我们可以通过注入NavParams然后通过他的get方法来获取。在本例中,我们只是传入了想要查看的checklist,如果你想要的话可以传入很多其他你需要的值。
跟之前一样,我们将一个一个的去实现每个方法。很多跟之前home页面方法相同。

addItem

> 修改 addItem 函数为如下:

  1. addItem(): void {
  2. let prompt = this.alertCtrl.create({
  3. title: 'Add Item',
  4. message: 'Enter the name of the task for this checklist below:',
  5. inputs: [
  6. {
  7. name: 'name'
  8. }
  9. ],
  10. buttons: [
  11. {
  12. text: 'Cancel'
  13. },
  14. {
  15. text: 'Save',
  16. handler: data => {
  17. this.checklist.addItem(data.name);
  18. }
  19. }
  20. ]
  21. });
  22. prompt.present();
  23. }

这些看起来应该很熟悉,但是注意处理器的不同。由于我们在数据模型中创建了一个addItem助理函数,我们只需要调用这个函数传入我们需要创建的项的名即可(告诉过你数据模型会带来很大的便利!)。

renameItem

> 修改 renameItem 函数为如下:

  1. renameItem(item): void {
  2. let prompt = this.alertCtrl.create({
  3. title: 'Rename Item',
  4. message: 'Enter the new name of the task for this checklist below:',
  5. inputs: [
  6. {
  7. name: 'name'
  8. }
  9. ],
  10. buttons: [
  11. {
  12. text: 'Cancel'
  13. },
  14. {
  15. text: 'Save',
  16. handler: data => {
  17. this.checklist.renameItem(item, data.name);
  18. }
  19. }
  20. ]
  21. });
  22. prompt.present();
  23. }

基本上相同除了在处理器中调用了数据模型的renameItem方法,同时传入了需要改名的项的引用。

removeItem

> 修改 removeItem 函数为如下:

  1. removeItem(item): void {
  2. this.checklist.removeItem(item);
  3. }

这个更简单,我们简单的调用了数据模型的removeItem助理函数然后传入了我们需要删掉的项。

toggleItem

> 修改 toggleItem 函数为如下:

  1. toggleItem(item): void {
  2. this.checklist.toggleItem(item);
  3. }

这个函数用于切换单独项的标记为开或者关,我们也只是简单的传入项的引用给数据模型。

uncheckItems

> 修改 uncheckItems 函数为如下:

  1. uncheckItems(): void {
  2. this.checklist.items.forEach((item) => {
  3. if(item.checked){
  4. this.checklist.toggleItem(item);
  5. }
  6. });
  7. }

这个函数是绑定到模板里的reset按钮的,他将循环checklist里面的每个项,如果当前项是开的状态的话,调用数据模型的toggleItem方法关掉他。这个方法让用户可以一次关掉所有项。
现在,我们只剩下checklist页需要处理了。我们之前已经设置好了这个模板的整体结构,但是为了显示真实数据,我们也需要对他进行home页类似的调整。
> 修改 src/pages/checklist/checklist.html 如下:

  1. <ion-header>
  2. <ion-navbar color="secondary">
  3. <ion-title>
  4. {{checklist.title}}
  5. </ion-title>
  6. <ion-buttons end>
  7. <button ion-button icon-only (click)="uncheckItems()"><ion-icon name="refresh-circle"></ion-icon></button>
  8. <button ion-button icon-only (click)="addItem()"><ion-icon name="add-circle"></ion-icon></button>
  9. </ion-buttons>
  10. </ion-navbar>
  11. </ion-header>
  12. <ion-content>
  13. <ion-list no-lines>
  14. <ion-item-sliding *ngFor="let item of checklist.items">
  15. <ion-item>
  16. <ion-label>{{item.title}}</ion-label>
  17. <ion-checkbox [checked]="item.checked" (click)="toggleItem(item)">
  18. </ion-checkbox>
  19. </ion-item>
  20. <ion-item-options>
  21. <button ion-button icon-only color="light" (click)="renameItem(item)"><ion-icon name="clipboard"></ion-icon></button>
  22. <button ion-button icon-only color="danger" (click)="removeItem(item)"><ion-icon name="trash"></ion-icon></button>
  23. </ion-item-options>
  24. </ion-item-sliding>
  25. </ion-list>
  26. </ion-content>

看起来跟home页面很像,但是有些许不同。我们用类定义中的checklist数据在navbar中显示当前checklist的标题。我们再次使用 ngFor 来循环数据,但是这次我们只是循环的数据项,也就是checklist的子项。同时注意我们使用了双花括号来渲染数据:

  1. {{item.title}}

我们也可以通过方括号来设置元素属性:

  1. [checked]="item.checked"

他将会把checked的值得设置为item.checked的值。

 总结

现在你基本可以执行应用里的每个函数来,包括创建checklist,修改,查看单独的checklist,以及给他们添加单独的数据项。
试试看在浏览器中运行你的应用,添加你自己的checklist和数据项。
下一节课中,我们将实现保存数据然后看看怎么样美化这个原因(先屋子再门面,对吧?)。