【Appetite】ionic3实录(七)次页实现及分析解决问题
上一节提到几个问题,现在我们逐一来解释一下:
问题一:initSwiper方法为什么放在获取数据之后?它放在其它地方可以吗?此外,它上面为什么会放个this.cd.detectChanges()?
initSwiper() {
new Swiper('.wheel .swiper-container', {
slidesPerView: 2,
initialSlide: 1,
watchActiveIndex: true,
centeredSlides: true,
resizeReInit: true,
keyboardControl: true,
grabCursor: true
});
}
一般一些js插件,是依托dom的。我们观察initSwiper方法,第一个参数'.wheel .swiper-container'
其实是个选择器,所以它也是依托dom操作的,此外,由于我们使用了数据绑定,this.vm.dessertSlides
的值更新会影响到dom,所以应该在数据更新从而使得dom更新完成后再调用initSwiper方法。
angular的脏检测机制是基于一定条件和时间的,在给this.vm.dessertSlides
赋值,dom还没更新完成就调用initSwiper方法不一定会获得想要结果的,所以在此之前调用手动检测方法this.cd.detectChanges()
强制检测并刷新dom。
问题二:为什么用[hidden],不用*ngIf或者ngSwitch?
因为前者只是隐藏,而后两者是会移除。也就是说,前者只会初始化一次,而后两者每次显示都需要重新初始化,进一步说,若使用后两者方式,对于这里用到的swiper,它不是一个angular封装起来的组件,不会自动初始化,我们每次显示它时都需要显式调用一下initSwiper方法,这就有点繁琐还耗费性能。同理,有些组件要注意类似情况。这并不是说都不要用*ngIf,因为当页面元素太多时会容易导致界面卡顿,所以没用的多余元素应该移除,此时就用它。一句话说就是根据情况合理选择显示控制方法。
问题三:<div class="swiper-container">标签内容能否换成注释掉的<ion-slides>内容?
这是当然可以的,因为ion-slides组件组件最终也是生成<div class="swiper-container">标签代码。
问题三的延伸:既然ion-slides已经是封装起来的控件了,我们用它不是可以省掉不少代码?
是的,在大多场景中可以直接用ion-slides,只是会有个别坑要解决,比如我们改动下ts和html文件。
ts中把initSwiper方法相关内容移除:
import { Component, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core';
import { NavController, Slides, PopoverController } from 'ionic-angular';
import { AboutProvider } from '../../providers/about/about';
import { ChatPage } from '../contact/chat/chat';
declare let Swiper: any;
@Component({
selector: 'page-about',
templateUrl: 'about.html'
})
export class AboutPage {
@ViewChild(Slides) slides: Slides;
vm: {
dessertSlides: any[] , //轮播数据源
dessertList: any[], //甜点列表数据源
selectedSegment: string //segment选择对象
} = {
dessertSlides: [],
dessertList: [],
selectedSegment: 'one'
};
constructor(public navCtrl: NavController, private aboutProvider: AboutProvider, private cd: ChangeDetectorRef, private popoverController: PopoverController) {
}
ionViewDidLoad() {
this.getDessertSlides();
this.getDessertList();
}
/**
* 获取甜点轮播图片
*/
getDessertSlides(){
return this.aboutProvider.getDessertSlides().then((rep: any) => {
this.vm.dessertSlides = rep.result;
return rep;
});
}
/**
* 获取甜点列表
*/
getDessertList(){
return this.aboutProvider.getDessertList().then((rep: any)=>{
this.vm.dessertList = rep.result;
return rep;
});
}
}
}
html里用ion-slides:
<ion-content>
<div [hidden]="vm.selectedSegment != 'one'" class="wheel">
<ion-slides centeredSlides="true" slidesPerView="2" initialSlide="1" zoom="false" >
<ion-slide *ngFor="let item of vm.dessertSlides">
<img [src]="item.src" />
</ion-slide>
</ion-slides>
<div text-center *ngFor="let item of vm.dessertList" padding-left padding-right>
<h5><strong>{{item.title}}</strong></h5>
<p>{{item.desc}}</p>
<img [src]="item.src" style="height: 140px; width: 100%;" />
</div>
</div>
<div [hidden]="vm.selectedSegment !='two'" >
<div text-center *ngFor="let item of dessertList" >
<h5>{{item.title}}</h5>
<p>{{item.desc}}</p>
<img [src]="item.src" style="height: 140px; width: 100%;" />
</div>
</div>
<div [hidden]="vm.selectedSegment != 'three'">
三
</div>
<div [hidden]="vm.selectedSegment != 'four'">
四
</div>
</ion-content>
执行看下效果:
数据未正确应用
对比上一节的实际效果图,会发现这里只显示了两个图片,什么原因呢?是因为该组件在异步获取到数据this.vm.dessertSlides
前已完成了初始化了,这样新数据其实没有应用到,为了处理这种情况,我们改造一下html,为ion-slides
组件加上一段*ngIf="vm.dessertSlides && vm.dessertSlides.length>0"
:
<ion-slides centeredSlides="true" slidesPerView="2" initialSlide="1" zoom="false" *ngIf="vm.dessertSlides && vm.dessertSlides.length>0">
<ion-slide *ngFor="let item of vm.dessertSlides">
<img [src]="item.src" />
</ion-slide>
</ion-slides>
这样当新数据有值且长度大于0时,才会显示ion-slides
组件,进而内部进行初始化,此时运行看效果如下,只是此时有个比较突兀的2图向3图切换效果,有兴趣的可以试试: