简体   繁体   English

@Viewchild在ngOnInit期间没有初始化

[英]@Viewchild not initializing during ngOnInit

I'm running two MatTables in different components with data sources from different observables. 我在不同组件中运行两个MatTable,其中包含来自不同可观察数据的数据源。 One of my tables sort functionality is working fine and but on my second table it seems as if the @ViewChild for MatSort doesn't initialize during ngOnInit. 我的一个表格排序功能正常工作,但在我的第二个表格中,看起来似乎在@OnInit期间,@ LookChild for MatSort没有初始化。

Data renders and the material table has sort buttons but the functionality is nothing. 数据渲染和材料表有排序按钮,但功能不算什么。 Checked my imports and the module and everything is fine. 检查我的进口和模块,一切都很好。
On logging the MatSort one component logs a MatSort object and the other is undefined 在记录MatSort时,一个组件记录一个MatSort对象,另一个组件未定义

Sorting not working. 排序不起作用。

Feed.component: 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;
}

Now my next component initializes in the same way but the @ViewChild shows up as a MatSort Object 现在我的下一个组件以相同的方式初始化,但@ViewChild显示为MatSort对象

Message.component: 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 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 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>

I really cant find why in my first component the sort returns undefined when in my second, working component it returns and object. 我真的无法找到为什么在我的第一个组件中,在我的第二个工作组件返回和对象时,sort返回undefined。 Am I missing something about the order of @ViewChild? 我错过了@ViewChild的顺序吗?

From official docs: https://angular.io/api/core/ViewChild#description 来自官方文档: https//angular.io/api/core/ViewChild#description

View queries are set before the ngAfterViewInit callback is called. 在调用ngAfterViewInit回调之前设置视图查询。

In order to get @ViewChild property inited, you need to call it in ngAfterViewInit lifecycle hook. 为了获得@ViewChild属性,您需要在ngAfterViewInit生命周期钩子中调用它。

export class MessageComponent implements OnInit, AfterViewInit {

   @ViewChild(MatSort) sort: MatSort;

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

If you are using Angular 8, you need to refactor the implementation of @ViewChild properties since static flag is required 如果您使用的是Angular 8,则需要重构@ViewChild属性的实现,因为需要static标志

the problem in FeedComponent is that you are making this.dataSource.sort = this.sort assignment before initializing this.dataSource FeedComponent的问题是你在初始化this.dataSource之前进行this.dataSource.sort = this.sort赋值

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;
    });
  }

please note that console.log(this.sort) will still print undefined because of lifecycle sequences. 请注意,由于生命周期序列, console.log(this.sort)仍将打印未定义。 on ngOnInit view queries are not set. ngOnInit视图查询未设置。

so the question arises; 所以问题出现了; then how would this.dataSource.sort = this.sort assignment work in ngOnInit in MessageComponent ? 那么this.dataSource.sort = this.sort赋值如何在MessageComponent中的ngOnInit中工作?

the answer is long but to put it in simple terms; 答案很长,但简单来说就是这样; because that code gets executed in subscribe callback. 因为该代码在subscribe回调中执行。 since the code in subscribe callback gets executed when observable emits, an asynchronous operation happens. 由于订阅回调中的代码在observable发出时执行,因此会发生异步操作。 and that async operation happens in a subsequent change detection cycle after a cycle where ngAfterViewInit hook gets executed. 并且在执行ngAfterViewInit挂钩的循环之后的后续更改检测周期中发生异步操作

you are not getting undefined output in your second component because that console.log statement is also executed in a subscribe callback. 您没有在第二个组件中获得未定义的输出,因为该console.log语句也在订阅回调中执行。 moving that log statement out of subscribe callback will also print undefined. 从订阅回调中移出该日志语句也将打印undefined。

if you place console.log statements in ngAfterViewInit hook, they both will print actual values whether they are placed in a subscribe callback or not. 如果在ngAfterViewInit挂钩中放置console.log语句,它们都将打印实际值,无论它们是否放在订阅回调中。

as a summary; 作为总结;

make assignment after initializing this.datasource 初始化this.datasource后进行赋值

this.dataSource= new MatTableDataSource(this.postData)
this.dataSource.sort = this.sort // make assignment after initializing 

and do it in ngAfterViewInit hook, even though it works in ngOnInit due to the async operation. 并且在ngAfterViewInit挂钩中执行它,即使它由于异步操作而在ngOnInit工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM