[英]@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.