Skip to content

Files

Latest commit

8e4ed31 · Jan 10, 2022

History

History
1811 lines (1051 loc) · 40.5 KB

4.md

File metadata and controls

1811 lines (1051 loc) · 40.5 KB

四、构建应用

快速介绍

我最喜欢爱奥尼亚的一点是,它很容易覆盖很多领域,几乎没有任何信息过载。到目前为止,我们已经看到了如何构建一个项目,添加页面,并将这些页面连接起来,以及如何使用 Ionic 视图和浏览器进行测试,并部署到 Ionic 专业版。

事实上,这本身就是一个令人印象深刻的壮举,因为这一切都是在很少的几页中概述的。这是可能的,因为框架适应这种简洁程度,而不牺牲基本内容。

但是像生活中的一切一样,学习新东西的最好方法——不管内容多么简洁——是通过构建一些东西。这就是我们在本章中要做的。

为了充分利用这一章,我强烈建议您在前进的过程中查看伟大的 Ionic 组件文档,您也可以在此处找到。

在这一章的最后,你可以找到下载这款应用完整源代码的链接。

在我们开始之前

但是在我们开始之前,我想提一下,我喜欢让事情变得简单——因为它不仅允许更容易的知识保留,而且将范围限制在完成某件事所需的必要要素上。

考虑到这一理念,让我们尽可能简单地创建我们的简洁库应用程序。这意味着我们的应用程序不会使用数据库或执行 AJAX 请求,所以所有需要的数据都将被硬编码。

当然,你以后可能会修改应用程序,使其更加复杂,并包括这样的功能,但这完全取决于你。

图书馆页面

应用的页面将是应用的主要入口点。正如我们最近所看到的,它包含了侧边菜单,带有指向应用程序其他主页面的链接。

我非常喜欢在应用程序中有一个侧菜单;不过,我认为页面的主要内容应该包括一些同样指向或引用 app 其他主页面的卡片,比如图书路径——所以为了实现这一点,我们在页面增加一些卡片。

为了添加一些显示在这些卡片上的数据,我们需要打开 library.ts 文件并添加以下代码。

代码清单 4-a:库文件上的卡片数据

  cardData =
  [

  {title: 'Paths', description: 'Learning paths', 

  pushPage: 'PathsPage'},

  {title: 'Books', description: 'Books in the library', 

  pushPage: 'BooksPage'}
  ];

该代码可以添加到constructor之前的LibraryPage类中。下面是使用此代码更新的 library.ts 文件的外观:

代码清单 4-b:更新的库文件

  import {
  Component }
  from '@angular/core';
  import {
  IonicPage, NavController, NavParams }
  from 'ionic-angular';
  import {
  PathsPage }
  from '../../pages/paths/paths';
  import {
  BooksPage }
  from '../../pages/books/books';

  @IonicPage()
  @Component({

  selector: 'page-library',

  templateUrl: 'library.html',
  })
  export class LibraryPage {

  cardData =
  [

  {title: 'Paths', description: 'Learning paths', 

  pushPage: 'PathsPage'},

  {title: 'Books', description: 'Books in the library', 

  pushPage: 'BooksPage'}

  ];

  constructor(public navCtrl: NavController, public navParams: NavParams)   

  { }

  ionViewDidLoad() {

  console.log('ionViewDidLoad LibraryPage');

  }
  }

我们将在 library.html 文件中使用cardData对象,以便构建一些卡片在用户界面中显示。接下来让我们做这个。

让我们打开library.html并在ion-content标签内添加以下代码。

代码清单 4-c:Ionic 卡标记

  <ion-list *ngFor="let card of cardData"
  no-padding>

  <ion-card>

  <div class="card-title" 

  [navPush]=card.pushPage>{{card.title}}

  </div>

  </ion-card>
  </ion-list>

让我们快速检查一下。我们添加了ion-list组件,以便能够遍历cardData对象的各个元素——这可以通过使用ngFor来实现。

card对象表示的cardData的每个元素随后在ion-card组件中被引用。card.pushPage属性用于允许用户导航到对应于card.title的页面。

进行此更改后,library.html 文件是这样的:

代码清单 4-d:更新的 library.html 文件

  <ion-header>

  <ion-navbar>

  <button ion-button menuToggle>

  <ion-icon name="menu"></ion-icon>

  </button>

  <ion-title>Succinctly Library</ion-title>

  </ion-navbar>
  </ion-header>

  <ion-content padding>

  <h3>Welcome to the Succinctly Series</h3>

  <ion-list *ngFor="let card of cardData"
  no-padding>

  <ion-card>

  <div class="card-title" 

  [navPush]=card.pushPage>{{card.title}}</div>

  </ion-card>

  </ion-list>

  <button ion-button secondary menuToggle>Toggle
  Menu</button>
  </ion-content>

我们可以通过运行ionic serve命令在浏览器中预览 UI 的变化。

图 4-a:更新的库页面

为了测试爱奥尼亚角的导航功能,让我们点击路径书籍卡片。正如预期的那样,如图 4-b 所示,相应的页面被显示并添加到导航堆栈中。这是可能的,因为card.pushPage引用了爱奥尼亚将要挂载的页面。

图 4-b:装载路径页面

我们可以卸载导航堆栈上的当前页面,只需单击后退箭头按钮,返回到上一个页面——在本例中是页面。

我们现在有我们的图书馆页面工作;然而,我们仍然可以改进卡组件的外观,所以接下来让我们这样做。

制作更好的卡片

俗话说,“长相不是一切,但确实很重要。”在当今竞争激烈的应用市场中,用户被选择宠坏了,好看、有吸引力的应用比没有吸引力的应用更有可能留在人们的设备上。

因此,创造良好的用户体验有些重要,为了做到这一点,拥有一个相对吸引人的用户界面是关键。

由于卡片组件将是我们应用程序的重要组成部分,并且可能会在应用程序的大部分页面上使用,所以花一些时间创建一张外观更好的卡片是值得的。这不仅会让我们的 app 看起来更好,还会给用户提供更好的体验。

让我们开始吧。下面的代码将是我们新的和改进的卡的基础。

代码清单 4-e:更好的卡片

  <ion-card>

  <ion-item>

  <h2>Card Header</h2>

  <p>Card Short Description</p>

  </ion-item>

  <img src="" />

  <ion-card-content>

  <ion-card-title>

  Card Title

  </ion-card-title>

  <p>

  Card Long Description

  </p>

  </ion-card-content>

  <ion-row>

  <button ion-button [navPush]=card.pushPage>{{card.title}}</button>

  </ion-row>
  </ion-card>

现在让我们用这个代码来替换library.html文件中的前一张卡片。library.html 文件现在将如下所示。

代码清单 4-f:更新的 library.html 文件

  <ion-header>

  <ion-navbar>

  <button ion-button menuToggle>

  <ion-icon name="menu"></ion-icon>

  </button>

  <ion-title>Succinctly Library</ion-title>

  </ion-navbar>
  </ion-header>

  <ion-content padding>

  <h3>Welcome to the Succinctly Series</h3>

  <ion-list *ngFor="let card of cardData"
  no-padding>

  <ion-card>

  <ion-item>

  <h2>Card Header</h2>

  <p>Card Short Description</p>

  </ion-item>

  <img src="" />

  <ion-card-content>

  <ion-card-title>

  Card Title

  </ion-card-title>

       <p>

  Card Long Description

  </p>

  </ion-card-content>

  <ion-row>

  <button ion-button 

  [navPush]=card.pushPage>{{card.title}}

  </button>

   </ion-row>

  </ion-card>

  </ion-list>

  <button ion-button secondary menuToggle>Toggle
  Menu</button>
  </ion-content>

现在让我们执行ionic serve命令,看看这是什么样子。

图 4-c:更新的库页面

这看起来比以前版本的卡片好一点,因为它包含一个图像(我们仍然需要指定)、各种文本元素和一个按钮。

从用户界面的角度来看,还不是超级性感,但至少可以给用户提供更多的信息和更好的体验。

有了这个新的卡片结构和轮廓,让我们添加路径书籍页面所需的卡片元素,并升级图书馆页面的外观和感觉。

更新库页面

现在我们有了一个新的卡片样式,让我们完成更新页面。从用户界面的角度来看,图书馆页面的 HTML 标记已经基本完成;然而,我们需要使这些文本元素对每张卡片都是动态的。

让我们从扩展 library.ts 文件中的cardData对象开始,并用下面的代码替换它。

代码清单 4-g:更新的卡片数据对象

  cardData =
  [

  { img:
  '../../assets/imgs/logo.png',

  header: 'Paths', 

  short: 'Learning
  paths', 

  title: '', 

  description: 'Paths
  are books organized by related topics', 

  buttontxt: 'Explore
  Paths',

  pushPage: 'PathsPage'

  },

  { img:
  '../../assets/imgs/azure.jpg',

  header: 'Books', 

  short: 'Books
  to read', 

  title: '', 

  description: 'Over
  130 books on technologies that matter', 

  buttontxt: 'Browse
  Books',

  pushPage: 'BooksPage'

  }

  ];

正如您可能在代码清单 4-g 中注意到的,我还在\assets\imgs\文件夹中放入了一些图像。所以,在更新了cardData对象之后,我们的 library.ts 文件现在看起来如下。

代码清单 4-h:更新后的库文件

  import {
  Component }
  from '@angular/core';
  import {
  IonicPage, NavController, NavParams }
  from 'ionic-angular';
  import {
  PathsPage }
  from '../../pages/paths/paths';
  import {
  BooksPage }
  from '../../pages/books/books';

  @IonicPage()
  @Component({

  selector: 'page-library',

  templateUrl: 'library.html',
  })
  export class LibraryPage {

  cardData =
  [

  { img:
  '../../assets/imgs/logo.png',

  header: 'Paths', 

  short: 'Learning
  paths', 

  title: '', 

  description: 'Paths
  are books organized by related topics', 

  buttontxt: 'Explore
  Paths',

  pushPage: 'PathsPage'

  },

  { img:
  '../../assets/imgs/azure.jpg',

  header: 'Books', 

  short: 'Books
  to read', 

  title: '', 

  description: 'Over
  130 books on technologies that matter', 

  buttontxt: 'Browse
  Books',

  pushPage: 'BooksPage'

  }

  ];

  constructor(public navCtrl: NavController, public navParams: NavParams)

  { }

  ionViewDidLoad() {

  console.log('ionViewDidLoad LibraryPage');

  }
  }

更新后的标记 library.html 现在如下所示。

代码清单 4-i:更新的 library.html 文件

  <ion-header>

  <ion-navbar>

  <button ion-button menuToggle>

  <ion-icon name="menu"></ion-icon>

  </button>

  <ion-title>Succinctly Library</ion-title>

  </ion-navbar>
  </ion-header>
  <ion-content padding>

  <h3>Welcome to the Succinctly Series</h3>

  <ion-list *ngFor="let card of cardData"
  no-padding>

        <ion-card>

  <ion-item>

  <h2>{{card.header}}</h2>

  <p>{{card.short}}</p>

  </ion-item>

  <img src="{{card.img}}" />

  <ion-card-content>

  <ion-card-title>{{card.title}}</ion-card-title>

  <p>{{card.description}}</p>

  </ion-card-content>

  <ion-row center>

  <ion-col text-center>

  <button ion-button [navPush]=card.pushPage>

  {{card.buttontxt}}

  </button>

  </ion-col>

  </ion-row>

  </ion-card>

  </ion-list>
  </ion-content>

让我们再次执行ionic serve命令,看看这些变化是什么样子的。

图 4-d:更新的库页面

太棒了。我们的应用程序看起来比以前好得多——小的改变会有很大的帮助。

有了我们的页面,让我们专注于更新我们的路径页面。

更新路径页面

如前所述,我们将对应用程序将要使用的数据进行硬编码。路径页面只不过是一个小的主题集合,书籍被组织到其中——彼此相关的主题,从而形成一个学习路径。

为了将书籍主题组织成路径,我们需要在path . ts文件中创建一个pathData对象,该对象将指示哪些书籍主题对应于哪些学习路径。

让我们如下定义pathData对象。

代码清单 4-j:路径数据对象

  pathData =
  [

  { name: 'Database',

  books: [

  { title: 'Force.com Succinctly', page: this.pg },

  { title: 'Azure Cosmos DB and DocumentDB
  Succinctly', 

  page: this.pg }

  ]

  },

  { name: 'Web Development',

  books: [

  { title: 'Force.com Succinctly', page: this.pg }

  ]

  },

  { name: 'Cloud',

  books: [

  { title: 'Azure Cosmos DB and DocumentDB
  Succinctly', 

  page: this.pg }

  ]

  },

  { name: 'Microsoft & .NET',

  books: [

  { title: 'Microsoft Bot Framework Succinctly', page: this.pg },

  { title: 'Twilio with C# Succinctly', page: this.pg },

  { title: 'Customer Success for C# Developers
  Succinctly', 

  page: this.pg },

  { title: 'Data Capture and Extraction with
  C# Succinctly', 

  page: this.pg },

  ]

  },

  { name: 'Mobile Development',

  books: [

  { title: 'Twilio with C# Succinctly', page: this.pg }

  ]

  },

  { name: 'Miscellaneous',

  books: [

  { title: 'Customer Success for C# Developers
  Succinctly', 

  page: this.pg }

  ]

  }

  ];

我们在这里所做的是创建了许多类别(学习路径),并在每个类别下定义了许多与之对应的书籍。每本书都指向图书概览页面——这是用this.pg定义的。

简单地说——鉴于这个系列有 130 多本书——我刚刚添加了我写的书名,这些书名在我写这本书的时候已经出版了。

定义了pathData JSON 数据对象后,我们的 paths.ts 文件现在看起来如下。

代码清单 4-k:更新的路径文件

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

  @IonicPage()
  @Component({

  selector: 'page-paths',

  templateUrl: 'paths.html',
  })
  export class PathsPage {

  pg =
  'BookoverviewPage';

  pathData =
  [

  { name: 'Database',

  books: [

  { title: 'Force.com Succinctly', page: this.pg },

  { title: 'Azure Cosmos DB and DocumentDB
  Succinctly', 

  page: this.pg }

  ]

  },

  { name: 'Web Development',

  books: [

  { title: 'Force.com Succinctly', page: this.pg }

  ]

  },

  { name: 'Cloud',

  books: [

  { title: 'Azure Cosmos DB and DocumentDB
  Succinctly', 

  page: this.pg }

  ]

  },

  { name: 'Microsoft & .NET',

  books: [

  { title: 'Microsoft Bot Framework Succinctly', page: this.pg },

  { title: 'Twilio with C# Succinctly', page: this.pg },

  { title: 'Customer Success for C# Developers
  Succinctly', 

  page: this.pg },

  { title: 'Data Capture and Extraction with
  C# Succinctly', 

  page: this.pg },

  ]

  },

  { name: 'Mobile Development',

  books: [

  { title: 'Twilio with C# Succinctly', page: this.pg }

  ]

  },

  { name: 'Miscellaneous',

  books: [

  { title: 'Customer Success for C# Developers
  Succinctly', 

  page: this.pg }

  ]

  }

  ];

  constructor(public navCtrl: NavController, public navParams: NavParams) 

  { }

  ionViewDidLoad() {

  console.log('ionViewDidLoad PathsPage');

  }
  }

现在让我们开始更新路径页面的用户界面。为了做到这一点,打开paths.html文件。让我们用以下内容替换现有的标记。

代码清单 4-1:更新的 paths.html 文件

  <ion-header>

  <ion-navbar>

  <ion-title>Paths</ion-title>

  </ion-navbar>
  </ion-header>
  <ion-content padding>

  <ion-list *ngFor="let path of pathData"
  no-padding>

  <ion-card>

  <ion-item>

           <ion-row center>

  <ion-col text-center>

  <h1><b>{{path.name}}</b></h1>
                      </ion-col>

  </ion-row>

  </ion-item>

  <ion-list *ngFor="let book of path.books"
  no-padding>

  <ion-card-content>

  <ion-row center>

  <ion-col text-center>

  <h2>{{book.title}}</h2>

  </ion-col>

  </ion-row>

                <ion-row center>

  <ion-col text-center>

  <button ion-button [navPush]=book.page>

  Check book

  </button>

  </ion-col>

  </ion-row>

  </ion-card-content>

  </ion-list>

  </ion-card>

  </ion-list>
  </ion-content>

正如我们所看到的,我在 paths.html 文件中添加了一些标记,所以它看起来相对不错,并且仍然足够简单,易于阅读和理解。

我添加了两个ngFor循环——一个在学习路径上循环,另一个在每个学习路径的书名上循环。

代码的其余部分只是常规标记,在常规 HTML 标记中使用了常见的标准 ion 组件,如ion-cardion-itemion-card-contention-rowion-col

如果我们现在执行ionic serve命令,我们的路径页面如下所示。

图 4-e:更新路径页面

有了一点代码,我们现在有了一个好看的页面和按学习路径组织的书籍。

更新书籍页面

现在让我们继续更新书籍页面,它将与路径页面非常相似。不同的是书籍页面将只包含图书馆内书籍的信息,而不指定它们与哪个学习路径相关。

你可以把图书页面看作是路径路径的精简版本——我们在标记中不再使用两个ngFor实例,而是只需要一个来循环所有的图书标题。

首先,让我们在 books.ts 文件中定义一个booksData对象,如下所示。

代码清单 4-m:图书数据对象

  booksData =
  [

  { title: 'Force.com Succinctly', page: this.pg },

  { title: 'Azure Cosmos DB and DocumentDB
  Succinctly', 

  page: this.pg },

  { title: 'Microsoft Bot Framework Succinctly', page: this.pg },

  { title: 'Twilio with C# Succinctly', page: this.pg },

  { title: 'Customer Success for C# Developers
  Succinctly', 

  page: this.pg },

  { title: 'Data Capture and Extraction with
  C# Succinctly', 

  page: this.pg }

  ];

定义了booksData对象后,现在让我们相应地更新 books.ts 文件。

代码清单 4-n:更新后的图书文件

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

  @IonicPage()
  @Component({

  selector: 'page-books',

  templateUrl: 'books.html',
  })
  export class BooksPage {

  pg =
  'BookoverviewPage';

  booksData =
  [

  { title: 'Force.com Succinctly', page: this.pg },

  { title: 'Azure Cosmos DB and DocumentDB
  Succinctly', 

  page: this.pg },

  { title: 'Microsoft Bot Framework Succinctly', page: this.pg },

  { title: 'Twilio with C# Succinctly', page: this.pg },

  { title: 'Customer Success for C# Developers
  Succinctly', 

  page: this.pg },

  { title: 'Data Capture and Extraction with
  C# Succinctly', 

  page: this.pg }

  ];

  constructor(public navCtrl: NavController, public navParams: NavParams) 

  { }

  ionViewDidLoad() {

  console.log('ionViewDidLoad BooksPage');

  }
  }

现在让我们开始更新书籍页面的用户界面。为此,打开books.html文件。让我们用以下内容替换现有的标记。

代码清单 4-o:更新的 books.html 文件

  <ion-header>

  <ion-navbar>

  <ion-title>Books</ion-title>

  </ion-navbar>
  </ion-header>
  <ion-content padding>

  <ion-list *ngFor="let book of booksData"
  no-padding>

  <ion-card>

  <ion-card-content>

  <ion-row center>

       <ion-col text-center>

  <h2><b>{{book.title}}</b></h2>
                      </ion-col>

  </ion-row>

  <ion-row center>

  <ion-col text-center>

  <button ion-button [navPush]=book.page>

  Check book

  </button>

  </ion-col>

  </ion-row>

  </ion-card-content>

  </ion-card>

  </ion-list>
  </ion-content>

可以看到,就像path页面一样,这个页面在标准 HTML 标记中使用了ion-cardion-itemion-card-contention-rowion-col等 ion 框架组件。

要查看书籍页面的外观,让我们像平常一样执行ionic serve命令。

图 4-f:更新后的图书页面

太好了——我们已经完成了页。我们列表的下一页是图书概览页面。

图书概览页面

我们在构建应用程序方面达到了一个有趣的里程碑。到目前为止,我们创建和更新的所有页面几乎都是独立的,这意味着它们使用的对象和变量位于定义页面本身的同一个 Angular 组件中。

有了图书概览页面,这种情况不会再有了。因为我们的应用程序库中有几个书名,所以为每个书名创建一个单独的页面是不切实际的。

有那么一会儿,想象一下我们的应用列表中所有的简洁地说就是系列标题。我们将需要创建超过 130 页的内容——这是不切实际或不可行的。

换句话说,图书概览页面必须是一个通用页面,我们可以将希望页面显示的图书细节作为参数传递给它。

在我们更新图书概览页面之前,我们首先需要谈一谈 Ionic 页面中的导航参数——也称为navParams

命名参数

导航参数,顾名思义,让我们将信息从一个页面传递到另一个页面。这非常有用,并且定义了我们如何将特定于记录的数据发送到页面的过程——这正是我们需要的,以便使图书概览页面足够通用,可以用来显示任何给定图书的数据。

这也意味着我们必须稍微修改路径书籍页面,以便为这两个页面上列出的书籍传递必要的导航参数。

但是在我们对路径书籍页面进行任何调整之前,让我们首先通过查看一些通用代码示例来了解导航参数实际上是如何工作的,以及它们是如何传递的。

指定导航参数的第一种方法是添加一个navParams指令,如下面的代码所示。

代码清单 4-p:导航参数指令

  <button ion-button [navPush]=book.page [navParams]=book.params>

  Check book
  </button>

我们能够将book.params对象分配给navParams指令,从而将所需的导航参数传递给book.page

我们实现这一点的另一种方法是通过代码,如下例所示。

代码清单 4-q:通过代码传递导航参数

  pushPage() {

  this.navCtrl.push(OtherPage, {name: 'Force.com Succinctly', 

  author: 'Ed
  Freitas', url:
  'https://…'});
  }

既然我们知道了如何将参数传递给我们正在导航的页面,那么我们实际上是如何接收这些参数并使用它们做一些事情的呢?

答案很简单:基本上,每个 ion-Angular 页面组件构造器都会收到一个navParams参数,这个参数包含了传递给页面的所有导航参数。

所有的 Ionic 角度页面组件都有这个构造函数。这里有一个例子。

代码清单 4-r:页面组件构造器

  constructor(public navCtrl: NavController, public navParams: NavParams) 
  { }

现在我们知道如何将参数传递给页面并使用它们,我们可以理解它们对于我们能够使页面通用是多么重要。

现在,我们准备继续工作并更新图书概述页面。

更新图书概览页面

顾名思义,图书概览页面将只包含该书的简短概述,以及一些基本信息,如作者姓名、出版日期和页数。

为了理解图书概览(以及稍后的图书详细信息)页面将如何显示图书信息的层次结构,让我们看看下图。

图 4-g:分层图书信息

考虑到这一点,让我们相应地更新这个页面——我们可以通过将这些基本信息添加到页面现有的 HTML 标记中来实现,如下所示。

代码清单 4-s:更新的 bookoverview.html 文件

  <ion-header>

  <ion-navbar>

  <ion-title>Overview</ion-title>

  </ion-navbar>
  </ion-header>
  <ion-content padding>

  <ion-card>

  <ion-card-content>

  <ion-row center>

  <ion-col text-center>

  <h1>{{book.title}}</h1>

  </ion-col>

  </ion-row>

  <ion-row center>

  <ion-col text-center>

  <h2><b>Author:</b> {{book.overview.author}}</h2>

  </ion-col>

  </ion-row>

  <ion-row center>

  <ion-col text-center>

  <h3><b>Published:</b> 

  {{book.overview.published}}</h3>

  </ion-col>

  </ion-row>

  <ion-row center>

           <ion-col text-center>

  <h3>{{book.overview.numpages}}
  pages</h3>
                  </ion-col>

  </ion-row>

  <ion-row center>

  <ion-col text-center>

  <button ion-button 

                   [navPush]= book.overview.details.page 

  [navParams]=book>

  Book details

  </button>

  </ion-col>

  </ion-row>

  </ion-card-content>

  </ion-card>
  </ion-content>

基于这个例子,我们可以看到页面使用的是一个book数据对象,其中包含了书籍的信息。特定于书籍概述的属性,如出版日期、页数和作者,被归入book.overview属性。

我们可以看到这个页面调用了书籍详情页面——这是通过将book.overview.details.page分配给navPush来完成的。

注意整个book对象通过navParams传递到书籍详情页面。

我们已经准备好了书籍概述页面用户界面,这很酷,但是BookoverviewPage类实际上是如何处理从书籍路径页面传递的navParams的呢?

这是可能的,因为包含通过导航参数传递的实际数据的navParams.data对象被分配给了BookoverviewPage类的constructor中的一个book对象,如我们在下面的代码中所见。

代码清单 4-t:更新的书籍概述

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

  @IonicPage()
  @Component({

  selector: 'page-bookoverview',

  templateUrl: 'bookoverview.html',
  })
  export class BookoverviewPage {

  public book;

  constructor(public navCtrl: NavController, public navParams: NavParams)    

  {

  this.book = navParams.data;

  }

  ionViewDidLoad() {

  console.log('ionViewDidLoad BookoverviewPage');

  }
  }

这解释了图书概览页面如何能够接收和使用调用页面发送的navParams

然而,我们仍然不知道书籍路径页面如何实际调用书籍概述页面,以及到底什么被传递为navParams—这是我们接下来要探索的。

将导航参数添加到书籍页面

为了能够从书籍页面调用书籍概述页面,我们需要能够将book对象分配给navParams

因此,本质上,我们的 Books 页面 HTML 标记现在应该如下所示。

代码清单 4-u:更新的 books.html 文件

  <ion-header>

  <ion-navbar>

  <ion-title>Books</ion-title>

  </ion-navbar>
  </ion-header>
  <ion-content padding>

  <ion-list *ngFor="let book of booksData"
  no-padding>

  <ion-card>

  <ion-card-content>

  <ion-row center>

  <ion-col text-center>

  <h2><b>{{book.title}}</b></h2>
                      </ion-col>

  </ion-row>

  <ion-row center>

  <ion-col text-center>

  <button ion-button 

  [navPush]=book.page [navParams]=book>

                    Overview

  </button>

  </ion-col>

  </ion-row>

  </ion-card-content>

  </ion-card>

  </ion-list>
  </ion-content>

如你所见,唯一新的是在button上,book对象被分配给了navParams——这是BookoverviewPage类后来使用的。

这很酷——但是,我们不要忘记路径页面也调用了书籍概述页面,所以本质上,书籍路径页面应该共享book对象——因为两个页面都需要使用它。对book对象的任何更改都会影响这两个页面。

为此,我们需要修改 books.ts 文件,并使book对象可访问路径页面。但是因为我们有不止一个book对象,我们真正拥有的是一组book对象。

因此,让我们在 books.ts 中定义一个BooksData数组,它将包含我们的应用程序将处理的各种book对象。

代码清单 4-v:BookData 数组

  export let BooksData =
  [
  { title: 'Force.com Succinctly', page: 'BookoverviewPage', 
  overview: { author: 'Ed Freitas', numpages: 100,

  published: 'December 08, 2017',
  details: { page: 'BookdetailPage',
  pic: 
  'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-
  cover/forcedotcomsuccinctly.jpg?v=09012018021233',
  url: 'https://www.syncfusion.com/ebooks/forcedotcomsuccinctly' } }
  },

  { title: 'Azure Cosmos DB and DocumentDB
  Succinctly', 
  page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 103,

  published: 'May 23, 2017', 
  details: { page: 'BookdetailPage',
  pic: 
  'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-
  cover/Azure_Cosmos_DB_and_DocumentDB_Succinctly.jpg?v=09012018021233',
  url:      'https://www.syncfusion.com/ebooks/azure_cosmos_db_and_documentdb_succinctly' } }
  },

  { title: 'Microsoft Bot Framework Succinctly', page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 109,

  published: 'May 02, 2017',
  details: { page: 'BookdetailPage',
  pic: 'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-cover/Microsoft_Bot_Framework_Succinctly.jpg?v=09012018021233', 
  url: 'https://www.syncfusion.com/ebooks/azure_cosmos_db_and_documentdb_succinctly' } }
  },

  { title: 'Twilio with C# Succinctly', page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 107,

  published: 'April 03, 2017',
  details: { page: 'BookdetailPage',
  pic: 'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-cover/Twilio_with_C_sharp_Succinctly.jpg?v=09012018021233', 
  url: 'https://www.syncfusion.com/ebooks/twilio_with_c_sharp_succinctly' } }
  },

  { title: 'Customer Success for C# Developers
  Succinctly', page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 97,

  published: 'October 03, 2016',
  details: { page: 'BookdetailPage',
  pic: 'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-cover/Customer_Success_Succicntly.jpg?v=09012018021233', 
  url: 'https://www.syncfusion.com/ebooks/customer_success_for_c_sharp_developers'} } },

  { title: 'Data Capture and Extraction with C#
  Succinctly', page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 85,

  published: 'September 19, 2016',
  details: { page: 'BookdetailPage',
  pic: 'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-cover/Data_Capture_And_Extraction_Succinctly.jpg?v=09012018021233',
  url: 'https://www.syncfusion.com/ebooks/data_capture_and_extraction_with_c_sharp_succinctly' } }
  }
  ];

请注意,BooksData数组中的每个book对象包含一般信息,但也包含一个overview对象,以及其中的一个details对象。因此,本质上来说,BooksData数组的每个实例——每个book对象——都包含该书的一般信息、该书的概述(由BookoverviewPage类使用)和该书的细节(将由BookdetailPage类使用——我们将很快看到这一点)。

这就是修改后的 books.ts 文件现在的样子。

代码清单 4-w:更新的书籍文件

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

  export let BooksData =
  [
  { title: 'Force.com Succinctly', page: 'BookoverviewPage', 
  overview: { author: 'Ed Freitas', numpages: 100,

  published: 'December 08, 2017',
  details: { page: 'BookdetailPage',
  pic: 
  'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-
  cover/forcedotcomsuccinctly.jpg?v=09012018021233',
  url: 'https://www.syncfusion.com/ebooks/forcedotcomsuccinctly' } }
  },

  { title: 'Azure Cosmos DB and DocumentDB
  Succinctly', 
  page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 103,

  published: 'May 23, 2017', 
  details: { page: 'BookdetailPage',
  pic: 
  'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-
  cover/Azure_Cosmos_DB_and_DocumentDB_Succinctly.jpg?v=09012018021233',
  url:      'https://www.syncfusion.com/ebooks/azure_cosmos_db_and_documentdb_succinctly' } }
  },

  { title: 'Microsoft Bot Framework Succinctly', page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 109,

  published: 'May 02, 2017',
  details: { page: 'BookdetailPage',
  pic: 'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-cover/Microsoft_Bot_Framework_Succinctly.jpg?v=09012018021233', 
  url: 'https://www.syncfusion.com/ebooks/azure_cosmos_db_and_documentdb_succinctly' } }
  },

  { title: 'Twilio with C# Succinctly', page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 107,

  published: 'April 03, 2017',
  details: { page: 'BookdetailPage',
  pic: 'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-cover/Twilio_with_C_sharp_Succinctly.jpg?v=09012018021233', 
  url: 'https://www.syncfusion.com/ebooks/twilio_with_c_sharp_succinctly' } }
  },

  { title: 'Customer Success for C# Developers
  Succinctly', page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 97,

  published: 'October 03, 2016',
  details: { page: 'BookdetailPage',
  pic: 'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-cover/Customer_Success_Succicntly.jpg?v=09012018021233', 
  url: 'https://www.syncfusion.com/ebooks/customer_success_for_c_sharp_developers'} } },

  { title: 'Data Capture and Extraction with
  C# Succinctly', page: 'BookoverviewPage',
  overview: { author: 'Ed Freitas', numpages: 85,

  published: 'September 19, 2016',
  details: { page: 'BookdetailPage',
  pic: 'https://cdn.syncfusion.com/conteimg/downloads/ebook/ebook-cover/Data_Capture_And_Extraction_Succinctly.jpg?v=09012018021233',
  url: 'https://www.syncfusion.com/ebooks/data_capture_and_extraction_with_c_sharp_succinctly' } }
  }
  ];

  @IonicPage()
  @Component({

  selector: 'page-books',

  templateUrl: 'books.html',
  })
  export class BooksPage {

  booksData =
  BooksData;

  constructor(public navCtrl: NavController, public navParams: NavParams) 

  { }

  ionViewDidLoad() {

  console.log('ionViewDidLoad BooksPage');

  }
  }

请注意如何将BooksData数组分配给booksData对象,该对象在 books.html 的ion-list组件的ngFor指令中使用,以便遍历每本书。

将导航参数添加到路径页面

为了能够从路径页面调用书籍概览页面,我们需要能够将book对象分配给navParams

路径页面 HTML 标记现在应该如下所示。

代码清单 4-x:更新的 paths.html 文件

  <ion-header>

  <ion-navbar>

  <ion-title>Paths</ion-title>

  </ion-navbar>
  </ion-header>
  <ion-content padding>

  <ion-list *ngFor="let path of pathData"
  no-padding>

  <ion-card>

  <ion-item>

  <ion-row center>

  <ion-col text-center>

  <h1><b>{{path.name}}</b></h1>
                      </ion-col>

  </ion-row>

  </ion-item>

  <ion-list *ngFor="let book of path.books"
  no-padding>

  <ion-card-content>

  <ion-row center>

  <ion-col text-center>

  <h2>{{book.title}}</h2>

  </ion-col>

  </ion-row>

  <ion-row center>

  <ion-col text-center>

  <button ion-button [navPush]=book.page 

  [navParams]=book>

  Check book

  </button>

  </ion-col>

  </ion-row>

  </ion-card-content>

         </ion-list>

  </ion-card>

  </ion-list>
  </ion-content>

现在我们已经将book对象分配给了navParams,让我们稍微重构一下之前为 paths.ts 编写的代码。

我们需要这样做,以便利用我们在 books.ts 中创建的BooksData数组,因此我们可以在 paths.ts 中使用它。我们也可以通过在我们的BooksPage import 子句中导入BooksData来做到这一点。以下是重构的 paths.ts 文件。

代码清单 4-y:更新的路径

  import {
  Component }
  from '@angular/core';
  import {
  IonicPage, NavController, NavParams }
  from 'ionic-angular';
  import {
  BooksPage, BooksData }
  from '../../pages/books/books';

  @IonicPage()
  @Component({

  selector: 'page-paths',

  templateUrl: 'paths.html',
  })
  export class PathsPage {

  pg =
  'BookoverviewPage';

  pathData =
  [

  { name: 'Database',

  books: [

  BooksData[0],

  BooksData[1]

  ]

  },

  { name: 'Web Development',

  books: [

  BooksData[0]

  ]

  },

  { name: 'Cloud',

  books: [

  BooksData[1]

  ]

  },

  { name: 'Microsoft & .NET',

  books: [

  BooksData[2],

  BooksData[3],

  BooksData[4],

  BooksData[5]

  ]

  },

  { name: 'Mobile Development',

  books: [

  BooksData[3]

  ]

  },

  { name: 'Miscellaneous',

  books: [

  BooksData[5]

  ]

  }

  ];

  constructor(public navCtrl: NavController, public navParams: NavParams) 

  { }

  ionViewDidLoad() {

  console.log('ionViewDidLoad PathsPage');

  }
  }

请注意,我已经使用从书籍页面导入的BooksData对象重构了pathData中的books数组对象。这样,我们不需要重复任何书籍数据。

在现实场景中,您可能会使用 RESTful API 从网络服务器获取类似BooksData的数据结构,并通过 Angular 服务发出 AJAX 请求。然而,应该保持对模块化和可重用性的同样关注。

我们现在已经更新了所有调用书籍概述页面的现有页面。我们应用程序剩下的唯一一点就是更新图书详细信息页面,我们很快就会更新。

在此之前,让我们再次运行ionic serve命令,检查从路径页面访问书籍概述页面时的外观。

图 4-h:从路径页面调用的图书概览页面

现在,让我们做同样的事情,但是通过从书籍页面导航到书籍概述页面。

图 4-i:从图书页面调用的图书概览页面

我们可以看到,我们能够导航到两个不同书籍的书籍概述页面:一个通过路径页面,另一个通过书籍页面。

我们需要做的唯一一件事就是更新 Book Detail 页面,这是我们接下来要做的。

更新图书详细信息页面

我们已经进入了这个应用的最后阶段。图书详情页面,顾名思义,负责显示每本书的details对象的属性。

为此,让我们修改现有的bookdetail.html文件,并添加以下标记。

代码清单 4-z:更新的 bookdetail.html 文件

  <ion-header>

  <ion-navbar>

  <ion-title>Details</ion-title>

  </ion-navbar>
  </ion-header>
  <ion-content padding>

  <ion-card>

  <ion-card-content>

  <img src="{{book.overview.details.pic}}" />

  <ion-row center>

  <ion-col text-center>

  <button ion-button>

  Get book (For future use)</button>

  </ion-col>

  </ion-row>

  </ion-card-content>

  </ion-card>
  </ion-content>

您可能已经注意到,我们只显示pic属性。这是因为我想留给你一个挑战,让你自己去解决。

挑战是使用templateUrl属性并将其添加到按钮中,这样当在浏览器窗口中单击它时,它就可以打开,您可以直接从 Syncfusion 网站上抓取图书。

我给你一个提示——你可以通过在应用浏览器中启动来做到这一点——这可以通过使用 InAppBrowser Cordova 插件来实现。更多细节可以在这里找到,大家随意看看。

Book Detail 页面的 UI 完成后,现在让我们继续关注 bookdetail.ts 文件,并对其进行相应的更新。我们可以在代码清单 4-aa 中看到更新后的代码。

代码清单 4-aa:更新的 bookdetail.ts 文件

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

  @IonicPage()
  @Component({

  selector: 'page-bookdetail',

  templateUrl: 'bookdetail.html',
  })
  export class BookdetailPage {

  public book;

  constructor(public navCtrl: NavController, public navParams: NavParams)    

  {

  this.book = navParams.data;

  }

  ionViewDidLoad() {

  console.log('ionViewDidLoad BookdetailPage');

  }
  }

我们在这里真正做的是获取navParams.data对象(从图书概览页面接收到的实际导航参数)并将参数分配给book对象,这就是 bookdetail.html 使用的。

现在让我们再次执行ionic serve命令,以便查看我们所做的更改。

图 4-j:图书详细信息页面

太棒了——我们现在有了一个全面运行的应用程序。我们已经完成了所有的步骤,以便创建一个小的,但完全工作的 Ionic 应用程序——恭喜!

完整源代码

你可以在这里下载这个 app 的完整源代码。请注意,这包括完整的项目,可以在您选择的开发环境中打开——在我的例子中,我使用了 Visual Studio Code。

还要注意,这个项目不包括\node_modules 文件夹,所以为了安装所需的 Node.js 模块,需要进入项目文件夹运行npm install –g ionic cordova命令。

总结

这是一个漫长而有趣的章节,因为我们已经看到了如何浏览各种应用程序页面,并赋予应用程序其特性和功能。

考虑到 Ionic 框架的广度,不可能在一本书里涵盖所有内容。然而,通过我们构建这个应用程序的一步一步的方法,我们已经探索了您需要的大部分要素,以便开始使用这个框架并创建您自己的应用程序。

在下一章,也是最后一章,我们将快速回顾各种各样的 ional 资源,这些资源应该可以帮助你扩展对这个框架的理解,这样你就可以自己继续探索了。