组件在 NgOnInit 完成之前渲染?

Posted

技术标签:

【中文标题】组件在 NgOnInit 完成之前渲染?【英文标题】:Component renders before NgOnInit finishes? 【发布时间】:2018-09-19 18:54:38 【问题描述】:

我有一个组件,它对调用 api 的服务执行一些调用。组件在这些调用完成之前呈现,导致页面为空。

这是来自组件的代码:

import Component, OnInit, OnDestroy from '@angular/core';
import AuthService from '../services/auth.service';
import AssignmentService from '../services/assignment.service';
import Assignment from '../models/Assignment';
import Request from '../models/Request';
import MeService from '../services/me.service';
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
import Router from '@angular/router';
import NavbarService from '../services/navbar.service';

@Component(
  selector: 'app-newrequest',
  templateUrl: './newrequest.component.html',
  providers: [AssignmentService]
)
export class NewrequestComponent implements OnInit 
  me: MicrosoftGraph.User;
  assignments: Assignment[];
  requests: Request[] = [];
  ready = false;

  constructor(private meService: MeService,
              private authService: AuthService,
              private assignmentService: AssignmentService,
              private router: Router,
              public nav: NavbarService) 
  

  ngOnInit() 
    this.nav.show();
    this.nav.element = 'newrequests';
    if (localStorage.getItem('loggedin') === 'yes') 
      this.meService.getMe().subscribe(data => 
          this.me = data;
        ,
        error => 
          console.log(error);
        ,
        () => this.assignmentService.getAssignments().subscribe(data => 
            this.assignments = data;
          ,
          error => 
            console.log(error);
          ,
          () => 
            this.setRequests();
          ));
     else 
      this.router.navigate(['']);
    
  

  onLogout() 
    this.authService.logout();
  

  onLogin() 
    this.authService.login();
  

  private setRequests() 
    this.assignments.forEach(item => 
      if (this.me.mail.toLowerCase() === item.lecturer.toLowerCase()) 
        this.parseRequests(item.request, item.name);
      
    );

    this.ready = true;
  

  private parseRequests(toSplit, name) 
    const split = toSplit.split(',');

    split.forEach(item => 
      this.requests.push(new Request(item, name));
    );
  

这是页面中的代码:

<app-navbar-component></app-navbar-component>
<div *ngIf="ready">
  <div *ngFor="let request of requests">
    <p class="lead">request.user</p>
  </div>
</div>

这是我的服务中的功能:

getAssignments() 
    return this.http.get(this.BASE_API_URL + '/assignments').catch(this.onError);
  

我没有收到任何错误。请求已加载(通过 console.log 检查)。唯一的问题是页面在数据加载完成之前呈现。

有什么想法吗?

提前致谢!

【问题讨论】:

是的,没错。 HTTP 请求是异步的,因此它不会等到完成。 我该如何规避这个问题?我认为 *ngIf 会工作...... 规避什么?请求最终完成时是否呈现数据? 如果你不想在收到数据之前渲染页面,你可以使用router resolve guard。 请求完成后组件不渲染。 【参考方案1】:

是的,由于服务调用是异步的,调用订阅时数据不会立即到达。

但是在您的代码中,一旦数据到达,您并没有将 ready 变量设置为 true。所以你在页面上看不到任何数据。获取数据后设置为true

this.me = data;
this.ready =true;

或通过安全导航操作符处理。

【讨论】:

感谢您的帮助。我认为你监督了准备好的部分。我在 setRequests() 函数中将 ready 设置为 true。 是的。我注意到,当我在您提到的地方设置为 true 时,它​​在组件中变为 true。然而,这还为时过早。它使用的数据到那时还没有加载。我需要作业数据。 在subscribe里面调用setrequests 不幸的是没有改变任何东西 我确定你的方法 parseRequest 有问题。只需检查您的数组是否有数据【参考方案2】:

您是否尝试过使用解析?

创建一个解析器,命名为 assignments-details.resolver.ts

import  Injectable  from '@angular/core';
import  Resolve, ActivatedRouteSnapshot, RouterStateSnapshot from '@angular/router';
import MeService from '../services/me.service';

@Injectable()
export class AssignmentsDetailsResolver implements Resolve<any> 

  constructor(api: MeService)

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) 
   return this.api.getAssignments();
  


然后在你的路由配置中这样做


  path: 'your_path',
  component: NewrequestComponent,
  resolve:  AssignmentsDetails: AssignmentsDetailsResolver 

然后确保在您的 NewrequestComponent 上导入 ActivatedRoute

import  ActivatedRoute  from '@angular/router'

在你的构造函数中这样称呼它

 constructor(private meService: MeService,
              private authService: AuthService,
              private assignmentService: AssignmentService,
              private router: Router,
              public nav: NavbarService,
              private activateRoute: ActivatedRoute) 

   this.activateRoute.data.subscribe((response) => 
       //to get the response
       console.log(response.AssignmentsDetails);
     );
  

还要确保在模块的 provider 属性中注册“AssignmentsDetailsResolver”。

希望对你有所帮助。

【讨论】:

这里的resolve方法就是这个问题的准确答案。

以上是关于组件在 NgOnInit 完成之前渲染?的主要内容,如果未能解决你的问题,请参考以下文章

在 ngOnInit 中调用多个异步函数的问题

Angular2:如何在渲染组件之前加载数据?

Angular2:如何在渲染组件之前加载数据?

ngOnInit 比 @Input 快

Angular5服务调用在ngOnInit上不起作用

Vue 功能,在运行函数之前等待组件完成重新渲染