项目:露营伴侣(Camper Mate) - 第四课:用户输入和表单
这节课我们来看Camp Details和My Details页的功能,也就是这些功能组成我们的输入功能。如果你已经完成了本书的其他应用的话,或者之前用过Ionic 2的输入域,你可能有这样用过ngModel:
<ion-input type="text" [(ngModel)]="myField"></ion-input>
这种写法给输入域设置了双向数据绑定,这些我在基础部分详细讨论过,本质上他就是他这个输入域的值捆绑到本页类定义的:
this.myField
如果你改变this.myField的值的话,将会映射到这个输入框来,如果输入框的值改变的话将会被映射到this.myField。对于管理用户输入来将,这是个非常简单的方法,但是如果有更多更复杂的输入域的时候,那么就需要使用FormBuilder了。
通过使用From Builder,不仅可以使代码更漂亮,还可以在“控制”每个输入框的同时带来更多强大的功能(如果看一眼Giflists应用的话,会看到我们用来控制文本域改变的定语,通过他可以做很多神奇的事情)。
我们也可以使用Validators与Form Builder写作,他允许我们捆绑一个“验证”到指定的输入域,这个验证将会检查输入是否允许(即,是否是正确的邮件格式)。
我们直接进入实现过程吧,更直观些。我们会先实现Camp Details页面的功能,然后烤制到My Details页。
> 修改 src/pages/camp=details/camp-details.html 的列表部分为如下:
<ion-list no-lines>
<form [formGroup]="campDetailsForm" (change)="saveForm()">
<ion-item>
<ion-label stacked>Gate Access Code</ion-label>
<ion-input formControlName="gateAccessCode" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Ammenities Code</ion-label>
<ion-input formControlName="ammenitiesCode" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>WiFi Password</ion-label>
<ion-input formControlName="wifiPassword" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Phone Number</ion-label>
<ion-input formControlName="phoneNumber" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Departure Date</ion-label>
<ion-datetime formControlName="departure" displayFormat="DD/MM/YYYY"></ion-datetime>
</ion-item>
<ion-item>
<ion-label stacked>Notes</ion-label>
<ion-textarea formControlName="notes" type="text"></ion-textarea>
</ion-item>
</form>
</ion-list>
这里的输入框都差不多只是有一些比较重要的区别。首先,我们将它们包装到一个form标签中:
<form [formGroup]="campDetailsForm" (change)="saveForm()">
我们定义了formGroup的值为campDetailsForm,这个值很快就会跟Form Builder一起使用。同时,我们也监听了(change)事件并在检测到的时候触发saveForm函数,意思就是用户改变和切换输入框的时候都会触发saveForm函数,但是不会在输入单个字符的时候触发(我们想要的话也可以做到)。通常对于表单而言我们都是监听他的(submit)事件并在其中处理数据,但是我们不想用户点击“Save”按钮或者其他类似操作来处理,我们只是希望用户在输入了一个新值的时候尽快存储起来。
另一个重要的变更时我们给每个输入框添加了formControlName,并给他指定了一个名字(跟我们将要使用ngModel做的差不多)。再次,我们就快把他和Form Builder一起使用了。
现在,我们看一下类定义。首先,我们改一下构造器:
> 修改 src/pages/camp-details/camp-details.ts 的构造器如下:
campDetailsForm: FormGroup;
constructor(public navCtrl: NavController, public formBuilder: FormBuilder,public dataService: Data) {
this.campDetailsForm = formBuilder.group({
gateAccessCode: [''],
ammenitiesCode: [''],
wifiPassword: [''],
phoneNumber: [''],
departure: [''],
notes: ['']
});
}
由于我们在模板中已经将formGroup定义为campDetailsForm,我们这里可以指定一个新的Form Builder组。我们通过将之前指定给输入框的formControlName提供进来创建一个新的组。注意,我们提供了一个包含了空白字符串的数组,他用于作为输入框的初始值,例如:
gateAccessCode: ['54321']
这段代码会将dateAccessCode输入框的初始值设为‘54321’。你也可以像这样在这里提供一个验证器(validator):
gateAccessCode: ['', Validators.required]
以上代码会把gateAccessCode变为必需的域。这就是设置表单的全部内容了,现在我们只要实现saveForm函数来。
> 修改 src/pages/camp-details/camp-details.ts 的 saveForm 函数:
saveForm(): void {
let data = this.campDetailsForm.value;
//this.dataService.setCampDetails(data);
}
注意:我们注释掉了对数据服务的调用时因为我们目前没有实现他,不然的话TypeScript会向我们抛出错误。
现在,我们可以在任何时候通过this.campDetailsForm.value获取表单的值。他将会返回一个对象包含了所有的值,也就是我们需要保存的数据。所以我们将这些数据传给数据服务去保存(现在还未实现)。
记住,saveForm函数在任何输入框改变的时候会触发,所以任何他们改变的时候我们读取这些值并保存他们。
现在我们只需要把这些变更映射到我们的My Details页。这些基本上一样的,我们我直接就复制粘贴了。不解释。
> 修改 src/pages/my-details/my-details.html 为如下:
<ion-header>
<ion-navbar color="primary">
<ion-title>
<img src = "assets/images/logo.png" class="logo" />
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-card>
<ion-card-header>
My Details
</ion-card-header>
<ion-card-content>
Update this form with your details so you have an easy reference for
later.
</ion-card-content>
</ion-card>
<ion-list no-lines>
<form [formGroup]="myDetailsForm" (change)="saveForm()">
<ion-item>
<ion-label stacked>Car Registration</ion-label>
<ion-input formControlName="carRegistration" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Trailer Registration</ion-label>
<ion-input formControlName="trailerRegistration" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Trailer Dimensions</ion-label>
<ion-input formControlName="trailerDimensions" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Phone Number</ion-label>
<ion-input formControlName="phoneNumber" type="text"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Notes</ion-label>
<ion-textarea formControlName="notes" type="text"></ion-textarea>
</ion-item>
</form>
</ion-list>
</ion-content>
> 修改 src/pages/my-details/my-details.ts的构造器如下:
myDetailsForm: FormGroup;
constructor(public navCtrl: NavController, public formBuilder: FormBuilder,public dataService: Data) {
this.myDetailsForm = formBuilder.group({
carRegistration: [''],
trailerRegistration: [''],
trailerDimensions: [''],
phoneNumber: [''],
notes: ['']
});
}
> 修改 src/pages/my-details/my-details.ts的 saveForm函数如下:
saveForm(): void {
let data = this.myDetailsForm.value;
//this.dataService.setMyDetails(data);
}
确实,表单不是世界上最刺激的东西(最起码大部分人会这么认为),但是对于移动应用来讲他是极度重要的组件之一,所以了解他们的工作方式非常重要。学会使用Form Builder可以让你的表单更好管理更强大,但是有时候,一个简单的[(ngModel)]就足够了。
在下一节课,我们将学习稍有有趣一点的东西,也稍微复杂一点:我们来实现Google Maps!