# -- 第八课：导航

如果你有Ionic 1或者Angular 1的背景，那么你以前应该处理过URL，状态等之间的路由导航。Ionic在这个上的专注点是使用一个导航栈，他引入了**pushing 压入**视图到**navigation stack导航栈**和**popping 弹出**。在具体了解Ionic 2中是如何具体实现这个之前，我们先来了解一下这个概念。

## 压入与弹出

想象一下你的**root page 根页面**是一张画有小猫的纸，然后你将这张纸放到桌子上。那么现在他是桌子上唯一的一张纸，你现在正在从上往下盯着他。由于他目前是桌子上唯一的一张纸，所以你可以看到这个小猫图片： ![小猫](https://877130497-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LBtuaet5GRr7L6p_j6_%2F-LBty0R2-90Yy_bs85eB%2F-LBty39NZdGjrPNyKI8V%2F1.8.1.jpg?generation=1525685967165777\&alt=media)

现在，我们假设你想看另一张不同的纸（也就是去到另一个页面），那么你会将另一张纸**压入**到你的纸张栈上面。我们就假设这是一张小狗的图片吧，你拿出另一张纸，然后将他放到小猫上面： ![小狗](https://877130497-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LBtuaet5GRr7L6p_j6_%2F-LBty0R2-90Yy_bs85eB%2F-LBty3A9IwJxIiBohvMY%2F1.8.2.jpg?generation=1525685967146762\&alt=media)

小猫还在那里，但是我们已经看不到他了，因为他在小狗后面。我们更深入一点，假设我们**压入**一张小牛的图片，看起来应该是这样的： ![小牛](https://877130497-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LBtuaet5GRr7L6p_j6_%2F-LBty0R2-90Yy_bs85eB%2F-LBty3DMjXZ-Z1I2z_Go%2F1.8.3.jpg?generation=1525685967096403\&alt=media)

小猫和小狗都在那里，但是小牛在最上面所以我们只看得到小牛。现在我们稍微倒过来一点。由于纸张都是按顺序的堆起来的，所以我们可以非常容易的通过**弹出**来回溯他们。如果你想回到小狗的图片，你可以**弹出**纸张栈，移除当前顶部的纸张（小牛）。如果你想回到小猫的图片，你可以再次**弹出**纸张栈，也就是移除当前顶部的小狗图片。现在我们回到了开始的地方。\
你应该看到了这种导航方式对维护历史记录多方便，在导航到子视图的时候非常好理解，但是讲**压入**和**弹出**就没那么好理解了。有时候你想直接回到某个页面而不触发这个变更。（例如，进入应用之前的登录界面，甚至是通过菜单改变的小部分区域）\
在本例中，我们更改根页面，以我们桌子上的图片打比方，即忽略当前栈中的其他图片只关心桌子上当前显示的图片：\
![小牛](https://877130497-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LBtuaet5GRr7L6p_j6_%2F-LBty0R2-90Yy_bs85eB%2F-LBty3EHbuz_07mFVqo-%2F1.8.3.jpg?generation=1525685967135493\&alt=media)

上面的例子中，我看到一个小牛的页面作为根页面，与将他显示到栈顶来比，它只有它自己。\
刚开始，将页面设置为**根页面**来导航还是将他**压入**栈来进行导航很难理解。一般来讲，如果你想要转到的页面上当前页面的子页面，或者如果你想使用导航的回退功能，那么用**压入**的方式。举例，当我正在浏览艺术家列表，然后点击其中一个以显示详情就需要将他**压入**。当我浏览了大量页面表单然后点击‘下一个’进入到表单的第二页的时候，我就需要压入那个第二页了。\
如果你想要转到的页面不是当前视图的子页面，或者他是应用的不同部分，这个时候你就可以使用设置**根页面**的方式了。例如，当你有一个在进入应用之前的登录也，在用户登录验证成功之后，你就需要该变更应用的根目录里。如果你有一个侧边菜单有**Dashboard**，**Shop**，**About**和**Contact**选项，在用户选择这些选项的时候你就可以设置相应的**根页面**。\
始终记住**根页面和根组件不一样**，普通来讲，根组件（在**app.module.ts**里定义的）定义了根页面是什么 -- 根页面在应用中到处都可以改，然后跟组件不能更改。

## Ionic 2中的基本导航

好了，我们了解了原理，所以现在我们来深入一个实际的Ionic 2范例看一下**压入**，**弹出**以及设置**根页面**以及如何**在页面之间传递参数**。\
所有这些的核心是Ionic提供的**NavController**。你会经常看到Ionic 2应用中导入它：

```typescript
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
    selector: 'home-page',
    templateUrl: 'home.html'
})
export class HomePage {

    constructor(public nav: NavController) {

    }
}
```

我们注入了**NavController**然后创建了他的应用，这样我们在类里面可以使用它。也许你猜到了，NavController帮助我们控制导航 -- 所以我们来看一下如何使用他来**压入**和**弹出**。\
压入页面需要一个页面，将他放到导航栈的顶部（这样将会把它设置为当前页面），大概是这样的：

```typescript
this.nav.push(SecondPage);
```

这里用到了我们之前创建的**NavController**引用，你只需要提供需要导航的页面的引用就可以了，当然这个页面也需要在页头导入：

```typescript
import {SecondPage} from '../second-page/second-page';
```

同时添加到**app.module.ts**的*entryComponents*和*declarations*数组里。当触发压入代码的时候，应用将会转到新的页面。当你压入一个页面的时候，导航栏（假定你有）会自动出现一个‘返回’按钮，所以通常你是不同担心通过弹出导航回之前的页面，因为‘返回’按钮自动帮你完成了这个操作。\
有些情况下你需要手动弹出一个页面，你可以这样去做：

```typescript
this.nav.pop();
```

非常简单，对不对？我之前讲过，还有其他方法来切换页面，也就是设置根页面。如果你看一眼**app.ts**的时候你发现下面这行代码：

```typescript
rootPage: any = MyPage;
```

在根元件里面声明*rootPage*会设置根页面，因为根组件的模板是这样的：

```markup
<ion-nav [root]="rootPage"></ion-nav>
```

这样我们将会设置的root属性为*rootPage*定义的值。当你在应用的任何角落想要更改根页面的时候，可以通过我们的老朋友**NavController**来实现 —— 你只需要调用他的*setRoot*方法：

```typescript
this.nav.setRoot(SecondPage);
```

### 页面之前端的参数

移动应用的一个基本需求是指页面之间传递参数。一个很常见的例子是当使用‘Master Detail’模式（也就是你有一个列表的条目，在你点击其中一个条目的时候跳转到另一个页面显示选中条目详情）的时候。当你导航到详情页面的时候，我们需要知道我们要展示的条目的数据，这些数据需要从之前的页面传过来。在Ionic 2中，可以通过**NavParams**来实现。首先，你需要在压入（setRoot里面也可以用）调用的时候传入需要传递的数据：

```typescript
this.nav.push(SecondPage, {
    thing1: data1,
    thing2: data2
});
```

这跟我们之前做的是一模一样的，除了增加了一个额外的参数，这个参数是一个包含了我们需要传递到**SecondPage**的数据的对象。然后，在接受页面，我们导入**NavParams**然后将它们注入到构造器：

```typescript
import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';

@Component({
    selector: 'second-page',
    templateUrl: 'second-page.html'
})

export class SecondPage {
    constructor(nav: NavController, navParams: NavParams){

    }
}
```

然后你就可以通过以下方式来获取传入的参数：

```typescript
this.navParams.get('thing1');
```

## 导航组件

Ionic提供的一些组件都有不一样的导航行为。这些不是核心导航概念，但是在应用中它会和导航相冲突。所以，我们来看一下他们是什么和何时使用它们。

### 模态框 Modals

也许你已经熟悉了模态框的理念。在网络开发中，模态框基本上是一些将所有其他内容挡住后面的弹出框。通常模态框有‘lightbox’样式，一个暗化的背景和聚焦的内容区域。\
Ionic里面的模态框也是一样的，他在你的内容之上弹出，但是他看起来跟其他正常页面没有区别。通常来讲，当你想给用户一个启动然后直接移除而不是回退的视图的时候，你会用到模态框，而不是压入页面。\
模态框有一个很酷的能力就是他支持者移除的时候向启动他的页面传回数据。例如，你可以创建一个这样的模态框（记得导入和注入**ModalController**）：

```typescript
let myModal = modalCtrl.create(MyPage);
myModal.present();
```

注意，模态框是‘呈现’的，而不是压入到导航栈。当我们想要从模态框传回一些数据的时候，我们只要在他呈现之前给他添加*onDismiss*处理器就可以了：

```typescript
let myModal = modalCtrl.create(MyPage);

myModal.onDismiss(data => {
    console.log(data);
});

myModal.present();
```

这样，当模态框移除的时候，我们可以使用他回传的数据做些事情了。移除模态框的方法是在模态框内部调用以下方法：

```typescript
this.view.dismiss();
```

此处的*view*是**ViewController**的引用，他和**NavController**差不多也需要导入和注入到构造器。如果我们想在隐藏的时候往*onDismiss*里面传递数据的话，我们需要这样去操作：

```typescript
let data = {
    thing1: "value1",
    thing2: "value2"
};

this.view.dismiss(data);
```

现在，*data*对象就被传到到启动模态框的页面里面的*onDismiss*处理器里面了。

### 标签页

标签页是一个非常流行的组件，对我们应用里的导航有很大的冲击。标签页的使用非常简单，在模板里面这么用就可以了：

```markup
<ion-tabs>
    <ion-tab [root]="tab1Root" tabTitle="Tab 1" tabIcon="navigate"></ion-tab>
    <ion-tab [root]="tab2Root" tabTitle="Tab 2" tabIcon="person"></ion-tab>
    <ion-tab [root]="tab3Root" tabTitle="Tab 3" tabIcon="bookmarks"></ion-tab>
</ion-tabs>
```

然后在类定义中定义这样去定义页面：

```typescript
tab1Root: any = TabOne;
tab2Root: any = TabTwo;
tab3Root: any = TabThree;

constructor(){

}
```

需要记住的是每个标签页有他自己的**根页面**。你可以这个去想，切换标签页相当于切换不同的根页面，然后在每个标签页里压入和弹出页面。在标签页布局中，可以在每个页签之前切换，每个页签维护了他自己的历史记录。

### 侧边菜单

侧边菜单实际上做的跟导航没有什么不同，他就是一个UI元素而已，但是他很方便，普通，可以在其中放一些按钮然后通过按钮导航到其他页面（切换页面实际上是手动通过**setRoot**和**push**实现的）。在应用中添加侧边菜单非常简单，这样像下面这样修改你的根组件的模板就可以了：

```markup
<ion-menu [content]="content">
    <ion-content>
        <ion-list>
            <button ion-item (click)="openPage(homePage)">
            Home
            </button>
        </ion-list>
    </ion-content>
</ion-menu>

<ion-nav id="nav" #content [root]="rootPage"></ion-nav>
```

我们使用来创建一个菜单，同时也需要告诉它我们会附加什么上去。这个就是为什么我们&#x5C06;*\[content]*&#x5C5E;性设置为一个布局变量*content*，然后在中添&#x52A0;*#content*来定义*content*。基本上这就是说是我们的主要内容区域，我们想让内容在其中显示。

关于Ionic 2的导航多少其他的需要去学习的，但是只要你学好了本课程的这些基本知识，那么在大部分情况下你都不会有什么问题。
