Как я могу заставить paginator показывать правильную страницу после ее изменения? - PullRequest
0 голосов
/ 05 ноября 2019

Кажется, у меня есть ошибка с нумерацией страниц, которую я не могу решить. Дело в том, что я могу изменить страницу с помощью p-paginator от Prime-NG. При этом содержимое страницы изменяется в соответствии с запросом, но pageNum всегда остается на 1, даже если я нажал на страницу.

Чтобы дать вам некоторую справочную информацию о моей настройке кода: у меня есть два компонента с именами applicant и applicant-filtered. Компонент applicant-filtered вызывается из компонента applicant с этими строками кода, если фильтр настроен.

<dgam-applicants-filtered (pageChanged)="onPageChanged($event)"
                                      *ngIf="searchObject && applicantsSearchResponse && !isLoading"
                                      [applicantSearchObject]="searchObject"
                                      [applicantsSearchResponse]="applicantsSearchResponse">
            </dgam-applicants-filtered>

Фильтр работает отлично, так что это не проблема, поэтому я не будуопубликовать код для этого. Но вот код для метода onPageChanged:

onPageChanged(event: any) {
        this.searchObject.pageRequest.pageNum = event.page;
        this.searchApplicants();
    }

Компонент applicant-filtered выглядит следующим образом:

<ng-container *ngIf="applicantsSearchResponse?.page.content.length > 0">
    <div class="filteredApplicantCards flex flex-wrap w-100 mt2">
        <div *ngFor="let applicant of applicantsSearchResponse?.page.content"
             class="filteredApplicantCard ma2 pb3 gray shadow-1 bg-white z-0">
            <dgam-applicant-card [applicantProfile]="applicant"></dgam-applicant-card>
        </div>
    </div>
</ng-container>


<p-paginator (onPageChange)="paginate($event)"
             *ngIf="applicantsSearchResponse?.page.totalCount > applicantSearchObject.pageRequest.pageSize"
             [rows]="applicantSearchObject.pageRequest.pageSize"
             [totalRecords]="applicantsSearchResponse?.page.totalCount"
             class="custom-pagination">
</p-paginator>

и это машинопись:

import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {ApplicantSearch, ApplicantSearchResponse} from '../interfaces/applicant';


@Component({
    selector: 'dgam-applicants-filtered',
    templateUrl: './applicants-filtered.component.html',
    styleUrls: ['./applicants-filtered.component.scss']
})
export class ApplicantsFilteredComponent implements OnInit {
    @Input() applicantsSearchResponse: ApplicantSearchResponse;
    @Input() applicantSearchObject: ApplicantSearch;
    @Output() pageChanged: EventEmitter<any> = new EventEmitter();

    constructor() {
    }

    ngOnInit() {
    }

    paginate(event) {
        this.pageChanged.emit(event);
    }
}

В консоли нет ошибок, и отладка не скупила внутренности. Так что мне не хватает подсказок и идей.

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

РЕДАКТИРОВАТЬ: Вот весь код компонента applicant:

import {animate, state, style, transition, trigger} from '@angular/animations';
import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
import {AbstractControl, FormBuilder, FormControl, FormGroup} from '@angular/forms';
import {Router} from '@angular/router';
import {ChangeContext, Options} from 'ng5-slider';
import {DialogService} from 'primeng/api';
import {of, timer} from 'rxjs';
import {debounce, finalize, switchMap, tap} from 'rxjs/operators';
import {CityGeo} from '../../services/city/city-interfaces';
import {ApplicantCreationDialogComponent} from './applicant-creation-dialog/applicant-creation-dialog.component';
import {
    ApplicantFilter,
    ApplicantSearch,
    ApplicantSearchResponse,
    ApplicantSortingCriteria
} from './interfaces/applicant';
import {ApplicantService} from './services/applicant.service';

@Component({
    animations: [
        trigger('FadeAnimation', [
            state('in', style({opacity: 1})),
            transition(':enter', [
                style({opacity: 0}),
                animate(400)
            ]),
            transition(':leave',
                animate(400, style({opacity: 0})))
        ])
    ],
    providers: [DialogService],
    selector: 'dgam-applicant',
    styleUrls: ['./applicant.component.scss'],
    templateUrl: './applicant.component.html'
})
export class ApplicantComponent implements OnInit {
    static ONLY_MY_APPLICANTS = 'onlyMyApplicants';
    static ONLY_MY_BRANCH = 'onlyMyBranch';
    static ONLY_MY_TENANT = 'onlyMyTenant';
    static IS_GLOBE = 'isGlobe';
    static IS_MOBILE = 'isMobile';
    static IS_SPECULATIVE_APPLICATION = 'isSpeculativeApplication';
    static IS_JOB_APPLICATION = 'isJobApplication';
    static IS_EMPLOYEE = 'isEmployee';
    static IS_APPLICANT = 'isApplicant';
    static IS_SHIFTWORK_IMAGINABLE = 'isShiftWorkImaginable';
    static AGE_REQUEST = 'ageRequest';
    static CITY_WITH_RADIUS = 'cityWithRadius';
    static CITY_WITH_ZIP_CODE = 'cityWithZipCode';
    skillsIncludeFilterValues: string[] = [];
    skillsIncludeFilterOption = 'and';
    skillsExcludeFilterValues: string[] = [];
    selectedLanguages: string[] = [];
    cityForm: FormGroup;
    cityProposals: CityGeo[] = [];
    proposalsVisible = false;
    highlightedCity: CityGeo;
    city: '';
    selectedCity: CityGeo;
    minAge = 0;
    maxAge = 100;
    options: Options = {
        ceil: 100,
        floor: 0
    };

    isLoading = true;

    @Input() showFilter: boolean;
    @Input() possibleFilters: ApplicantFilter[];
    requestFilters: ApplicantFilter[] = [];
    @ViewChild('ageSlider') ageSlider: ElementRef;

    isFiltered = false;
    // die folgenden Booleans werden nur fuer die Filterung benoetigt
    onlyMyApplicants = false;
    onlyMyBranch: false;
    onlyMyTenant: false;
    speculativeApp: false;
    jobApplication: false;
    isEmployee: false;
    isApplicant: false;
    isGlobe: false;
    isMobile: false;
    isShiftWorkImaginable: false;

    applicantsSearchResponse: ApplicantSearchResponse;

    searchObject: ApplicantSearch = {
        pageRequest: {
            pageNum: 0,
            pageSize: 16,
            sortByList: [
                {name: 'firstContactReceived', direction: 'DESCENDING'}
            ]
        }
    };

    sortingCriterias = [
        {sort: [{name: 'firstContactReceived', direction: 'ASCENDING'}], title: 'NEWEST'},
        {sort: [{name: 'firstContactReceived', direction: 'DESCENDING'}], title: 'OLDEST'}
    ];

    selectedSorting: ApplicantSortingCriteria[] = this.sortingCriterias[0].sort;


    constructor(private applicantService: ApplicantService,
                private fb: FormBuilder,
                private router: Router,
                private dialogService: DialogService) {
    }

    get radiusControl(): AbstractControl {
        return this.cityForm.get('mobility').get('jobLocationRadius');
    }

    onFilterDelete(filterToDelete: ApplicantFilter) {
        this.searchObject.requestFilters = this.searchObject.requestFilters
            .filter(requestedFilter => requestedFilter.name !== filterToDelete.name);

        if (filterToDelete.name === ApplicantComponent.ONLY_MY_APPLICANTS) {
            this.onlyMyApplicants = false;
        } else if (filterToDelete.name === ApplicantComponent.IS_GLOBE) {
            this.isGlobe = false;
        } else if (filterToDelete.name === ApplicantComponent.IS_MOBILE) {
            this.isMobile = false;
        } else if (filterToDelete.name === ApplicantComponent.ONLY_MY_TENANT) {
            this.onlyMyTenant = false;
        } else if (filterToDelete.name === ApplicantComponent.ONLY_MY_BRANCH) {
            this.onlyMyBranch = false;
        } else if (filterToDelete.name === ApplicantComponent.IS_SPECULATIVE_APPLICATION) {
            this.speculativeApp = false;
        } else if (filterToDelete.name === ApplicantComponent.IS_JOB_APPLICATION) {
            this.jobApplication = false;
        } else if (filterToDelete.name === ApplicantComponent.IS_APPLICANT) {
            this.isApplicant = false;
        } else if (filterToDelete.name === ApplicantComponent.IS_EMPLOYEE) {
            this.isEmployee = false;
        } else if (filterToDelete.name === ApplicantComponent.IS_SHIFTWORK_IMAGINABLE) {
            this.isShiftWorkImaginable = false;
        } else if (filterToDelete.name === ApplicantComponent.CITY_WITH_RADIUS ||
            filterToDelete.name === ApplicantComponent.CITY_WITH_ZIP_CODE) {
            this.cityForm.reset();
        } else if (filterToDelete.name === ApplicantComponent.AGE_REQUEST) {
            let ageFilter: ApplicantFilter = this.possibleFilters.find(filter => filter.name === ApplicantComponent.AGE_REQUEST);
            if (ageFilter) {
                ageFilter.selection = undefined;
                this.options = {
                    ceil: this.maxAge,
                    floor: this.minAge
                };
            } else {
                const newAgeFilter: ApplicantFilter = {
                    name: ApplicantComponent.AGE_REQUEST,
                    selection: [this.minAge, this.maxAge],
                    values: undefined
                };
                this.possibleFilters.push(newAgeFilter);
            }
            ageFilter = this.requestFilters.find(filter => filter.name === ApplicantComponent.AGE_REQUEST);
            if (ageFilter) {
                ageFilter.selection = [this.minAge, this.maxAge];
            } else {
                const newAgeFilter: ApplicantFilter = {
                    name: ApplicantComponent.AGE_REQUEST,
                    selection: undefined,
                    values: undefined
                };
                this.options = {
                    ceil: this.maxAge,
                    floor: this.minAge
                };
                this.requestFilters.push(newAgeFilter);
            }
        } else if (filterToDelete.name === 'skillsAnd') {
            this.skillsIncludeFilterValues = [];
            this.skillsIncludeFilterOption = 'and';
        } else if (filterToDelete.name === 'skillsOr') {
            this.skillsIncludeFilterValues = [];
            this.skillsIncludeFilterOption = 'and';
        } else if (filterToDelete.name === 'skillsNot') {
            this.skillsExcludeFilterValues = [];
        } else if (filterToDelete.name === 'languages') {
            this.selectedLanguages = [];
        }
        this.searchApplicants();
    }

    isSearchByAge(): boolean {
        const ageFilter = this.possibleFilters.find(filter => filter.name === ApplicantComponent.AGE_REQUEST);

        let isSearchByAge = false;
        if (ageFilter && ageFilter.selection) {
            isSearchByAge = ageFilter.selection[0] !== String(this.minAge) || ageFilter.selection[1] !== String(this.maxAge);
        }
        return isSearchByAge;
    }

    public searchApplicants() {

        this.isFiltered = this.isSearchByAge() || this.onlyMyApplicants || this.isGlobe || this.isMobile ||
            this.onlyMyBranch || this.onlyMyTenant || this.jobApplication ||
            this.speculativeApp || this.isEmployee || this.isApplicant || this.isShiftWorkImaginable ||
            this.searchObject.requestFilters.filter(filter =>
                this.isNotABooleanFilter(filter)
            ).some(f => f.selection !== undefined || f.selection !== null);

        this.isLoading = true;

        if (this.isFiltered) {
            this.setSelectionForAllBooleanFilter();

            // CHOOSE_SORTING nicht mitsenden
            this.searchObject.pageRequest.sortByList =
                this.searchObject.pageRequest.sortByList.filter(sorting => sorting.name);
            this.applicantService.searchApplicants(this.searchObject).pipe(
                finalize(() => this.isLoading = false)
            ).subscribe(response => {
                this.applicantsSearchResponse = response;
                this.possibleFilters = response.possibleFilters;

                const ageFilter: ApplicantFilter = response.requestedFilters.find(filter => filter.name === ApplicantComponent.AGE_REQUEST);
                if (!ageFilter) {
                    response.possibleFilters.push({
                        name: ApplicantComponent.AGE_REQUEST,
                        selection: undefined,
                        values: undefined
                    });
                }

                const cityFilter: ApplicantFilter = response.requestedFilters.find(filter => filter.name === ApplicantComponent.CITY_WITH_RADIUS);
                if (!cityFilter) {
                    response.possibleFilters.push({
                        name: ApplicantComponent.CITY_WITH_RADIUS,
                        selection: undefined,
                        values: undefined
                    });
                }
                const cityZipFilter: ApplicantFilter = response.requestedFilters.find(filter => filter.name === ApplicantComponent.CITY_WITH_ZIP_CODE);
                if (!cityZipFilter) {
                    response.possibleFilters.push({
                        name: ApplicantComponent.CITY_WITH_ZIP_CODE,
                        selection: undefined,
                        values: undefined
                    });
                }


                const nationalityFilter = this.possibleFilters.find(filter => filter.name === 'nationality');

                if (nationalityFilter) {
                    this.possibleFilters.push({
                        name: 'excludeNationality',
                        selection: undefined,
                        values: nationalityFilter.values
                    });
                }

                // bei Antwort vom Server setzen wir die Dropdowns auf die gewählten Werte,
                // somit ist das FilterPanel in dem Zustand, den wir angefragt hatten
                this.applicantsSearchResponse.requestedFilters.forEach(requestedFilter => {
                    const filter = this.possibleFilters.find(possibleFilter => possibleFilter.name === requestedFilter.name);
                    if (filter) {
                        if (filter.name === ApplicantComponent.AGE_REQUEST) {
                            filter.selection = requestedFilter.values;
                        } else if (filter.name === ApplicantComponent.CITY_WITH_RADIUS) {
                            filter.selection = requestedFilter.values;
                        } else if (filter.name === ApplicantComponent.CITY_WITH_ZIP_CODE) {
                            filter.selection = requestedFilter.values;
                        } else {
                            filter.selection = requestedFilter.values[0];
                            if (filter.name === 'excludeNationality') {
                                filter.values.push(requestedFilter.values[0]);
                            }
                        }
                    }
                });
            });

        } else {
            this.fetchPossibleFilters();
        }
    }

    public changeSorting() {
        this.searchObject.pageRequest.sortByList = this.selectedSorting;
        this.searchApplicants();
    }

    public onfilterChanged(filters: ApplicantFilter[]) {
        this.searchObject.requestFilters = filters;
        this.searchObject.pageRequest.pageNum = 0;
        this.searchApplicants();
    }

    ngOnInit() {
        this.fetchPossibleFilters();
        this.createForm();
        this.initLocationSubscription();
        this.cityForm.get('mobility').get('jobLocationRadius').disable();
    }


    newApplicant() {
        this.dialogService.open(ApplicantCreationDialogComponent, {
            header: 'Bewerber anlegen'
        }).onClose.subscribe(applicantId => {
                if (applicantId) {
                    this.router.navigate(['applicant/edit', applicantId]);
                }
            }
        );
    }

    patchProposal(proposal: CityGeo, event: any = null): void {
        // noinspection DuplicatedCode
        if (event) {
            // when enter is pressed to use a highlightedProposal, prevent the default which would be the form submitting
            event.preventDefault();
        }
        this.selectedCity = proposal;
        (this.cityForm.get('mobility').get('city') as FormControl).patchValue(proposal.city, {emitEvent: false});
        (this.cityForm.get('mobility').get('cityGeoId') as FormControl).patchValue(proposal.id, {emitEvent: false});
        this.proposalsVisible = false;
        this.cityForm.get('mobility').get('jobLocationRadius').enable();
        const cityWithRadius = this.possibleFilters.find(filter => filter.name === ApplicantComponent.CITY_WITH_RADIUS);
        cityWithRadius.selection = [proposal.id, this.radiusControl.value, proposal.city];
    }

    highlightPreviousProposal(): void {
        const currentIndex = this.cityProposals.findIndex(proposal => proposal === this.highlightedCity);
        let newIndex = currentIndex - 1;
        if (newIndex < 0) {
            newIndex = this.cityProposals.length - 1;
        }
        this.highlightedCity = this.cityProposals[newIndex];
    }

    highlightNextProposal(): void {
        const currentIndex = this.cityProposals.findIndex(proposal => proposal === this.highlightedCity);
        let newIndex = currentIndex + 1;
        if (newIndex === this.cityProposals.length) {
            newIndex = 0;
        }
        this.highlightedCity = this.cityProposals[newIndex];
    }

    public onFilterSelection() {
        const requestFilters: ApplicantFilter[] = [];

        for (const filter of this.possibleFilters) {

            if (filter.name === ApplicantComponent.ONLY_MY_APPLICANTS) {
                filter.selection = this.onlyMyApplicants ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.IS_GLOBE) {
                filter.selection = this.isGlobe ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.IS_MOBILE) {
                filter.selection = this.isMobile ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.ONLY_MY_BRANCH) {
                filter.selection = this.onlyMyBranch ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.ONLY_MY_TENANT) {
                filter.selection = this.onlyMyTenant ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.IS_JOB_APPLICATION) {
                filter.selection = this.jobApplication ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.IS_SPECULATIVE_APPLICATION) {
                filter.selection = this.speculativeApp ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.IS_EMPLOYEE) {
                filter.selection = this.isEmployee ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.IS_APPLICANT) {
                filter.selection = this.isApplicant ? String(true) : undefined;
            } else if (filter.name === ApplicantComponent.IS_SHIFTWORK_IMAGINABLE) {
                filter.selection = this.isShiftWorkImaginable ? String(true) : undefined;
            }
            if (filter.name === ApplicantComponent.AGE_REQUEST && this.isSearchByAge()) {
                requestFilters.push({
                    name: filter.name,
                    values: [String(filter.selection[0]), String(filter.selection[1])]
                });
            } else if (filter.selection) {
                if (filter.name === ApplicantComponent.CITY_WITH_RADIUS) {
                    const values = filter.selection as string[];
                    requestFilters.push({
                        name: filter.name,
                        values: [values[0], values[1], values[2]]
                    });
                } else if (filter.name === ApplicantComponent.CITY_WITH_ZIP_CODE) {
                    const values = filter.selection as string[];
                    requestFilters.push({
                        name: filter.name,
                        values: [values[0]]
                    });
                } else {
                    requestFilters.push({
                        name: filter.name,
                        values: [filter.selection as string]
                    });
                }
            }
        }

        if (this.isSkillIncludeSearchActive()) {
            requestFilters.push({
                name: this.skillsIncludeFilterOption === 'and' ? 'skillsAnd' : 'skillsOr',
                values: this.skillsIncludeFilterValues
            });
        }

        if (this.isSkillsExcludeSearchActive()) {
            requestFilters.push({
                name: 'skillsNot',
                values: this.skillsExcludeFilterValues
            });
        }

        if (this.isLanguageSearchActive()) {
            requestFilters.push({
                name: 'languages',
                values: this.selectedLanguages
            });
        }

        this.onfilterChanged(requestFilters);
    }

    getChipText(filter: ApplicantFilter): string {
        if (filter.name === ApplicantComponent.ONLY_MY_APPLICANTS) {
            return 'Meine Bewerber';
        } else if (filter.name === ApplicantComponent.IS_GLOBE) {
            return 'Reisebereitschaft';
        } else if (filter.name === ApplicantComponent.IS_MOBILE) {
            return 'Mobilität';
        } else if (filter.name === ApplicantComponent.IS_SHIFTWORK_IMAGINABLE) {
            return 'Schichtarbeit';
        } else if (filter.name === ApplicantComponent.ONLY_MY_BRANCH) {
            return 'Meine Geschäftsstelle';
        } else if (filter.name === ApplicantComponent.ONLY_MY_TENANT) {
            return 'Meine Firma';
        } else if (filter.name === ApplicantComponent.IS_SPECULATIVE_APPLICATION) {
            return 'Initiativbewerbung';
        } else if (filter.name === ApplicantComponent.IS_JOB_APPLICATION) {
            return 'Stellenbewerbung';
        } else if (filter.name === ApplicantComponent.AGE_REQUEST) {
            return `Alter ${filter.values[0]}-${filter.values[1]}`;
        } else if (filter.name === ApplicantComponent.CITY_WITH_RADIUS) {
            return `Wunschort: ${filter.values[1]}km um ${filter.values[2]}`;
        } else if (filter.name === ApplicantComponent.CITY_WITH_ZIP_CODE) {
            return `Wunschort nach PLZ: ${filter.values[0]}`;
        } else if (filter.name === 'skillsAnd') {
            return this.skillsIncludeFilterValues.join(' & ');
        } else if (filter.name === 'skillsOr') {
            return this.skillsIncludeFilterValues.join(' ODER ');
        } else if (filter.name === 'skillsNot') {
            return `NICHT ${this.skillsExcludeFilterValues.join(' ODER ')}`;
        } else if (filter.name === 'languages') {
            return this.selectedLanguages.join(', ');
        } else if (filter.name === 'status') {
            const filterName = filter.values + '';
            if (filterName === 'NEW') {
                return 'Neu';
            } else if (filterName === 'DECLINED') {
                return 'Abgelehnt';
            } else if (filterName === 'IN_WORK') {
                return 'Angestellt';
            } else if (filterName === 'IN_DEALING_WITH') {
                return 'in Bearbeitung';
            }
        } else if (filter.name === 'applicantStatus') {
            const filterName = filter.values + '';
            if (filterName === 'EMPLOYEE') {
                return 'Mitarbeiter(in)';
            } else if (filterName === 'APPLICANT') {
                return 'Bewerber(in)';
            }
        } else if (filter.name === 'state') {
            const filterName = filter.values + '';
            return filterName === 'reserved' ? 'Reserviert' : 'Angefragt';
        } else if (filter.name === 'excludeNationality') {
            return filter.values + ' ausschließend';
        } else {
            return typeof filter.values === 'string' ? filter.values : filter.values[0];
        }
    }

    onPageChanged(event: any) {
        this.searchObject.pageRequest.pageNum = event.page;
        this.searchApplicants();
    }

    onFilterStatus(status: string) {
        const requestFilters: ApplicantFilter[] = [];
        if (status === 'RESERVED' || status === 'REQUESTED') {
            if (status === 'RESERVED') {
                requestFilters.push({
                    name: 'state',
                    values: ['reserved']
                });
            } else {
                requestFilters.push({
                    name: 'state',
                    values: ['questioned']
                });
            }
        } else {
            requestFilters.push({
                name: 'status',
                values: [status]
            });
        }

        this.onfilterChanged(requestFilters);
    }

    onAgeChanged(changeContext: ChangeContext) {
        const ageFilter = this.possibleFilters.find(filter => filter.name === ApplicantComponent.AGE_REQUEST);
        ageFilter.selection = [changeContext.value, changeContext.highValue];
    }

    onZipCodeFilterChanged(event) {
        const zipFilter = this.possibleFilters.find(filter => filter.name === ApplicantComponent.CITY_WITH_ZIP_CODE);
        zipFilter.selection = [event.target.value];
    }

    possibleFiltersHaveAgeFilter(): boolean {
        return this.possibleFilters.find(filter => filter.name === ApplicantComponent.AGE_REQUEST) !== undefined;
    }

    possibleFiltersHaveCityRadius(): boolean {
        return this.possibleFilters.find(filter => filter.name === ApplicantComponent.CITY_WITH_RADIUS) !== undefined;
    }

    possibleFiltersHaveCityWithZipCode(): boolean {
        return this.possibleFilters.find(filter => filter.name === ApplicantComponent.CITY_WITH_ZIP_CODE) !== undefined;
    }

    private isSkillIncludeSearchActive(): boolean {
        return this.skillsIncludeFilterValues.length > 0;
    }

    private isSkillsExcludeSearchActive() {
        return this.skillsExcludeFilterValues.length > 0;
    }

    // noinspection DuplicatedCode
    private initLocationSubscription() {
        const jobLocation = this.cityForm.get('mobility').get('city');

        jobLocation.valueChanges.pipe(
            tap(() => {
                this.selectedCity = null;
                this.highlightedCity = null;
                this.proposalsVisible = false;
            }),
            debounce(() => timer(300)),
            switchMap(value => {
                // only show results if user typed something
                if (value.length > 0) {
                    return this.applicantService.findCitiesByNameOrZip(value);
                }
                return of([] as CityGeo[]);
            }),
            tap(proposals => {
                // automatically select city if it's the only option
                if (proposals.length === 1 && proposals[0].city === jobLocation.value) {
                    this.selectedCity = proposals[0];
                    return;
                }
                // mark first proposal as highlighted
                if (proposals.length > 0) {
                    this.highlightedCity = proposals[0];
                    this.proposalsVisible = true;
                }
            })
        ).subscribe(proposals => {
            this.cityProposals = proposals;
        });
    }

    private createForm() {
        this.cityForm = this.fb.group({
            mobility: this.fb.group({
                city: '',
                cityGeoId: '', // bleibt fuer den Post enthalten
                jobLocationRadius: '',
                zipCode: ''
            })
        });
    }


    // noinspection JSMethodCanBeStatic
    private isNotABooleanFilter(filter) {
        return filter.name !== ApplicantComponent.ONLY_MY_APPLICANTS &&
            filter.name !== ApplicantComponent.ONLY_MY_BRANCH &&
            filter.name !== ApplicantComponent.ONLY_MY_TENANT &&
            filter.name !== ApplicantComponent.IS_GLOBE &&
            filter.name !== ApplicantComponent.IS_MOBILE &&
            filter.name !== ApplicantComponent.IS_JOB_APPLICATION &&
            filter.name !== ApplicantComponent.IS_SPECULATIVE_APPLICATION &&
            filter.name !== ApplicantComponent.IS_APPLICANT &&
            filter.name !== ApplicantComponent.IS_SHIFTWORK_IMAGINABLE &&
            filter.name !== ApplicantComponent.IS_EMPLOYEE;
    }

    private setSelectionForAllBooleanFilter() {
        this.setSelectionForBooleanFilter(ApplicantComponent.ONLY_MY_APPLICANTS, this.onlyMyApplicants);
        this.setSelectionForBooleanFilter(ApplicantComponent.ONLY_MY_TENANT, this.onlyMyTenant);
        this.setSelectionForBooleanFilter(ApplicantComponent.ONLY_MY_BRANCH, this.onlyMyBranch);
        this.setSelectionForBooleanFilter(ApplicantComponent.IS_MOBILE, this.isMobile);
        this.setSelectionForBooleanFilter(ApplicantComponent.IS_GLOBE, this.isGlobe);
        this.setSelectionForBooleanFilter(ApplicantComponent.IS_SPECULATIVE_APPLICATION, this.speculativeApp);
        this.setSelectionForBooleanFilter(ApplicantComponent.IS_APPLICANT, this.isApplicant);
        this.setSelectionForBooleanFilter(ApplicantComponent.IS_EMPLOYEE, this.isEmployee);
        this.setSelectionForBooleanFilter(ApplicantComponent.IS_JOB_APPLICATION, this.jobApplication);
        this.setSelectionForBooleanFilter(ApplicantComponent.IS_SHIFTWORK_IMAGINABLE, this.isShiftWorkImaginable);
    }

    private setSelectionForBooleanFilter(filterName: string, isActive: boolean) {
        this.searchObject.requestFilters.filter(filter => filter.name === filterName)
            .forEach(filter => filter.selection = String(isActive));
    }

    private fetchPossibleFilters() {
        const searchPossibleFilters: ApplicantSearch = {
            pageRequest: {
                pageNum: 0,
                pageSize: 1
            }
        };

        this.isLoading = true;

        this.applicantService.searchApplicants(searchPossibleFilters).pipe(
            finalize(() => this.isLoading = false)
        ).subscribe(response => {
            this.possibleFilters = response.possibleFilters;

            const nationalityFilter = this.possibleFilters.find(filter => filter.name === 'nationality');

            if (nationalityFilter) {
                this.possibleFilters.push({
                    name: 'excludeNationality',
                    selection: undefined,
                    values: nationalityFilter.values
                });
            }

            if (!this.possibleFiltersHaveAgeFilter()) {
                this.possibleFilters.push({
                    name: ApplicantComponent.AGE_REQUEST,
                    selection: undefined,
                    values: undefined
                });
            }
            if (!this.possibleFiltersHaveCityRadius()) {
                this.possibleFilters.push({
                    name: ApplicantComponent.CITY_WITH_RADIUS,
                    selection: undefined,
                    values: undefined
                });
            }
            if (!this.possibleFiltersHaveCityWithZipCode()) {
                this.possibleFilters.push({
                    name: ApplicantComponent.CITY_WITH_ZIP_CODE,
                    selection: undefined,
                    values: undefined
                });
            }
        });
    }

    private isLanguageSearchActive() {
        return this.selectedLanguages.length > 0;
    }
}

Вот полезная нагрузка, которую я отправляю на сервер:

{"pageRequest":{"pageNum":0,"pageSize":16,"sortByList":[{"name":"firstContactReceived","direction":"DESCENDING"}]},"requestFilters":[{"name":"status","values":["NEW"]}]}

и это пример ответа:

{,…}
page: {content: [{id: "4381eac1-01a9-44d0-a4c2-c8df3c54e615", firstName: null, lastName: null,…},…],…}
content: [{id: "4381eac1-01a9-44d0-a4c2-c8df3c54e615", firstName: null, lastName: null,…},…]
0: {id: "4381eac1-01a9-44d0-a4c2-c8df3c54e615", firstName: null, lastName: null,…}
age: null
anonymous: true
applicantStatus: "APPLICANT"
availableFrom: "2019-10-28"
bestMatch: 75
city: "Silz, Pfalz"
contractType: "Festanstellung"
email: null
firstContactReceived: "2019-10-28"
firstName: null
id: "4381eac1-01a9-44d0-a4c2-c8df3c54e615"
isFromMyBranch: false
isFromMyTenant: false
isGlobe: false
isJobApplication: true
isMobile: true
isOwnApplicant: true
isPossibleDuplicate: true
isShiftWorkImaginable: true
isSpeculativeApplication: false
languages: []
lastName: null
nationality: null
phoneNumber: null
profession: "Kaufmann / Kauffrau für Spedition und Logistikdienstleistung"
profileCompleteness: 22
reservations: []
skills: ["Prüfplan"]
0: "Prüfplan"
status: "NEW"
tenantId: "57ce8d3e-7fb1-4efc-8e83-66148508e22c"
twentyFourHourWarning: false
vehicle: "CAR"
zipCode: ""
1: {id: "4dbcefba-f69a-416b-9ada-5ba2adf24824", firstName: "John", lastName: "Macintosh",…}
2: {id: "288923f5-17dd-4d8a-96a3-5756ec1d6a1b", firstName: "John", lastName: "MacintoshderZweite",…}
3: {id: "13c578d4-37c2-4275-89f6-e33ace8181af", firstName: "Gerti", lastName: "Müller",…}
4: {id: "e7af4b2d-35c4-4e9d-b30c-e151333e0fed", firstName: "Gertrude", lastName: "Müller",…}
5: {id: "350db96d-44ed-4ceb-84c7-2b6ef495fb81", firstName: "Lisa", lastName: "Schubert",…}
6: {id: "bc8412dd-f4dc-4a93-852d-8448c2da2ce8", firstName: "Snooze", lastName: "Light",…}
7: {id: "ced250ca-d346-4227-ab7b-f9b2fa8a012d", firstName: "Michael", lastName: "Zimmermann",…}
8: {id: "e3c55e9e-c916-4199-bdaa-a01a15e07813", firstName: "Moritz", lastName: "Zimmermann",…}
9: {id: "f8618617-f757-4161-b715-ed61631abfa9", firstName: "Mina", lastName: "Kreuzer",…}
10: {id: "df0650c9-aaf5-4630-9924-2a25224683d7", firstName: "Theo", lastName: "Dor",…}
.....
15: {id: "eae55a93-727e-47cb-8dfd-074c92bdbc48", firstName: "Jan", lastName: "Janssen",…}
totalCount: 61
possibleFilters: [{name: "profession",…}, {name: "onlyMyApplicants", values: ["false", "true"]},…]
requestedFilters: [{name: "status", values: ["NEW"]}]

pageSize всегда будет в 16.

...