第六课:新建一个自定义的管道和所有相片的飞页(Flipbook)

本节课中,我们将制作自定义管道用来更友好的展示照片的拍摄日期,我们也将完成应用的关键部分之一也就是slideshow。虽然他没有拍照那么复杂,但是他算是整个应用的灵魂。

创建一个自定义管道

我们在本书的基础部分已经涵盖了管道是什么,这里还是跟你简单温习一下:管道实际上允许我们在数据进行展示之前对他进行修改。
我们此处的目标是创建一个标签显示此照片上几天之前拍摄的,这样,“3 天前”,“10 天前”。目前而言,我们已经在照片的数据上存储了拍摄日期,不幸的是,他看起是酱紫的:

  1. Sun Mar 06 2016 00:40:02 GMT+1000 (AEST)

也就是天底下最不友好的日期显示方式。所以,这里是@Pipe最好的使用情景:我们创建一个管道用来接收丑陋的日期格式,转换到“X 天前”这样的格式,然后返回之。
我们先看看如何创建这个管道,然后在慢慢讨论。
> 修改 src/pipes/days-agp.ts 为如下:

  1. import { Injectable, Pipe } from '@angular/core';
  2. @Pipe({
  3. name: 'daysAgo'
  4. })
  5. @Injectable()
  6. export class DaysAgo {
  7. transform(value, args?) {
  8. let now = new Date();
  9. let oneDay = 24 * 60 * 60 * 1000;
  10. let diffDays = Math.round(Math.abs((value.getTime() - now.getTime())/(oneDay)));
  11. return diffDays;
  12. }
  13. }

@Pipe装饰器里面我们提佛那个一个名字daysAgo,意思就是在模板中可以直接通过使用这个名字作为关键字来使用这个管道。使用管道的时候,永远都需要传入数据,然后数据都会被传入transform函数也就是上面的value。我们会把我们的照片对象的Date对象传入到这个管道,这就是我们这里要做的。同时注意可以通过管道传入额外的参数,也就是我们为什么用args?,因为这不是强制要求传入的参数。
首先我们来一点数学魔法得出照片上几天之前拍摄的,(Stack Overflow也有这个,我的解决方式可能比他丑点儿),然后返回他。返回的值就是将被渲染到模板上去的内容。
如果你还记得之前的课的话,我们已经在app.module.ts中设置好这个管道了。
> 修改home.html中的photo项为如下:

  1. <ion-item>
  2. <img [src]="photo.image" />
  3. <ion-badge item-right light>{{photo.date | daysAgo}} days ago</ion-badge>
  4. </ion-item>

注意了(译者敲了下黑板),我们加了这个句:

  1. {{photo.date | daysAgo}}

这样写会把photo.date传入到daysAgo管道,然后将daysAgo返回的东西展示最这里。最终结果是一个带有类似“5 天前”的徽章。

为所有照片创建一个Slideshow

我们已经创建好了Slideshow的模板,所以现在我们需要制作一些逻辑来循环和展示所有照片,同时我们也要添加一个打开slideshow的途径(重启途径也要)。
我们还是先从playSlideshow函数开始,这样我们可以真正的打开页面。
> 修改 src/pages/home/home.ts的 playSlideshow 函数为如下:

  1. playSlideshow(): void {
  2. if(this.photos.length > 1){
  3. let modal = this.modalCtrl.create(SlideshowPage, {photos:
  4. this.photos});
  5. modal.present();
  6. } else {
  7. let alert = this.simpleAlert.createAlert('Oops!', 'You need at least two photos before you can play a slideshow.');
  8. alert.present();
  9. }
  10. }

跟创建警告提示框类似,我们给用户撞见了一个模态框页面。首先,我们创建了一个Modal,然后通过NavController呈现到用户面前。在这里,我们使用已经创建好的SlideshowPage来创建模态框,也将所有的照片数据传入进去。这样我们可以在Slideshow页面上获取到这些数据。
注意,我们只有在用户有超过1张照片的时候才会触发他(因为没有或者只有1张照片的slideshow就不是真的slideshow了,对吧?),否则会显示警告提示框。
现在,我们来定义Slideshow页的类定义。这个类挺小的,所以先全部贴出来,然后分段讲解。
> 修改 src/pages/slideshow/slideshow.ts 为如下:

  1. import { NavParams, ViewController } from 'ionic-angular';
  2. import { Component, ElementRef, ViewChild } from '@angular/core';
  3. @Component({
  4. selector: 'page-slideshow',
  5. templateUrl: 'slideshow.html'
  6. })
  7. export class SlideshowPage {
  8. @ViewChild('imagePlayer') imagePlayer: ElementRef;
  9. imagePlayerInterval: any;
  10. photos: any;
  11. constructor(public navParams: NavParams, public viewCtrl: ViewController) {
  12. this.photos = this.navParams.get('photos');
  13. }
  14. ionViewDidEnter(){
  15. this.playPhotos();
  16. }
  17. closeModal(){
  18. this.viewCtrl.dismiss();
  19. }
  20. playPhotos(){
  21. let imagePlayer = this.imagePlayer.nativeElement;
  22. let i = 0;
  23. //Clear any interval already set
  24. clearInterval(this.imagePlayerInterval);
  25. //Restart
  26. this.imagePlayerInterval = setInterval(() => {
  27. if(i < this.photos.length){
  28. imagePlayer.src = this.photos[i].image;
  29. i++;
  30. }
  31. else {
  32. clearInterval(this.imagePlayerInterval);
  33. }
  34. }, 500);
  35. }
  36. }

这里有些东西可能你不大熟悉。我们导入了NavParams服务可以用他拿到在创建的时候传过来的数据。你可以看到我们在构造器里面通过this.navParams.get(‘photos’);获取数据。这里另一个奇怪的东西是ViewController。我们需要用到这个来隐藏模态框(我们这里定义了一个closeModal函数提供给模板里面的按钮使用)。
这里最重要的函数是playPhotos,这就是我们循环所有照片然后在页面上相应改变对于的图片元素的地方。首先,我们通过之前设置好的@ViewChild引用得到图片元素(之前模板里面通过 #本地变量 的方式定义的)的引用,我们清理了当前正在运行的所有定时器(防止用户在slideshow还在播放的时候突然重启slideshow)。然后我们开始循环this.photos里的照片。如果还剩下照片可以展示,图片的src属性将会使用下一张照片的数据更新,如果没有的话,就会清理计时器。上面代码中,定时器的间隔是500毫秒,也就是0.5秒,我们可以根据个人喜好通过调整这个数值来调整slideshow的快和慢。(甚至给用户提供选择来控制)
playPhotos函数上ionViewDidEnter函数自动触发的,ionViewDisEnter函数上在进入视图的时候自动执行的,这个函数在我们每次进入视图的时候都会执行。

总结

这是另一节比较小的课,但是这里讲的东西都蛮酷的。至此,应用的大部分主体功能都完成了,我们只要加上一些边边角角就可以了。当然,我们还是得梅花他,但是下节课我们要学习的是整合本地通知和社交分享功能。