多个组件的使用(Using multiple components)
随着应用程序变得更加复杂,我们需要将代码拆分成多个组件。请记住,组件控制着屏幕上的特定区域。它们还可以用于指定整个屏幕,从而允许我们在应用程序中切换不同的页面。在本节中,我们将介绍可以引入的多个组件。我们还将学习如何使用Angular路由器在视图之间进行导航。
当我们首次讨论组件时,我们注意到组件负责由组件中指定的HTML标签表示的视图。当我们有多个组件和多个视图时,我们需要指定每个视图标签。有两种方法可以组合这些标签。
- 一个组件的标签可以嵌套在另一个组件的模板中,
- 使用Angular路由器将组件映射到URL字符串,并插入同一个标签中。
将一个视图嵌套在另一个视图中的一个简单示例如下。假设我们有一个主组件,定义如下:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>Embedded component</h1>
<embedded-comp></embedded-comp>
`,
})
export class AppComponent { }
注意,<embedded-comp>
标签不是标准的HTML标签。我们可以定义一个新的组件来负责这个标签定义的视图,如下所示:
import { Component } from '@angular/core';
@Component({
selector: 'embedded-comp',
template: `<p>This is an embedded text </p>`,
})
export class SecondComponent { }
请注意,选择器现在是嵌入在主组件中的<embedded-comp>
标签。
我们还没有完成。我们还必须在主模块文件(app.module.ts)中链接这两个定义,如下所示:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { SecondComponent} from './second.component';
@NgModule({
imports: [ BrowserModule],
declarations: [ AppComponent, SecondComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
在app.component.ts文件中的添加部分以加粗字体显示。第一个添加的是Typescript导入语句,用于包含SecondComponent类的定义。第二个添加的是将SecondComponent添加到声明列表中,以允许Angular在运行时将其纳入定义中。为了增加趣味性,这个简单的应用程序将如下显示。
Angular 路由器(The Angular router)
Angular 路由器是 Angular 中的一个功能,允许在多页面应用程序中导航到不同的页面。它将组件映射到特定的 URL,并将它们插入到其他组件中。
在上面的示例中,我们首先有一个简单的页面,使用路由器将第二个页面/组件插入到标题下方。要配置路由器,我们将 <base href="/">
标签添加到 index.html 文件的 <head>
标签中。这将设置应用程序期望的基本 URL。
接下来,我们设置我们的主要 AppComponent 组件如下:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `<h1>路由器示例</h1>
<a routerLink="/page2">第二页</a>...
<router-outlet></router-outlet>
`
})
export class AppComponent { }
这里有两行是新的内容:
- 链接标签有一个
routerLink
属性,指定要插入的页面的相对 URL。路由器定义了相同的属性,可以附加到按钮和其他控件上。 <router-outlet>
标签是路由器预定义的标签,用于指示页面应该插入到何处。新页面将在此标签后插入。
我们可以定义一个简单的第二页,插入到主页中。定义如下:
import { Component } from '@angular/core';
@Component({
template: `<p>这是第二页</p>
<button routerLink="/">返回首页</button>
`
})
export class Page2Component { }
请注意,我们已经添加了另一个 routerLink
属性给按钮,以允许用户导航回主页。
为了将所有内容链接在一起,我们需要更新主模块,导入路由器模块并链接第二页。新的 app.module.ts 如下所示。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { Page2Component} from './page2.component';
const myRoutes: Routes = [
{path: 'page2', component: Page2Component}
];
@NgModule({
imports: [ BrowserModule, RouterModule.forRoot(myRoutes) ],
declarations: [ AppComponent, Page2Component ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
新的代码用粗体标记。请注意以下几点:
- 我们需要导入路由器模块,因为它不在 Angular 核心库中。我们还导入一个称为 Routes 的类,允许我们定义应用程序所有 URL 的行为。
- 在此示例中,我们将应用程序的路由定义为一个常量。这不是强制性的,但在添加更多路由时,它将让代码更清晰。
- 路由是一个 JavaScript 对象数组。此对象中的路径选择器指定了相对 URL,不包括前导斜杠 "/”。组件选择器指定了要用于此 URL 的组件。
- Angular 的导入语句使用 RouterModule.forRoot() 方法来初始化路由,用于在路由器模块中的应用程序。
- 我们还导入 Page2Component 类(TypeScript 导入),并声明生成的 Angular 组件,以便可以在所有链接的组件中使用它。
点击链接后,应用程序如下所示。
(图片的代码为英文,上面代码为了突出文本,把原英文的文本换成了中文)
我们可以通过向组件添加另一个链接来轻松添加另一页(Page3)。
<a routerLink="/page2">Page 2</a>...
<a routerLink="/page3">Page 3</a>
我们需要更新app.module.ts来连接所有代码。加粗的文字是额外的代码。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { Page2Component} from './page2.component';
import { Page3Component} from './page3.component';
const myRoutes: Routes = [
{path: 'page2', component: Page2Component},
{path: 'page3', component: Page3Component}
];
@NgModule({
imports: [ BrowserModule, RouterModule.forRoot(myRoutes) ],
declarations: [ AppComponent, Page2Component, Page3Component ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
我们可以使用路由链接添加一个有用的页面,以便在URL不正确时显示。由于用户可以在浏览器中输入任何URL,我们可以让Web服务器处理常规的404错误,或者引入我们自己的错误页面。在Angular的测试环境中,非法的URL实际上将导致路由器运行时错误。
我们将非法的URL与以下内容匹配,以添加到Routes数据结构中。
const myRoutes: Routes = [
{path: 'page2', component: Page2Component},
{path: 'page3', component: Page3Component},
{path: '**', component: PageNotFoundComponent}
];
我们可以创建一个简单的错误消息在一个新的PageNotFoundComponent组件中。
import { Component } from '@angular/core';
@Component({
template: `<h2>页面未找到!</h2>
<button routerLink="/">首页</button>
`
})
export class PageNotFoundComponent { }
现在,如果我们尝试输入URL“/page4”,我们将收到以下显示结果。
(图片的代码为英文,上面代码为了突出文本,把原英文的文本换成了中文)
很遗憾,我们引入了一个新的问题,通配符URL也将匹配默认URL。在路由器匹配通配符之前,我们需要显式地路由默认URL。路由器按顺序处理Routes数据结构,因此我们需要将通配符放在路由列表的末尾。
现在,我们可以指定一个新的空白组件,或者更有用的是一个"帮助"组件,如下所示:
{path: ‘’, component: HelpComponent, pathMatch: ‘full’}
请注意路由对象中的pathmatch:'full’的添加。这是必要的,以指示空白URL应完全为空白。另一个选项是pathmatch:‘prefix’,在这种情况下,它将匹配所有URL字符串。
我们将借此机会演示重定向路由的用法。我们可以通过更新路由将默认URL重定向到HelpComponent,同时引入一个帮助组件,如下所示:
const myRoutes: Routes = [
{path: ‘page2’, component: Page2Component},
{path: ‘page3’, component: Page3Component},
{path: ‘help’, component: HelpComponent},
{path: ‘’, redirectTo:“/help”, pathMatch: ‘full’}
{path: ‘**’, component: PageNotFoundComponent}
];
一个简单的HelpComponent可以如下所示:
import { Component } from ‘@angular/core’;
@Component({
template: <p> 选择一个页面链接 </p>
})
export class HelpComponent { }
当输入默认URL时,它将显示如下:
有一个最后的路由器功能我们将看一下,即能够对更复杂的应用程序进行 URL 模式匹配。我们仅仅看一个非常简单的模式匹配。
以下的路由使用了参数语法。
{path: ‘params/:id’, component: ParamsComponent}
字符串“:id”引入了一个名为“id”的参数。在我们的例子中,它会跟随斜杠“/”分隔符。
现在更复杂的问题是要在组件中提取参数。在这种情况下,我们可以将 ParamsComponent 定义如下。
import { Component } from ‘@angular/core’;
import { ActivatedRoute, Router, Params } from ‘@angular/router’;
@Component({
selector: ‘app-root’,
template: <h2>Parameter example</h2> <p>The parameter was {{param}}.<p> <button routerLink="/">Home</button>
})
export class ParamsComponent {
param: string;
constructor(private route: ActivatedRoute, private router: Router) {}
ngOnInit() {
this.param = this.route.snapshot.params[‘id’];
}
}
请注意以下代码属性:
- 我们必须从 Angular 路由器库中导入参数处理类。
- 构造函数定义了两个可注入的服务,ActiveRoute 和 Router。你会记得这是 Angular 在组件中使用 Angular 可注入服务的方式。
- 我们使用 ngOnInit() 钩子在组件第一次初始化时执行代码。
- 钩子中的代码允许我们在组件初始化时提取 ‘id’ 参数。
在这里我们应该注意到,这个代码其实是一个非常简单的参数传递版本。一个真正的应用程序可能需要考虑从 web 服务器加载页面时的延迟,并引入一些延迟处理。我们将单独讨论这些问题。
下面的图片显示了带有字符串"23abc"作为参数的应用程序。请注意它在 URL 地址栏和在组件中显示的文本中都出现了。
请注意任何有效的 URL 字符串都可以作为参数。如果你想要一个数字参数,那么你可能需要将字符串转换成数字。