Подписка на Observable не запускает обнаружение изменений

Я использую «angular2-virtual-scroll» для реализации загрузки по требованию. Раньше элементы управлялись наблюдаемыми с использованием асинхронного канала, запускаемого родительским компонентом. Теперь я пытаюсь вызвать свой сервис из ребенка. Вызов успешен, и я получаю свои данные, мне нужно использовать событие подписки, чтобы применить другую логику. Проблема заключается в том, что обнаруженные изменения не работают, когда я обновляю свои массивы в функции подписки. Я читал другие подобные проблемы, но мне не повезло найти решение.

Это основной компонент, в котором используются сервисные вызовы. Первоначальный запрос выполняется из onInit. И затем, когда вы прокручиваете вниз, вызывается fetchMore.

import { Component, OnInit, Input, OnDestroy  } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { User } from './../models/user';
import { Role } from './../../roles/models/role';
import { UsersService } from './../services/users.service';
import { ChangeEvent } from 'angular2-virtual-scroll';
import { promise } from 'selenium-webdriver';
import { VirtualScrollComponent } from 'angular2-virtual-scroll';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'app-users-list',
  template: `
    <div class="status">
        Showing <span class="">{{indices?.start + 1}}</span>
        - <span class="">{{indices?.end}}</span>
        of <span class="">{{users?.length}}</span>
        <span>({{scrollItems?.length}} nodes)</span>
    </div>
    <virtual-scroll [childHeight]="75" [items]="users" (update)="scrollItems = $event" (end)="fetchMore($event)">
        <div #container>
            <app-user-info *ngFor="let user of scrollItems" [roles]="roles" [user]="user">
              <li>
                <a [routerLink]="['/users/edit/', user.id]" class="btn btn-action btn-edit">Edit</a>
              </li>
            </app-user-info>
            <div *ngIf="loading" class="loader">Loading...</div>
        </div>
    </virtual-scroll>
  `
})
export class UsersListComponent implements OnInit, OnDestroy {
  users: User[] = [];
  @Input() roles: Role[];
  currentPage: number;
  scrollItems: User[];
  indices: ChangeEvent;
  readonly bufferSize: number = 20;
  loading: boolean;
  userServiceSub: Subscription;

  constructor(private usersService: UsersService) {
  }

  ngOnInit() {
    this.reset();
  }

  ngOnDestroy() {
      if(this.userServiceSub) {
          this.userServiceSub.unsubscribe();
      }
  }

  reset() {
    this.loading=true;
    this.currentPage = 1;
    this.userServiceSub = this.usersService.getUsers(this.currentPage).subscribe(users => {
        this.users = users;
    });
  }

  fetchMore(event: ChangeEvent) {
    if (event.end !== this.users.length) return;
    this.loading=true;
    this.currentPage += 1;
    this.userServiceSub = this.usersService.getUsers(this.currentPage).subscribe(users => {
        this.users = this.users.concat(users);
    });
  }
}

Из того, что я прочитал, это может быть проблема контекста, но я не уверен. Любые предложения были бы замечательными.

"РЕДАКТИРОВАТЬ"

Глядя на исходный код компонента плагина, я вижу, где фиксируется событие изменения.

VirtualScrollComponent.prototype.ngOnChanges = function (changes) {
    this.previousStart = undefined;
    this.previousEnd = undefined;
    var items = changes.items || {};
    if (changes.items != undefined && items.previousValue == undefined || (items.previousValue != undefined && items.previousValue.length === 0)) {
        this.startupLoop = true;
    }
    this.refresh();
};

Если я поставлю точку останова в этом событии, она сработает при начальной загрузке, поэтому, когда мы создаем экземпляр массива в []. Он срабатывает, когда я нажимаю на страницу. Но он не срабатывает, когда массив обновляется в событии подписки. Я даже поместил кнопку, которая устанавливает пустой массив и обновляет представление, поэтому функция подписки должна нарушать обнаружение изменений.


person Tony_89    schedule 23.02.2018    source источник


Ответы (2)


Поэтому, когда вы говорите, что обнаружение изменений не работает, я предполагаю, что вы имеете в виду следующее: *ngFor="let user of scrollItems"?

Я не использовал этот конкретный компонент, и у меня нет работающего кода для работы... но я бы начал с более внимательного изучения этого:

<virtual-scroll [childHeight]="75" 
                [items]="currentBuffer" 
                (update)="scrollItems = $event" 
                (end)="fetchMore($event)">

Возможно, измените (update), чтобы вызвать метод, чтобы убедиться, что он испускает и что вы получаете от него то, что ожидаете.

РЕДАКТИРОВАТЬ: Вот пример подписки, которая обновляет основное связанное свойство, показывающее данные для моей страницы:

movies: IMovie[];

getMovies(): void {
    this.movieService.getMovies().subscribe(
        (movies: IMovie[]) => {
            this.movies = movies;
            this.performFilter(null);
        },
        (error: any) => this.errorMessage = <any>error
    );
}

Обнаружение изменений в этом случае работает нормально. Так что, скорее всего, проблема, которую вы видите, вызвана чем-то другим.

Обратите внимание, что ваш шаблон не должен быть привязан к свойству, чтобы обнаружение изменений работало. В моем примере я привязываюсь к свойству movies. В вашем примере вам нужно будет привязаться к свойству users.

person DeborahK    schedule 23.02.2018
comment
Хорошая идея, но ваше событие запускается правильно. И что я имею в виду под обнаружением изменений, так это то, что когда я обновляю массив в функциях подписки, я ожидаю, что представление будет соответствующим образом обновлено. В настоящее время он не обновляется, если я не нажимаю на страницу, поэтому я решил, что обнаружение изменений не срабатывает (пока я не нажимаю на страницу), но я могу ошибаться. Я все еще довольно новичок в angular. - person Tony_89; 23.02.2018
comment
Какой массив вы имеете в виду? Возможно, вам потребуется предоставить plunker или stackblitz, чтобы продемонстрировать проблему. - person DeborahK; 23.02.2018
comment
массив пользователей, я понял, что массив с именем currentbuffer не нужен, поэтому я соответствующим образом отредактировал код. Я настрою плункер, чтобы показать проблему. Но из примера плагина, когда вы обновили массив, в этом случае обнаружение изменения массива пользователей должно запускать функцию обновления, которая затем вызывает обновление представления. Но поскольку у меня есть обновление, завернутое в функцию с подпиской, обнаружение изменений не срабатывает. - person Tony_89; 23.02.2018
comment
Почти весь мой код задает ключевое свойство данных в подписке, и обнаружение изменений работает нормально. Поэтому я предполагаю, что что-то еще не так. - person DeborahK; 23.02.2018
comment
Да, это определенно странная проблема. Я обновил свой вопрос кодом, который плагин использует для обработки изменения массива. Он не попадает, когда я обновляю свой массив в функции подписки. - person Tony_89; 24.02.2018
comment
разобрался, ну вроде. Чтобы заставить работать обнаружение изменений, мне пришлось пометитьForCheck. Я не уверен, почему, потому что, как вы сказали, я использовал подписку для установки значений раньше и не имел проблем. В любом случае спасибо за помощь в этом вопросе. - person Tony_89; 24.02.2018

Таким образом, обнаружение изменений не срабатывало. Мне пришлось использовать «ChangeDetectorRef» с функцией «markForCheck», чтобы обнаружение изменений работало правильно. Я не уверен, почему, поэтому мне определенно нужно провести некоторое исследование.

person Tony_89    schedule 23.02.2018