开始使用Angular路由解析器
构建Angular应用程序有时是很棘手的。这可能是由于在调用API后服务器响应延迟,或者是随后影响用户界面的bug。
在本教程中,我们将讨论Angular Route Resolvers在构建Angular组件中的重要性。我们还将创建一个示例应用程序,从API中获取一些数据,并在数据检索时才渲染组件。
教学目的
本教程涵盖了Angular路由解析器的基础知识到高级功能。最后,你应该能够进行API调用,并且只在收到数据响应时才解析路由。
前提条件
要继续学习本教程,你应该熟悉以下内容。
- JavaScript或TypeScript的基础知识。
- Angular2+路由概念的基础知识。
- 依赖性注入的知识。
Angular API调用回顾
在我们进入实际话题之前,让我们回顾一下Angular的API调用。
通常情况下,当应用程序进行异步API调用时,需要一些时间来响应。这可能是由于网络不佳或服务器宕机。
无论是哪种情况,用户都会被重定向到一个没有API数据的组件。无论你是否有强大的网络连接,这种情况都会发生。
比如说。
...
import {Project} from "../models/project";
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private httpClient:HttpClient) { }
/**
* list of ministries
*/
ListOfMinistries():Observable<Ministry>{
return this.httpClient.get<Ministry>(`${environment.apiURL}ministries`);
}
让我们假设上面的服务从一个国家的州政府获得所有部委的名单。我们已经注入了HttpClient 来帮助我们向服务器发送这个API请求。
我们也有一个监听我们的响应的可观察对象。GET 方法从服务器上获取所有部委的列表,并将该列表传递给Ministry 对象。
现在的问题是,我们不知道这要花多长时间。它是需要一秒钟还是半个小时,完全取决于我们所请求的服务器。
让我们看看,无论需要多长时间,我们如何处理这个响应。
只要我们有了这个响应,它就会像下面这个组件所示的那样处理。
.......
import {ApiService} from "../../core/services/api.service";
...
export class MinistriesComponent implements OnInit {
ministries:Ministry[];
//inject the api service we previously created.
constructor(private apiService:ApiService,private router:Router) { }
ngOnInit(): void {
// list for ministries will be listed whenever this component is initialized
this.getListOfMinistries();
}
/**
* list of ministries
*/
getListOfMinistries(){
this.apiService.ListOfMinistries()
.subscribe((res)=>{
this.ministries=res.data;
},error => {
//error logs goes here...
})
}
}
在这个组件的开头,我们导入了我们之前创建的API服务。需要注意的是,这是一个依赖注入的概念,如果你很不熟悉的话,值得一看。
我们最初曾说过,是apiService ,用来与外部服务器通信,以获得所有部委的列表。
现在发生的情况是,我们把这个服务注入到我们的组件中,并按照我们的意愿使用它来获得各部委的列表,在我们的例子中,当MinistriesComponent 被初始化时。
为了有一个组织良好的组件,我们创建了一个方法getListOfMinistries() ,用来从apiService ,获得各部委的列表。
请记住,在依赖性注入中,我们可以在应用程序的任何地方注入一个服务的方法。因此,我们调用
apiservice.ListOfMinistries()方法来订阅来自API的各部委列表。
在其他因素不变的情况下,这应该在组件初始化时返回我们的部委列表,然而,情况并非总是如此。也许还有其他因素在起作用,比如服务器故障和网络连接不良。
我们在模板上的部委表会发生什么?对我们来说,重要的是要注意,每当一个组件被调用,它的模板就应该被渲染。
然而,在这种情况下,它的渲染没有来自API的部委列表,正如我们之前看到的那样,可能会有延迟。就用户界面而言,这破坏了整个页面,在那里应该显示列表。
如何防止这种情况呢?一个解决方案是在网站上实现加载器,当组件等待API的响应时,加载器将被加载。另一个解决方案是使用Angular路由解析器。
在下一节中,将深入介绍Angular解析器的概念。我们将看到如何使用这一功能来克服上述问题。
什么是Angular路由解析器?
更简单地说,Angular解析器指的是在组件完全加载之前执行的中间件。
Angular路由解析器,就像一个过滤器,在一个组件被渲染之前,它必须确保它有该模板所需的数据。这当然可以确保在Angular中实现单页应用程序(SPA)。
现在,下面的模板渲染了没有加载器或路由解析器的部委列表。
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Description</th>
<th>Representative</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let ministry of ministries;let i=index">
<td>{{i+1}}}</td>
<td>{{ministry.name}}</td>
<td>{{ministry.description}}</td>
<td>{{ministry.representative}}</td>
</tr>
</tbody>
</table>
</div>
在上面的模板上,我们有一个政府部委的表格。该表应该显示一个部委的名称,它的描述和部长。
通常情况下,当你试图循环浏览这个表时,你会发现这个表一开始是空的,但后来被数据填满了(我们假设API返回一些部委的列表)。
在你继续之前,测试一下上面的功能,以了解我们所说的 "延迟 "响应。
如果你成功地测试了上面的功能,你可能已经注意到破损的用户界面。这是一个公司很重视的用户界面问题,而你作为开发者必须有能力去修复。
正如你已经看到的,解决它的一个方法是使用Angular路由解析器。
现在我们希望我们的组件只有在有来自API的响应时才被显示(渲染)。在这一节中,让我们来实现路由解析器。
使用Angular路由解析器
之前,我们已经看到了不正确地处理API响应可能会破坏我们的用户界面。在这一节中,我们要用Angular路由解析器来实现同样的部委表,并注意其中的关键区别。
让我们先创建一个实现Resolver class 的类。
//ministries resolver
...
import {ApiService} from "../../core/services/api.service";
import { Resolve } from '@angular/router';
...
export class RouteResolver implements Resolve<any> {
//inject the api service we initially implemented
constructor(public apiService: ApiService) { }
//resolve our list of ministries api service
resolve() {
return this.apiService.ListOfMinistries()
}
}
在上面的类中,我们导入了我们设计的api服务。它有一个方法来获取所有政府部委的列表。我们在resolve() 方法中调用这个方法。
继续并编辑你的src/app/app-routing.module.ts ,如下所示。
.....
// Components
...
// Route resolver array
import { RouteResolver } from './resolvers/route.resolver';
const routes: Routes = [
{
path: 'ministries',
component: MinistryComponent,
resolve: {
routeResolver: RouteResolver
},
},
];
@NgModule({
...
providers: [RouteResolver]
})
....
在上面的路由模块中,我们导入之前创建的路由解析器来解析我们的部委列表。
然后我们创建一个类型为Routes 的路由常量。这个常量需要一个路由路径的数组,在我们的例子中,我们有一个单一的路由ministries ,它使用MinistryComponent ,并由RouteResolver 来解析。
现在我们已经将我们的ministries 路由配置为只返回已解析的数据,让我们看看我们如何在我们的组件上访问它。
更新MinistryComponent ,如下图所示。
...
import { ActivatedRoute } from '@angular/router';
...
export class MinistryComponent implements OnInit {
ministries:Ministry[];
constructor(
private route: ActivatedRoute
) { }
ngOnInit() {
this.route.data.subscribe(data => {
this.ministries=data;
//log your api response here...
})
}
}
在MinistryComponent ,我们已经导入了ActivatedRoute ,它只用于解析激活的路由。我们订阅了这个已解决的路由,并且只在我们有部委的数据时显示模板。
总结
在本教程中,我们已经看到Angular路由解析器是如何实现的。我们已经看到了它的优势,以及为什么我们倾向于避免直接从组件中访问API调用。