@Viewchild 在 ngOnInit 期间未初始化
@Viewchild not initializing during ngOnInit
我是 运行 不同组件中的两个 MatTable,数据源来自不同的可观察对象。我的 table 排序功能之一工作正常,但在我的第二个 table 上,似乎 MatSort 的 @ViewChild 在 ngOnInit 期间没有初始化。
数据呈现和 material table 有排序按钮,但功能是什么。检查了我的导入和模块,一切正常。
在记录 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>
我真的找不到为什么在我的第一个组件中排序 returns 未定义,而在我的第二个工作组件中它 returns 和对象。我是不是漏掉了@ViewChild 的顺序?
来自官方文档:https://angular.io/api/core/ViewChild#description
View queries are set before the ngAfterViewInit callback is called.
为了让 @ViewChild
属性 初始化,你需要在 ngAfterViewInit
生命周期挂钩中调用它。
export class MessageComponent implements OnInit, AfterViewInit {
@ViewChild(MatSort) sort: MatSort;
ngAfterViewInit(){
console.log(this.sort)
}
}
如果您使用的是 Angular 8,则需要重构 @ViewChild
属性的实现,因为需要 static
标志
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;
});
}
请注意,由于生命周期顺序,console.log(this.sort)
仍将打印 undefined。在 ngOnInit
上未设置视图查询。
所以问题来了;那么 this.dataSource.sort = this.sort
在 ngOnInit
中如何在 MessageComponent
中工作?
答案很长,但简单来说;因为该代码在 subscribe
回调中执行。由于 subscribe 回调中的代码在 observable 发出时执行,因此发生异步操作。 并且该异步操作发生在随后的变更检测周期中 在执行 ngAfterViewInit
挂钩的周期之后。
您没有在第二个组件中得到未定义的输出,因为该 console.log 语句也在订阅回调中执行。将该日志语句移出订阅回调也会打印 undefined。
如果你在 ngAfterViewInit
钩子中放置 console.log 语句,无论它们是否放置在订阅回调中,它们都会打印实际值。
作为总结;
初始化后赋值this.datasource
this.dataSource= new MatTableDataSource(this.postData)
this.dataSource.sort = this.sort // make assignment after initializing
并在 ngAfterViewInit
挂钩中执行,即使由于异步操作它在 ngOnInit
中工作。
我是 运行 不同组件中的两个 MatTable,数据源来自不同的可观察对象。我的 table 排序功能之一工作正常,但在我的第二个 table 上,似乎 MatSort 的 @ViewChild 在 ngOnInit 期间没有初始化。
数据呈现和 material table 有排序按钮,但功能是什么。检查了我的导入和模块,一切正常。
在记录 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>
我真的找不到为什么在我的第一个组件中排序 returns 未定义,而在我的第二个工作组件中它 returns 和对象。我是不是漏掉了@ViewChild 的顺序?
来自官方文档:https://angular.io/api/core/ViewChild#description
View queries are set before the ngAfterViewInit callback is called.
为了让 @ViewChild
属性 初始化,你需要在 ngAfterViewInit
生命周期挂钩中调用它。
export class MessageComponent implements OnInit, AfterViewInit {
@ViewChild(MatSort) sort: MatSort;
ngAfterViewInit(){
console.log(this.sort)
}
}
如果您使用的是 Angular 8,则需要重构 @ViewChild
属性的实现,因为需要 static
标志
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;
});
}
请注意,由于生命周期顺序,console.log(this.sort)
仍将打印 undefined。在 ngOnInit
上未设置视图查询。
所以问题来了;那么 this.dataSource.sort = this.sort
在 ngOnInit
中如何在 MessageComponent
中工作?
答案很长,但简单来说;因为该代码在 subscribe
回调中执行。由于 subscribe 回调中的代码在 observable 发出时执行,因此发生异步操作。 并且该异步操作发生在随后的变更检测周期中 在执行 ngAfterViewInit
挂钩的周期之后。
您没有在第二个组件中得到未定义的输出,因为该 console.log 语句也在订阅回调中执行。将该日志语句移出订阅回调也会打印 undefined。
如果你在 ngAfterViewInit
钩子中放置 console.log 语句,无论它们是否放置在订阅回调中,它们都会打印实际值。
作为总结;
初始化后赋值this.datasource
this.dataSource= new MatTableDataSource(this.postData)
this.dataSource.sort = this.sort // make assignment after initializing
并在 ngAfterViewInit
挂钩中执行,即使由于异步操作它在 ngOnInit
中工作。