@Viewchild не инициализируется во время ngOnInit - PullRequest
3 голосов
/ 09 июля 2019

Я использую две таблицы MatTable в разных компонентах с источниками данных из разных наблюдаемых.Одна из моих функций сортировки таблиц работает нормально, но для моей второй таблицы кажется, что @ViewChild for MatSort не инициализируется во время 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 ]

3 голосов
/ 09 июля 2019

Из официальных документов: 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

0 голосов
/ 09 июля 2019

проблема в 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 обратном вызове.так как код в обратном вызове подписки выполняется, когда наблюдаемый излучает, происходит асинхронная операция. и эта асинхронная операция происходит в последующем цикле обнаружения изменений после цикла, в котором выполняется ловушка 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 из-заасинхронная операция.

...