Skip to content

Latest commit

 

History

History
340 lines (319 loc) · 16.8 KB

51-ionic指令.md

File metadata and controls

340 lines (319 loc) · 16.8 KB

导航

接下来出场的是导航相关的组件。导航组件有许许多都的指令和许许多多的服务。
第一个出场的指令是ion-nav-view
第三章 Ionic CSS组件和导航中,我们已经学习过Ionic状态路由器并了解了他是如何工作的。我们也看过了Ionic中的ion-nav-view是如何向AngularJS里面的ui-view一样工作的。
当app启动的时候,$stateProvider将会查找默认的状态,然后尝试加载ion-nav-view里面对应的模板。

ion-view

接下来出场的是ion-view指令。ion-viewion-nav-view的子类。这个指令用来作为添加页头信息和其他内容的容器。当应用状态改变的时候,会在父容器ion-nav-view内展示对应的视图。
在Ionic中,为改善执行效率,视图都会被缓存起来。当视图离开ion-nav-view之后,他的子元素都将从DOM中移除,他的scope也将和*$watch循环断开连接。 当之前缓存的视图进入ion-nav-view*的时候,他的scope将会重新连接,已有的元素都将重新激活。
新建一个空白模板项目来学习:

ionic start -a "Example 18" -i app.example.eighteen example18 blank

通过cd目录,进入到example18文件夹,然后运行:

ionic serve

接下来,我们将给这个app添加一个路由器,这个路由器有2个状态。打开www/js/app.jsrun方法后添加以下方法:

.config(function($stateProvider, $urlRouterProvider) {
    $stateProvider
    .state('page1', {
        url: '/page1',
        templateUrl: 'page1.html'
    })
    .state('page2', {
        url: '/page2',
        templateUrl: 'page2.html'
    });
    $urlRouterProvider.otherwise('/page1');
})

我们分别给page1page2创建了两个状态。接下来,修改www/index.html的body部分:

<body ng-app="starter">
    <ion-nav-bar></ion-nav-bar>
    <ion-nav-view></ion-nav-view>
    <!-- Templates -->
    <script type="text/ng-template" id="page1.html">
        <ion-view view-title="Title">
            <ion-content>
            <h3>Page 1</h3>
            <button class="button button-dark" ui-sref="page2">Navigate to Page 2</button>
            </ion-content>
        </ion-view>
    </script>
    <script type="text/ng-template" id="page2.html">
        <ion-view view-title="Title">
            <ion-content>
            <h3>Page 2</h3>
            <button class="button button-dark" ui-sref="page1"> Navigate to Page 1</button>
            </ion-content>
        </ion-view>
    </script>
</body>

我们创建了一个供ion-view内容注入的ion-nav-view指令。我们的视图是通过script标签语法创建的。这样一来,AngularJS就不用发起AJAX请求来加载模板来。 但是这么做对于开发者和维护者来说都是很不友好的。
ion-view指令有一个子指令ion-content。每次导入新模板的时候,他都会进行缓存。你可以通过更改ion-view的属性来控制视图状态的外观,同理也可以控制缓存行为。
例如,在以上代码中,我们给ion-view标签添加一个view-title属性。这个值是用作页面标签,同时在没有ion-nav-bar的情况下,也可以用作导航条标题。
如果想要禁用模板缓存,在ion-view上面设置cache-view属性为false就可以了。同时也可以设置hide-nav-bar来控制是否显示nav-bar
加上上面这些属性之后,ion-view看起来差不多是这样子的:

<ion-view view-title="Title" cache-view="false" hide-navbar="false" hide-back-button="true" can-swipe-back="false">

为避免在导航条里面显示返回按钮,我们也可以控制返回按钮的显示。在后续学习ion-nav-bar的时候会学到这一点。
运行以下命令,可以验证目前所有对example18的更改:

ionic serve

Ionic视图事件

Ionic视图有很多生命周期方法,你可以在这些方法里添加你的自定义行为。我们将给example18添加一个run方法,代码如下。 在run方法中,我们给*$ionicview添加了事件监听器。打开www/js/app.js*添加以下代码:

.run(function($ionicPlatform, $rootScope) {
    // View Life Cycle
    $rootScope.$on('$ionicView.loaded', function(event, view) {
        console.log('Loaded..', view.stateName);
    });
    $rootScope.$on('$ionicView.beforeEnter', function(event, view)
    {
        console.log('Before Enter..', view.stateName);
    });
    $rootScope.$on('$ionicView.afterEnter', function(event, view)
    {
        console.log('After Enter..', view.stateName);
    });
    $rootScope.$on('$ionicView.enter', function(event, view) {
        console.log('Entered..', view.stateName);
    });
    $rootScope.$on('$ionicView.leave', function(event, view) {
        console.log('Left..', view.stateName);
    });
    $rootScope.$on('$ionicView.beforeLeave', function(event, view)
    {
        console.log('Before Leave..', view.stateName);
    });
    $rootScope.$on('$ionicView.afterLeave', function(event, view)
    {
        console.log('After Leave..', view.stateName);
    });
    $rootScope.$on('$ionicView.unloaded', function(event, view) {
        console.log('View unloaded..', view.stateName);
    });
})

你可以给一个单独的模块添加多个run方法。

这样,在我们从page1导航到page2的时候,你将会看到下面这样的结果:
run

你可以追踪这些事件的触发顺序,然后根据需求给他们绑定你的自定义行为。

$ionicView.loaded和*$ionicView.unloaded只会在控制器的$rootScope里生效,$scope*里面不会有任何效果。其他的方法可以。

ion-nav-bar

当我们制作多页面应用的时候,ion-nav-bar将会是你的好基友。这个指令负责在你的状态改变的时候更新页面标题栏。在example18中,我们添加了一个简化版的ion-nav-bar
现在,我们来看一下稍微强化版的。在www/index.html中,使用以下代码替换ion-nav-bar

<ion-nav-bar class="bar-assertive">
    <ion-nav-buttons side="left">
        <button class="button button-energized" ng-click="leftyClick()">
        Left Button
        </button>
    </ion-nav-buttons>
    <ion-nav-back-button class="button-clear">
        <i class="ion-arrow-left-c"></i> Back
    </ion-nav-back-button>
    <ion-nav-buttons side="right">
        <button class="button button-energized" ng-click="rightyClick()">
        Right Button
        </button>
    </ion-nav-buttons>
</ion-nav-bar>

在这段代码中,你可以看到ion-nav-bar使用ion-nav-buttons或者ion-nav-back-button作为子指令。
ion-nav-buttons用于将按钮展示在页头导航条。更新后的页面效果如下:
navigation

你可以定义leftyClick()rightyClick()功能以供按钮点击的时候调用。也可以添加一个Header Controller并定义这些方法,然后将Header Controller添加给ion-nav-bar; 或者也可以在root scope里面定义leftyClick()rightyClick(),虽然这个做法不怎么理想化。在实际环境中,根据情况右上角一般显示的是Logout按钮,Option按钮或者Add按钮。
ion-nav-bar也是ion-nav-back-button的宿主。这个指令负责在页面之间导航的时候自动显示返回按钮:
run

看到了吗,返回按钮自动出现了,把左边的按钮从原先的位置挤走了。

ion-nav-bar指令只有在模板内容被包装在ion-view标签里的时候才会正常工作。

那么,这样我们就又回到了ion-view标签的属性来。你可以设置ion-viewhide-nav-bar属性为false;这样将会为当前页面显示导航。或者你可以设置back-buttonfalse以隐藏页面上的返回按钮。
修改后的page2的ion-view如下:

<ion-view view-title="Page 2" hide-nav-bar="false" hide-backbutton="true">

此时,当你从page1导航只page2的时候,你会发现返回按钮不见了:
run

ion-nav-buttons

Ionic对页头里面的按钮提供了细粒度的控制。如果你在ion-view中声明了ion-nav-buttons,在模板中,他们将会覆盖ion-nav-bar指令里面的。
我们将page2模板更新如下:

<script type="text/ng-template" id="page2.html">
    <ion-view view-title="Page 2" hide-nav-bar="false" hide-back-button="true">
        <ion-nav-buttons side="left">
            <button class="button button-calm" ng-click="settingsClick()">
            Settings
            </button>
        </ion-nav-buttons>
        <ion-nav-buttons side="right">
            <button class="button button-calm" ng-click="optionsClick()">
            Options
            </button>
        </ion-nav-buttons>
        <ion-content>
            <h3>Page 2</h3>
            <button class="button button-dark" ui-sref="page1">
            Navigate to Page 1
            </button>
        </ion-content>
    </ion-view>
</script>

记住,我们不是对ion-nav-bar里面的ion-nav-buttons进行更改:

<ion-nav-bar class="bar-assertive">
    <ion-nav-buttons side="left">
        <button class="button button-energized" ng-click="leftyClick()">
        Left Button
        </button>
    </ion-nav-buttons>
    <ion-nav-back-button class="button-clear">
        <i class="ion-arrow-left-c"></i> Back
    </ion-nav-back-button>
    <ion-nav-buttons side="right">
        <button class="button button-energized" ng-click="rightyClick()">
        Right Button
        </button>
    </ion-nav-buttons>
</ion-nav-bar>

保存文件回到浏览器,page1效果如下:
run

page2在模板内显示了ion-nav-button
run

$ionicNavBarDelegate

可以在控制器内控制ion-nav-bar$ionicNavBarDelegate服务也可以。
为了更好的理解,我们给模板添加两个控制器:page1.html的PageOneCtrl和page2.html的PageTwoCtrl
更新后的模板如下:

<script type="text/ng-template" id="page1.html">
    <ion-view ng-controller="PageOneCtrl">
        <ion-content>
            <h3>Page 1</h3>
            <button class="button button-dark" ui-sref="page2">
            Navigate to Page 2
            </button>
        </ion-content>
    </ion-view>
</script>
<script type="text/ng-template" id="page2.html">
    <ion-view ng-controller="PageTwoCtrl">
        <ion-content>
            <h3>Page 2</h3>
            <button class="button button-dark" ui-sref="page1">
            Navigate to Page 1
            </button>
        </ion-content>
    </ion-view>
</script>

注意看我们移除了ion-view上面的所有属性和指令然后给他添加了ng-controller
我们需要去www/js/app.js里面更新这两个生命的控制器:

.controller('PageOneCtrl', function($scope, $ionicNavBarDelegate)
{
    $ionicNavBarDelegate.title('Page 1');
})
.controller('PageTwoCtrl', function($scope, $ionicNavBarDelegate)
{
    $ionicNavBarDelegate.title('Page 2');
    $ionicNavBarDelegate.showBackButton(false);
})

我们给page1page2设置了标题,并且将page2showBackButton设置为false。保存这些修改返回浏览器的时候,就可以看到你预想的结果了。
其他可以通过*$ionicNavBarDelegate*控制的属性有:

  • align:这个是用来指定标题,按钮的排列方向的:left, right, 以及center(默认)
  • showBar:用来设置或者取得ion-nav-bar是否显示

$ionicHistory

另一个非常重要的导航服务是*$ionicHistory*。$ionicHistory持续追踪所有视图并记录用户在视图之间的导航行为。
$ionicHistory的优美之处在于他支持平行历史记录,在这点上,浏览器是按固定顺序存储的。当你的界面有多个标签,每个标签都有一套视图的时候,平行历史记录就会非常有用了。
$ionicHistory可以用来捕获标签级别的历史记录;意思是,当用户选择标签2的时候,在标签2里面进行了一些页面导航,之后选择标签1,然后点击返回按钮;应用不会把用户带回标签2最后显示的页面, 而是导航到标签页的父容器的最后显示页,或者如果当前父容器页面的第一个页面的话就退出了应用。
回到手边的范例,更新PageTwoCtrl为以下:

.controller('PageTwoCtrl', function($scope, $ionicNavBarDelegate, $ionicHistory) {
    $ionicNavBarDelegate.title('Page 2');
    $ionicNavBarDelegate.showBackButton(false);
    console.log($ionicHistory.viewHistory())
})

我们在PageTwoCtrl中注入*$ionicHistory*依赖然后在控制台记录视图切换历史记录。
保存文件,返回浏览器,然后从page1导航到page2,我们可以看到如下展示:
run

viewHistory方法返回了一个对象,里面有backView(也就是之前视图),currentViewHisttories以及应用里面所有其他的视图的所有信息。
可以通过这个对象得到用户是如何导航到当前页面的;在此情景之下,视图历史非常有用。
你依然可以通过*$ionicHistory*服务的函数读取视图历史的独立属性,例如:

  • currentView:返回应用当前页面
  • currentHistoryId:返回当前视图父容器的ID
  • currentTitle:取得或者设置当前视图的标题
  • backView:返回当前视图在历史记录栈里面的上一个视图
  • forwardView:然后历史栈中的下一个视图。前视图当中用户从page1导航至page2,然后返回page1的时候有效。此时page2就是forwardView
  • currentStateName:返回当前状态名

为快速测试以上属性,我们更新PageOneCtrlPageTwoCtrl如下:

.controller('PageOneCtrl', function($scope, $ionicNavBarDelegate,$ionicHistory) {
    $ionicNavBarDelegate.title('Page 1');
    console.log('currentView', $ionicHistory.currentView());
    console.log('currentHistoryId', $ionicHistory.currentHistoryId());
    console.log('currentTitle', $ionicHistory.currentTitle());
    console.log('backView', $ionicHistory.backView());
    console.log('backTitle', $ionicHistory.backTitle());
    console.log('forwardView', $ionicHistory.forwardView());
    console.log('currentStateName', $ionicHistory.currentStateName());
})
.controller('PageTwoCtrl', function($scope, $ionicNavBarDelegate,$ionicHistory) {
    $ionicNavBarDelegate.title('Page 2');
    $ionicNavBarDelegate.showBackButton(false);
    console.log('viewHistory', $ionicHistory.viewHistory());
})

现在,当我们导航到page1的时候,可以记录的属性如下:
run

然后,当导航到Page 2的时候,可以看到早先看到的相同的值:
run

最后,当你再次导航回Page 1的时候,将会看到有forwardView了:
run

我已经为page1和page的ion-view指令设置了 cache-view="false";因此,上面截屏中的backViewnull

$ionicHistory还有3个其他的方法:

  • goBack:默认状态路由返回1个视图的历史距离。你可以传入一个负整数来制定返回多少个视图的历史距离。因为默认值是-1,所以返回的是一个视图的历史距离。如果执行*goBack(-2)*那么将返回两个视图的历史距离。 goBack不会超过历史栈,如果传入的值超过历史栈的话,会直接返回到第一个页面。
  • clearHistory:清除除了当前页面之外的其他历史栈
  • clearCache:清除所有缓存的视图

可以使用*$ionicHistorynextViewOptions*方法来控制下一个视图如何展示。
你可以控制以下选项:

  • disableAnimate:禁用下一个视图的动画效果
  • disableBack:将下一个视图的backView设为null
  • historyRoot:将下一个视图设置为历史栈的根

将以上属性添加到我们的PageOneCtrl

.controller('PageOneCtrl', function($scope, $ionicNavBarDelegate,$ionicHistory) {
    $ionicNavBarDelegate.title('Page 1');
    console.log('currentView', $ionicHistory.currentView());
    console.log('currentHistoryId', $ionicHistory.currentHistoryId());
    console.log('currentTitle', $ionicHistory.currentTitle());
    console.log('backView', $ionicHistory.backView());
    console.log('backTitle', $ionicHistory.backTitle());
    console.log('forwardView', $ionicHistory.forwardView());
    console.log('currentStateName', $ionicHistory.currentStateName());
    $ionicHistory.nextViewOptions({
        disableAnimate: true,
        disableBack: true,
        historyRoot: true
    });
})

此时,导航到page2,控制台的输出如下:
run

你会发现,当点击Navigate to Page 2的时候,动画效果和过度效果都被禁用了,backViewnull,最后views属性只有一个视图:page2
你也可以使用这些选项来控制你的应用的历史状态的行为。