最终效果以下:javascript
简单的代码以下:html
<ion-header> <ion-navbar> <ion-title>slide-tab-demo</ion-title> </ion-navbar> <div> <task-slide (slideClick)="slideClick($event)" [slides]="tabs" [selectedIndex]="navSelectedIndex" [taskType]="segment" [pageNumber]="4"></task-slide> </div> </ion-header> <div style="height: 100%;;background-color:#999"> <ion-slides #slidesRef (ionSlideDidChange)="slideChanged($event)"> <ion-slide *ngFor="let slide of slides; let i = index;"> <div class="list has-header" id="{{i}}"> <ion-label>{{slide.name}}</ion-label> </div> </ion-slide> </ion-slides> </div>
ts代码:java
import { Component, ViewChild } from '@angular/core'; import { IonicPage, NavController, NavParams, Slides, Tabs } from 'ionic-angular'; @Component({ selector: 'page-slide-tab-demo', templateUrl: 'slide-tab-demo.html', }) export class SlideTabDemoPage { @ViewChild("slidesRef") slidesRef: Slides; @ViewChild("tabsRef") tabsRef: Tabs; slides: any[] = []; currentTabId = "tab1"; tabs: any[] = []; navSelectedIndex = 0; constructor( public navCtrl: NavController, public alert: Alert, public navParams: NavParams ) { this.slides = [{ name: '头条新闻' }, { name: '娱乐新闻' }, { name: '体育新闻' }]; this.tabs = [{ _name: "头条", num: 99 }, { _name: "娱乐", num: 0 }, { _name: "热点", num: 0 }, { _name: "体育", num: 4 }, { _name: "财经", num: 0 }, { _name: "汽车", num: 0 }, { _name: "时尚", num: 0 }]; } slideClick(slideIndex) { this.slidesRef.slideTo(slideIndex); } slideChanged($event) { let currentIndex = this.slidesRef.getActiveIndex(); // this.alert.showAlert('sss'); this.navSelectedIndex = currentIndex; } }
简陋版slides+slides总结:能应付通常业务需求不繁琐的页面显示,但体验很差node
一:滚动条问题,因为ion-slides的高度是以子页面ion-slide的高度为最终同一高度,当页面间高度不一致的时候,会致使高度矮的页面出现一大段空白背景,若是业务场景须要slide上拉刷新的时候,就要把空白背景拉完才会触发刷新效果。git
解决办法:经过自定义指令获取屏幕高度减去上下tab栏来固定ion-slides的高度,但这时候会出现双滚动条,content一个,slides一个可能会出现其余的坑。github
二:首页点击须要跳到指定的页面很差控制,在开发过程当中,会出现点击滑动失效的问题,缘由不明。ionic
既然官方不提供,确定有各路大神提供牛逼的组件,这个组件封装涵盖了angular的大部分核心思想及ionic页面生命周期,大神都说能写出来这个,其余组件随便封装。ide
ionic2-super-tabs introduction优化
基本使用上边都有说明,简单说一下this
一、哪里用到super-tabs,就要在对应的module下引用
SuperTabsModule.forRoot()
二、页面充分用到懒加载[root]="xxPage",这里的Page就无需再次在根模块declarations、entryComponents、exports再次声明,直接在ts里xxpage : string = 'XXPage',字符串导入便可
三、进入子页面都会默认把先后一个页面同时渲染,有点相似ngIf(如6个子页面,进入第一个页面会渲染第一第二个页面,进入第二个页面会渲染第二第三个页面),因此若是请求数据太多要注重优化
四、父子页面的继承,只有第一次进入页面this的指向才是父页面,其他的操做都是指向每个子页面,下面坑继续说
目录结构 业务场景(三个super-tabs同时存在task页面上)
因为一开始作这种需求是经过文首用的slides+slides的方法,不少都是经过this去全局控制每个页面的状态,类型等,这个时候的this是很是好用的。可是用了super-tabs这个组件后this就失去了原来的光环。
缘由剖析:super-tabs采用的是子页面加载,尽管这很好的解决首页跳转页面的问题,但随即而来的继承会致使this失效,每一个task.ts除了第一次渲染this是指向TaskPage外,其他的任何操做都是指向当前的子页面,因此TaskPage的生命周期里面的操做也会到每一个子页面上操做,这样就会重复作相同的事情。
解决办法:在ionViewDidLoad完成第一次加载所需的接口,在每一个子页面都重写一个ionViewDidLoad,ionViewWillEnter周期覆盖父类的声明周期,这样就很好的规避同样操做重复作,其他的功能性操做如点击刷新列表等写在ionViewDidEnter,最好写在每一个子页面。父类private的属性和方法,子类也能继承
task.html
<super-tabs id="taskTabs" #SuperTabs *ngIf="segment === 'newTask'" [config]="{ sideMenu: 'left' }" scrollTabs toolbarColor="orange" toolbarBackground="white" indicatorColor="orange" badgeColor="yellow" (tabSelect)="onTabSelect($event)"> <super-tab [swipeBackEnabled]="false" [root]="waitAcceptPage" title="待受理({{newTaskCountObj.distributionWaitAccept}})" status="waitAcceptPage"></super-tab> <super-tab [root]="waitAppointmentPage" title="待预定({{newTaskCountObj.waitAppointment}})" status="waitAppointmentPage"></super-tab> <super-tab [root]="waitPickupPage" title="待提货({{newTaskCountObj.waitPickUp}})" status="waitPickupPage"></super-tab> <super-tab [root]="waitSignPage" title="待签收({{newTaskCountObj.waitSign}})" status="waitSignPage"></super-tab> <super-tab [root]="hadSignPage" title="已签收({{newTaskCountObj.doSign}})" status="hadSignPage"></super-tab> <super-tab [root]="cancelledPage" title="已取消({{newTaskCountObj.invalid}})" status="cancelledPage"></super-tab> <super-tab [root]="allTaskPage" title="所有({{newTaskCountObj.all}})" status="allTaskPage"></super-tab> </super-tabs>
task.ts (三个生命周期,大量运用到eventproxy神器)
注: this.navCtrl.getActiveChildNavs()用于获取是否有子类,无则undefined
self.navCtrl.getActiveChildNav().getActiveTab().getViews();获取子页面实例对象ViewController
ionViewDidLoad() { let self = this; let instance; /*tslint:disable*/ console.log('task----load') /*tslint:enable*/ //详情页返回删除数据 // 监听详情完成操做事件 // 若是要根据详情操做动态删除记录,详情操做结束派发事件‘finish_operate_in_details’ try { window['epInstance'].unbind('finish_operate_in_details').bind('finish_operate_in_details', (option) => { // 监遵从详情进入任务父类页面 window['epInstance'].unbind('main_task_page_didenter').bind('main_task_page_didenter', (data) => { // 获取子页面实例对象 let views = self.navCtrl.getActiveChildNav().getActiveTab().getViews(); if (views && views[0]) { instance = views[0]['instance']; } let taskData = instance.taskArrTemp[option.segment][option.saveStatu] || []; window['epInstance']['emit']('refresh_task_node_count', option.segment); // 动态删除任务记录 taskData.forEach((item, index) => { if (item.id === option.taskId || item.taskId === option.taskId) { taskData.splice(index, 1); } }); }); }); } catch (e) { } // 叫我刷新我就刷新 try { window['epInstance'].unbind('refresh_main_task_page').bind('refresh_main_task_page', (data) => { let childInst = this.getChildPageInstnce(); if (childInst) { childInst.selectInterface(false); } }); //刷新节点数量 window['epInstance'].unbind('refresh_task_node_count').bind('refresh_task_node_count', (segment) => { this.getAppNodeTypeCount(segment); }); } catch (e) { /*tslint:disable*/ /*tslint:enable*/ } } ionViewWillEnter() { //今日提醒 this._todayTask = this.global.getValue('todayTask'); // 详情页页面离开生命周期(ionViewDidLeave)派发leave_from_details事件。配合finish_operate_in_details事件控制动态删除记录 window['epInstance'].unbind('leave_from_details').bind('leave_from_details', (data) => { window['epInstance']['emit']('main_task_page_didenter', null); }); // 因为pageTaskItem已经继承了eventproxy实例,在pageTask页面再次继承该实例会被pageTaskItem的实例覆盖,因此要在全局定义变量 window['epInstance'].unbind('toggle_call_panel').bind('toggle_call_panel', (data) => { if (data) { this.toggleCallPanel = true; Object.assign(this.taskPhone, data); } else { this.toggleCallPanel = false; } }); // 导航 window['epInstance'].unbind('toggle_nav_panel').bind('toggle_nav_panel', (data) => { if (data) { this.toggleNavPanel = true; Object.assign(this.taskNav, data); } else { this.toggleNavPanel = false; } }); let activePages = this.navCtrl.getActiveChildNavs(); // 控制子类不要重复执行代码 if (this.global.getValue('segment') && activePages && activePages.length) { let intervalTime = 10; // 首次进入渲染较慢,延迟跳转,临时使用解决方案 // https://github.com/ionic-team/ionic/issues/12401 等待ionic-angular版本更新解决问题 if (this.firstEntry) { //首次进入控制 intervalTime = 400; } // 外面定时器是保证先渲染好页面再转换任务状态,渲染新的supertabs setTimeout(() => { this.segment = this.global.getValue('segment'); this._tabIndex = this.global.getValue('tabIndex'); // 这里的定时器是由于ngIf销毁到生成新的supertabs慢,致使滑动会寻找销毁的tabs,从而报错 setTimeout(() => { if (this.superTabs) { this.superTabs.slideTo(this._tabIndex); } }, intervalTime) this.global.remove('segment'); this.global.remove('tabIndex'); }, intervalTime); } this.firstEntry = false; // 清空搜索筛选内容 window['search_input_content'] = ''; window['search_input_screen'] = ''; window['search_input_statu'] = 'all'; window['search_input_time'] = ''; } ionViewDidEnter() { // 子类才调用 let activePages = this.navCtrl.getActiveChildNavs(); if (!activePages || (activePages && !activePages.length)) { window['epInstance']['emit']('refresh_task_node_count'); } } /** * 获取子页面实例 */ getChildPageInstnce() { let instance; let childNav = this.navCtrl.getActiveChildNav(); if (!childNav || (childNav && !childNav.getActiveTab)) { return; } let views = childNav.getActiveTab().getViews(); if (views && views[0]) { instance = views[0]['instance']; } return instance; }
all-task.ts (一个子页面作覆盖父类的该周期)
ionViewDidLoad() { } ionViewWillEnter() { this.saveStatu = 'all' this.queryStatu = ''; this.requestData(this.newTask, false, ''); }
以上是最大的一个坑,下面是项目需求对组件的扩展
1、上边tab栏文字输入有长度要求,当超过长度会出现显示异常解决方案:
参考文章: