Использование RxJS для отправки одного сетевого запроса - PullRequest
0 голосов
/ 02 октября 2018

В моем приложении Angular есть функция, которая принимает некоторые выбранные пользователем фильтры, а затем возвращает отфильтрованные данные на основе этих значений фильтра.Если фильтры не применяются, возвращаются нефильтрованные данные.

Значения фильтра передаются от одного компонента другому через EventEmitter () Angular, поэтому в шаблоне принимающего компонента у меня есть это:

<div class="page-content">
    <grid [records]="records"
        (sendFilterValues)="onFilterReceived($event)">
    </grid>
</div>

Проблема, с которой я сталкиваюсь (и я устранял это уже несколько дней), заключается в том, что поскольку фильтры приходят через EventEmitter () из другого компонента, это вызывает функцию, которая получила эти выборы изатем вызывает сетевой вызов несколько раз.

Оператор console.log в середине функции специально сообщает мне, что фильтры, входящие в эту функцию, вызывают ее запуск 10 раз.

Чтов конечном итоге происходит то, что для пользователя они видят сдвиг данных в представлении несколько раз, когда происходит несколько вызовов - некоторые для нефильтрованных данных, а некоторые для отфильтрованных данных.Я хочу управлять этим, чтобы был выполнен только один сетевой запрос.

Итак, я пытаюсь сделать, поскольку это наблюдаемый, использовать некоторый оператор RxJS, чтобы сделать только один вызов.Тем не менее, я не могу заставить его работать.Насколько я могу судить, оператор, который я применяю, не имеет никакого эффекта.

Вот мой код:

import { last } from 'rxjs/operators';

public onFilterReceived(values)
{
    let filters = {};

    if (values) {
        filters = values;
    }

    this.route.params.subscribe(
        (params: any) => {
            this.page = params['page'];
        }
    );

    let fn = resRecordsData => {
        this.records = resRecordsData;
    };

    console.count('onFilterReceived() is firing'); // fires 10 times

    this.streamFiltersService.getByFilters(
        this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
        this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
        this.branch = filters['branch'], fn).last;
} 

Пока у меня есть импорт в моем компоненте, моя IDEпредполагает, что это никогда не читается.Однако, когда я добавляю . сразу после кода ниже, вот так:

this.streamFiltersService.getByFilters(
    this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
    this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
    this.branch = filters['branch'], fn).

... различные операторы RxJS отображаются в качестве опций, включая оператор last.

Кстати, это 10-кратное срабатывание возвращается к исходному компоненту фильтра, где мой console.log снизу показывает мне, что это срабатывает 10 раз, и это передается принимающему компоненту.Если есть другой способ, которым я могу контролировать это, чтобы испускать только один раз, это также, вероятно, решило бы мою проблему.Вот этот код:

async onFilterValuesReceived({ 'zip' : zipArray, 'firstName' : firstName,
'lastName' : lastName, 'language' : language, 'location' : location, 'branch' : branch })
{
    let filtersObj = { 'zip' : zipArray, 'firstName' : firstName,
    'lastName' : lastName, 'language' : language, 'location' : location, 'branch' : branch };

    this.sendFilterValues.emit(await filtersObj);

    console.count('sending these...'); // fires 10 times
}

Итак, если это так, почему этот оператор не действует здесь?Я использую неправильный оператор или неправильно?Или что-то еще под вопросом?Я получаю ноль ошибок, связанных с использованием этих операторов, но я также не вижу, чтобы они влияли на выполнение функции.

Кстати, именно так выглядит код для сетевого вызова вуровень обслуживания:

public getByFilters(page, pagesize, stage?, language?, location?, zipcode?, firstName?, lastName?, branch?, fn?: any)
{
    return this.apiService.get({
      req: this.strReq, reqArgs: { page, pagesize, stage, language, location, zipcode, firstName, lastName, branch }, callback: fn });
}

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

public get(args: {
    req: string,
    reqArgs?: any,
    reqBody?: any,
    callback?: IRequestCallback
}): Observable<Response>
{
    args['reqType'] = 'get';
    return this.runHttpRequest(args);
}

Является ли структура здесь на уровне обслуживания API проблемой?Мы используем это во всех наших GET-запросах этого типа.Когда я пытаюсь использовать оператор takeLast здесь, я получаю ошибку TS:

[ts] Type '(this: Observable, count: number) => Observable' не присваивается типу 'Наблюдаемое.Свойство '_isScalar' отсутствует в типе '(this: Observable, count: number) => Observable'.

Я ломал голову над этой проблемой уже несколько дней и буду признателен всемвведите, как я могу решить эту проблему.

Я также полностью открыт для опции не-RxJS на данный момент, если есть другой способ, который может быть решен.

Ответы [ 2 ]

0 голосов
/ 12 июня 2019

В качестве еще одного упоминания о ответе вам нужно отменить.Чтобы сделать это с помощью RxJS, используйте оператор debounceTime () внутри канала наблюдаемого:

this.streamFiltersService.getByFilters(
    this.page - 1,
    this.pagesize,
    ...
).pipe(debounceTime(200)).subscribe((resRecordsData) => this.records = resRecordsData ); 

И тогда вам не нужна дополнительная библиотека.

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

this.records$ = this.streamFiltersService.getByFilters(
    this.page - 1,
    this.pagesize,
    ...
).pipe(debounceTime(200)); 
<app-whatever-component [records]="records$ | async"><app-whatever-component>
0 голосов
/ 02 октября 2018

Поскольку вы говорите, что открыты для не-RxJS опций, я предложу debounce на ваше рассмотрение.Короче говоря, если вы отклоните запрос, он сработает только один раз после того, как события прекратили запускаться.

Есть много библиотек, которые позволяют это, но я покажу пример с lodash - https://lodash.com/docs/4.17.10#debounce

import * as _ from "lodash";

private sendRequest = _.debounce(() => {
   this.streamFiltersService.getByFilters(
        this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
        this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
        this.branch = filters['branch'], 
        (resRecordsData) => {
           this.records = resRecordsData;
        })
}, 200);

public onFilterReceived(values)
{
    let filters = {};

    if (values) {
        filters = values;
    }

    this.route.params.subscribe(
        (params: any) => {
            this.page = params['page'];
        }
    );

    console.count('onFilterReceived() is firing'); // fires 10 times

    this.sendRequest();
} 

Только после того, как прошло 200 мс с момента последнего вызова sendRequest, запрос будет фактически выполнен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...