Angular http 请求完全执行后点击 7 路由

Angular 7 route on click after http-request got full performed

我有一个条目列表,当我单击一个列表时,我会进入该项目的更详细视图。现在这个视图通过 http-Request 加载(这比从一个页面到另一个页面的路由花费的时间稍长)。假设我有以下代码:

对于目标页面:

 projectNumber: string = '';

  ngOnInit() {
  }

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private logger: LoggerService,
    private projectReceiverService: ProjectReceiverService
  ) {
    this.initProjectDataFromProjectNumber(this.route.snapshot.paramMap.get('projectNum'));
  }

  initProjectDataFromProjectNumber(projectNumber: string) {
    let project = this.projectReceiverService.getProjectByNumber(projectNumber); 
    this.projectNumber = projectNumber; //getProjectNumber => results in error
  }

对于服务:

 getProjectByNumber(projectNumber: string): Project {
 let project: Project; 
 this.http.get(httpBaseUrl + "/project/getProject/" + projectNumber, httpOptions).subscribe((data) => {
  this.logger.info("Received data, processing...");
  project = data['projectDto'];
},
(error) => {
  this.logger.error("Error when receiving project.");
  this.logger.error(error.message);
});;
 return project;
}

对于上一页(我点击项目的地方):

 projectClicked(projectNumber: string) {
    this.routeTo('projects/' + projectNumber);
  }

现在,如果我能够在 http 请求完全成功后以某种方式设法进行路由,那就太好了(因为我可以,例如,在路由时直接传递项目对象,而且我也不会在显示一些内容时遇到困难可能尚未在详细页面上加载的数据)。我怎么能那样做?我考虑过在单击某些内容之前将其加载到前面,但如果事先加载每个项目的所有详细信息,那将是一个过载的地狱。

您可以使用路由解析器相对轻松地完成此操作。

事情是这样的。在您的路线配置中,您提供路线路径、组件和 resolver.

const routes = [{
  path: 'projects/:projectNumber',
  component: DestinationComponent,
  resolve: {
    project: ProjectResolver
  },
}];

现在您必须将此路由放入模块中,并提供您提到的解析器。例如。像这样:

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  providers: [ProjectResolver],
})
export class ProjectDetailsModule {}

但是这个 ProjectResolver 是什么?它只是一个可注入的 class 实现 Angular 的 Resolve 接口,在它的 resolve() 方法上以数据、Promise 或 Observable 的形式返回数据。看起来像这样:

import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
// You will also need your service
import { ProjectService } from './project.service';

@Injectable()
export class ProjectResolver implements Resolve {
  // Inject it with where the data comes from
  constructor(private projectService: ProjectService) {}

  // Now implement the resolve interface. Angular will give you the route,
  // from which you can read the parameter.
  resolve(route: ActivatedRouteSnapshot): Observable<Project> {
    const projectNumber = route.snapshot.params['projectNumber'];
    return this.projectService.getProjectByNumber(projectNumber);
  }
}

现在,我们必须稍微修复一下您的服务 - http 方法通常是异步的,因此您必须 return observable:

// I'll change the return type, from Project, to Obsevable<Project>
getProjectByNumber(projectNumber: string): Observable<Project> {
  return this.http.get(`${ httpBaseUrl }/project/getProject/${ projectNumber }`,  httpOptions)
    // Instead of subscribe, you'll use a pipe, to map. So that others can actually subscribe (in our case the Angular router)
    .pipe(
      map(data => {
        this.logger.info("Received data, processing...");
        project = data['projectDto'];
        return project;

        // This whole `map(...)` part could have been a one-liner:
        // map(data => data.projectDto)
        // so alltogether:
        // this.http.get().pipe( map(data => data.projectDto), catchError(this.logger.error));
    },
    catchError(err => this.logger.error('Error getting project:', err.message)),
  );
}

只剩下一件事了。我们如何在组件中获取一次数据?再次从路线出发。

// in the component
ngOnInit() {
  this.activatedRoute.data.subscribe(project => this.project = project);
  // or something like this for immediate value:
  // this.data = this.route.snapshot.data;
}

请注意,这会将您的页面保留在 "loading" 中,直到数据实际恢复。如果您想并行获取数据,这是一个不同的答案,但也可以做到。