@Viewchild在ngOnInit期间未初始化

时间:2019-07-09 17:56:21

标签: angular typescript angular-material viewchild ngoninit

我正在使用来自不同可观察对象的数据源在不同组件中运行两个MatTable。我的表排序功能之一工作正常,但是在第二个表上,好像MatView的@ViewChild在ngOnInit期间未初始化。

数据渲染,并且材质表具有排序按钮,但功能不存在。检查了我的导入和模块,一切都很好。
记录MatSort时,一个组件会记录MatSort对象,而另一个则未定义

排序不起作用。

Feed.component:

   import { PostService } from './../../services/post.service';
   import { Post } from './../../models/post';
   import { Component, OnInit, ViewChild, ChangeDetectorRef} from 
     '@angular/core';
   import { MatSort, MatTableDataSource, MatCheckbox, MatPaginator, 
     MatTabChangeEvent, MatDialog, MatDialogActions, MatTable}  from 
   "@angular/material"



export class FeedComponent implements OnInit {
  @ViewChild(MatSort) sort: MatSort;
  @ViewChild(MatPaginator) paginator: MatPaginator;
  postData: Post[] =[];
  dataSource : MatTableDataSource<any> 
  currentUser = JSON.parse(localStorage.getItem('user'))
  displayedColumns:string[] = ['User','Title', "Description", 
  "Contact" ]
  posts = this.ps.getPosts();

  constructor(private ps: PostService, public dialog:MatDialog, 
    public change:ChangeDetectorRef, public ms:MessageService) { 

  }



refreshPosts(){
   console.log(this.sort) < -------comes back undefined
  this.posts.subscribe(posts=>{
    this.dataSource.sort = this.sort
     this.postData = posts.filter(post => post.uid != 
       `${this.currentUser.uid}` && post.claimedBy 
        !=`${this.currentUser.uid}`);
     this.dataSource= new MatTableDataSource(this.postData)
     this.dataSource.paginator = this.paginator;
    });

  }
ngOnInit() {
   this.refreshPosts()
   console.log(this.sort)
   }


Post.service
  getPosts(){
    return  this.afs.collection('posts').snapshotChanges()
     .pipe(map(actions => 
     actions.map(this.documentToDomainObject)))
  }
 documentToDomainObject = _ => {
  const object = _.payload.doc.data();
  object.id = _.payload.doc.id;
  return object;
}

现在我的下一个组件以相同的方式初始化,但是@ViewChild显示为MatSort对象

Message.component:

export class MessageComponent implements OnInit {


 @ViewChild(MatSort) sort: MatSort;
  userReceived: MatTableDataSource<any>;
  userSent: MatTableDataSource<any>;
  displayedColumns:string[] = ["createdAt",'author',"title", "Delete"]
  sentColumns:string[] = ["createdAt","recipient", "title", "Delete"]


  currentUserId= this.currentUser['uid']
  currentUsername = this.currentUser['displayName']
  recipient:any;
  selectedMessage: MatTableDataSource<Message>;
  messageColumns= ['From','Title',"Body"];




  constructor(public ms:MessageService, public change:ChangeDetectorRef, public dialog: MatDialog  ) { }

  ngOnInit() {
    console.log(this.sort)
    this.updateMessages()
    this.currentUserId = this.currentUserId;
    this.currentUsername = this.currentUsername;

 }


updateMessages(){
    this.ms.getUserSent().subscribe(messages => {
      console.log(this.sort) <------logs MatSort object
      this.userSent = new MatTableDataSource(messages)
      this.userSent.sort = this.sort
      console.log(this.userSent.sort)
      console.log(this.userSent.data)

    })

message.service

 getUserSent() {
    let messages:any[] = [];
    this.userSent = this.afs
      .collection('messages', ref => ref.where('uid', '==', `${this.currentUser.uid}`)).snapshotChanges() 
return this.userSent
  }

feed.component.html

<div class = "mat-elevation-z8">
    <mat-form-field>
        <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Search Posts">
      </mat-form-field>
  <table matSort mat-table [dataSource]="dataSource" style="text-align:left">
      <ng-container matColumnDef="User">
          <th mat-header-cell *matHeaderCellDef mat-sort-header>User</th>
          <td mat-cell *matCellDef="let post">{{post.displayName}}</td>
       </ng-container>

  <ng-container matColumnDef="Title">
    <th mat-header-cell *matHeaderCellDef>Title</th>
    <td mat-cell *matCellDef="let post">{{post.title | truncate:15:false }}</td>
 </ng-container>

  <ng-container matColumnDef="Description">
    <th mat-header-cell *matHeaderCellDef >Description</th>
    <td mat-cell *matCellDef="let post">{{post.description | truncate: 20 : false}}</td>
  </ng-container>





  <ng-container matColumnDef="Contact">
    <th mat-header-cell *matHeaderCellDef> Contact </th>
    <td mat-cell *matCellDef="let post">
      <button  id="{{post.id}}" color="primary" (click)="openDialog($event.target.id)" style = "outline:none" value={{post.id}}>Claim</button>
    </td>

  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef='let row; columns: displayedColumns'></tr>
</table>
</div>
  <mat-paginator [length]="this.postData.length" [pageSize]="5" [pageSizeOptions]="[5,10,25]"></mat-paginator>

我真的找不到为什么在我的第二个工作组件中,排序返回并定义对象时,排序返回未定义的原因。我是否缺少有关@ViewChild的顺序的信息?

2 个答案:

答案 0 :(得分:3)

摘自官方文档:https://angular.io/api/core/ViewChild#description

  

视图查询是在调用ngAfterViewInit回调之前设置的。

为了启动@ViewChild属性,您需要在ngAfterViewInit生命周期挂钩中调用它。

export class MessageComponent implements OnInit, AfterViewInit {

   @ViewChild(MatSort) sort: MatSort;

   ngAfterViewInit(){
      console.log(this.sort)
   }
}

如果您使用的是Angular 8,则由于需要@ViewChild标志,因此需要重构static属性的实现

答案 1 :(得分:0)

FeedComponent中的问题是您要在初始化this.dataSource.sort = this.sort之前进行this.dataSource分配

refreshPosts(){
  console.log(this.sort) < -------comes back undefined
  this.posts.subscribe(posts=>{
     this.postData = posts.filter(post => post.uid != `${this.currentUser.uid}` && post.claimedBy !=`${this.currentUser.uid}`);
     this.dataSource= new MatTableDataSource(this.postData)
     this.dataSource.sort = this.sort // make assignment after initializing this.dataSource
     this.dataSource.paginator = this.paginator;
    });
  }

请注意,console.log(this.sort)仍然会由于生命周期顺序而无法打印。 ngOnInit上的视图查询未设置。

因此出现了问题;那么this.dataSource.sort = this.sort分配在ngOnInit中的MessageComponent中如何工作?

答案很长,但可以简单地说。因为该代码在subscribe回调中执行。由于在发出observable时将执行订阅回调中的代码,因此会发生异步操作。 ,并且异步操作会在执行ngAfterViewInit挂钩的周期之后的后续更改检测周期中发生。

您不会在第二个组件中得到未定义的输出,因为该console.log语句也在订阅回调中执行。将该日志语句移出订阅回调也将打印未定义。

如果将console.log语句放在ngAfterViewInit挂钩中,则无论是否将它们放置在订阅回调中,它们都将打印实际值。

作为摘要;

初始化this.datasource

后进行分配
this.dataSource= new MatTableDataSource(this.postData)
this.dataSource.sort = this.sort // make assignment after initializing 

并在ngAfterViewInit钩子中执行此操作,即使由于异步操作而在ngOnInit中也可以运行。