Angular - Select Dropdown did not display the records to be updated

Discussão em 'Angular' iniciado por Ayobamilaye, Fevereiro 6, 2025.

  1. Ayobamilaye

    Ayobamilaye Guest

    I have this Angular code. When I am to update the Merged records, I expect the Select Dropdown to display the records to be updated but blank, until I have to select afresh.

    I created two records through:

    <button *ngIf="!hasMergedMerchants(row.id)"
    class="btn btn-info btn-sm ms-1"
    (click)="mergeMerchants(row)">
    <i class="fa fa-link"></i> Merge
    </button>


    but when I loaded merchant-merge.component through:

    <button *ngIf="hasMergedMerchants(row.id)"
    class="btn btn-warning btn-sm ms-1"
    (click)="updateMerge(row)">
    <i class="fa fa-edit"></i> Update Merge
    </button>


    the dropdown for the already created records in:

    merchant-merge.component.html did not display anything for the user to see

    Kindly assist

    merchant-merge.component.html did not display anything for the user to see

    MAIN CODE:

    mechant-merge.model.ts:

    export interface IMerchantMerge {
    merchantId: string;
    subAggregators: ISubAggregator[];
    }
    export interface ISubAggregator {
    aggregatorId: string;
    aggregatorName: string;
    }


    api-response.model.ts:

    export interface IApiResponse<T> {
    data: T;
    successful: boolean;
    message: string;
    statusCode: number;
    responseCode?: string;
    responseDescription?: string;
    }


    merchant.service.ts:

    export class MerchantService {
    baseUrl = environment.baseHost;
    token = localStorage.getItem('token');
    private loading = new BehaviorSubject<boolean>(false);
    public loading$ = this.loading.asObservable();

    httpOptions = {
    headers: new HttpHeaders({ 'Content-type': 'application/json' }).set(
    'Authorization',
    `Bearer ${this.token}`
    ),
    };

    constructor(private http: HttpClient) {}

    getMerchantAggregators(merchantId: string): Observable<IApiResponse<ISubAggregator[]>> {
    return this.http.get<IApiResponse<ISubAggregator[]>>(
    `${this.baseUrl}/merchants/aggregators/${merchantId}`,
    this.httpOptions
    );
    }

    getAvailableAggregators(merchantId: string): Observable<IApiResponse<ISubAggregator[]>> {
    return this.http.get<IApiResponse<ISubAggregator[]>>(
    `${this.baseUrl}/merchants/available-aggregators/${merchantId}`,
    this.httpOptions
    );
    }

    mergeMerchants(request: IMerchantMerge): Observable<IApiResponse<boolean>> {
    return this.http.post<IApiResponse<boolean>>(`${this.baseUrl}/merchants/merge`, request);
    }

    updateMergedMerchants(request: IMerchantMerge): Observable<IApiResponse<boolean>> {
    return this.http.put<IApiResponse<boolean>>(`${this.baseUrl}/merchants/update-merge`, request);
    }
    ...
    }


    merchant-merge.component.ts:

    import { Component, EventEmitter, Input, OnInit } from '@angular/core';
    import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
    import { BsModalRef } from 'ngx-bootstrap/modal';
    import { ToastrService } from 'ngx-toastr';
    import { MerchantService } from 'src/app/features/admin/services/merchant.service';
    import { ISubAggregator, IMerchantMerge } from '../../../models/merchant/merchant-merge.model';
    import { IApiResponse } from '../../../../../shared/models/api-response.model';
    import { firstValueFrom } from 'rxjs';

    @Component({
    selector: 'app-merchant-merge',
    templateUrl: './merchant-merge.component.html',
    styleUrls: ['./merchant-merge.component.scss']
    })
    export class MerchantMergeComponent implements OnInit{
    pageTitle = 'Merchant';
    pageLabel = 'Merge Merchants';

    @Input()
    merchantId!: string;
    @Input()
    aggregatorId!: string;
    @Input()
    merchantName!: string;
    @Input() isUpdate: boolean = false;

    mergeForm!: FormGroup;
    availableAggregators: ISubAggregator[] = [];
    selectedAggregators: Set<string> = new Set();
    isLoading = false;
    event: EventEmitter<string> = new EventEmitter();

    constructor(
    private fb: FormBuilder,
    private merchantService: MerchantService,
    private toastr: ToastrService,
    public bsModalRef: BsModalRef
    ) {
    this.mergeForm = this.fb.group({
    aggregators: this.fb.array([], Validators.required)
    });
    }

    ngOnInit() {
    this.loadData();
    }

    async loadData() {
    this.isLoading = true;
    try {
    const availableRes = await firstValueFrom(this.merchantService.getAvailableAggregators(this.merchantId));
    this.availableAggregators = availableRes?.data ?? [];

    if (this.isUpdate) {
    const currentRes = await firstValueFrom(this.merchantService.getMerchantAggregators(this.merchantId));
    currentRes?.data?.forEach(agg => this.addAggregator(agg));
    }
    } catch (error) {
    this.toastr.error('Error loading aggregators');
    } finally {
    this.isLoading = false;
    }
    }

    get aggregatorsFormArray() {
    return this.mergeForm.get('aggregators') as FormArray;
    }

    addAggregator(aggregator?: ISubAggregator) {
    const aggregatorGroup = this.fb.group({
    aggregatorId: ['', Validators.required],
    aggregatorName: ['']
    });

    if (aggregator) {
    aggregatorGroup.patchValue(aggregator);
    this.selectedAggregators.add(aggregator.aggregatorId);
    }

    this.aggregatorsFormArray.push(aggregatorGroup);
    }

    removeAggregator(index: number) {
    const aggregator = this.aggregatorsFormArray.at(index).value;
    this.selectedAggregators.delete(aggregator.aggregatorId);
    this.aggregatorsFormArray.removeAt(index);
    }

    onAggregatorSelect(event: any, index: number) {
    const selectedAggregator = this.availableAggregators.find(a => a.aggregatorId === event);
    if (selectedAggregator) {
    this.aggregatorsFormArray.at(index).patchValue({
    aggregatorName: selectedAggregator.aggregatorName
    });
    this.selectedAggregators.add(selectedAggregator.aggregatorId);
    }
    }

    async onSubmit() {
    if (this.mergeForm.invalid) {
    return;
    }

    this.isLoading = true;
    try {
    const request = {
    merchantId: this.merchantId,
    subAggregators: this.aggregatorsFormArray.value
    };

    const response = this.isUpdate ?
    await this.merchantService.updateMergedMerchants(request).toPromise() :
    await this.merchantService.mergeMerchants(request).toPromise();

    if (response?.successful) {
    this.toastr.success(response?.message ?? 'Operation successful');
    this.event.emit('OK');
    this.bsModalRef.hide();
    } else {
    this.toastr.error(response?.message ?? 'An error occurred');
    }
    } catch (error) {
    this.toastr.error('Error processing request');
    } finally {
    this.isLoading = false;
    }
    }
    }


    merchant-merge.component.html:


    <div role="document">
    <div class="modal-content">
    <div class="modal-header">
    <h4 class="modal-title pull-left">
    {{ isUpdate ? 'Update Merged' : 'Merge' }} Merchants to - {{ merchantName }}
    </h4>
    <button type="button" class="btn-close close pull-right" aria-label="Close" (click)="bsModalRef.hide()">
    <span aria-hidden="true" class="visually-hidden">&times;</span>
    </button>
    </div>

    <div class="modal-body">
    <form [formGroup]="mergeForm" (ngSubmit)="onSubmit()">
    <div class="mb-3">
    <button type="button" class="btn btn-primary" (click)="addAggregator()" [disabled]="isLoading">
    <i class="fa fa-plus" aria-hidden="true"></i> Add Merchant
    </button>
    </div>

    <div formArrayName="aggregators">
    <div *ngFor="let aggregator of aggregatorsFormArray.controls; let i = index" class="row mb-3">
    <div [formGroupName]="i" class="col-md-11">
    <ng-select
    [items]="availableAggregators"
    [virtualScroll]="true"
    [selectOnTab]="true"
    [searchable]="true"
    bindValue="aggregatorId"
    bindLabel="aggregatorName"
    placeholder="Select Merchant"
    formControlName="aggregatorId"
    (change)="onAggregatorSelect($event, i)"
    [loading]="isLoading"
    [clearable]="false"
    >
    </ng-select>
    </div>
    <div class="col-md-1">
    <button type="button" class="btn btn-danger" (click)="removeAggregator(i)">
    <i class="fas fa-trash"></i>
    </button>
    </div>
    </div>
    </div>

    <div class="modal-footer">
    <button type="button" class="btn btn-secondary" (click)="bsModalRef.hide()">
    Close
    </button>
    <button type="submit" class="btn btn-success" [disabled]="mergeForm.invalid || isLoading">
    <i class="fa fa-save" aria-hidden="true"></i> {{ isLoading ? 'Processing...' : 'Submit' }}
    </button>
    </div>
    </form>
    </div>
    </div>
    </div>

    merchant.component.html:

    <ng-container *ngIf="hasMergedMerchants(row.id) !== null">
    <button *ngIf="!hasMergedMerchants(row.id)"
    class="btn btn-info btn-sm ms-1"
    (click)="mergeMerchants(row)">
    <i class="fa fa-link"></i> Merge
    </button>

    <button *ngIf="hasMergedMerchants(row.id)"
    class="btn btn-warning btn-sm ms-1"
    (click)="updateMerge(row)">
    <i class="fa fa-edit"></i> Update Merge
    </button>
    </ng-container>


    merchant.component.ts:

    import { Component, TemplateRef, EventEmitter, Input, OnInit, OnDestroy } from '@angular/core';
    import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
    import { PaginatePipe } from 'ngx-pagination';
    import { FormBuilder, FormGroup } from '@angular/forms';
    import { ToastrService } from 'ngx-toastr';
    import { MerchantService } from 'src/app/features/admin/services/merchant.service';
    import {
    IMerchantPageResult,
    IMerchantPagingFilter,
    IMerchantList,
    } from 'src/app/features/admin/models/merchant/merchant-list.model';
    import { MerchantCreateComponent } from 'src/app/features/admin/pages/merchants/merchant-create/merchant-create.component';
    import { DatePipe } from '@angular/common';
    import { OrderPipe } from 'ngx-order-pipe';
    import { saveAs } from 'file-saver';
    import Swal from 'sweetalert2';
    import { MerchantMergeComponent } from './merchant-merge/merchant-merge.component';
    import { finalize, map, Observable, shareReplay, Subject, takeUntil, tap } from 'rxjs';

    @Component({
    selector: 'app-merchants',
    templateUrl: './merchants.component.html',
    styleUrls: ['./merchants.component.scss'],
    })
    export class MerchantsComponent implements OnInit, OnDestroy{
    bsModalRef?: BsModalRef;
    pageTitle = 'Merchants';
    pageLabel = 'Merchant List';
    advanceSearch = 'Advance Search';
    search = 'Search';
    bankRolesData!: any[];
    merchantSearchCriteriaData!: any[];
    data1: any;
    merchantList: any;
    isLoading = false;
    isSubmitted = false;
    showModal!: boolean;
    selectedName: string = '';

    selectedSearchCriteria: any;
    allMerchantList!: IMerchantList[];
    pageResult!: IMerchantPageResult<IMerchantList[]>;

    userStatus = localStorage.getItem('isActive');
    userRole = localStorage.getItem('role');
    userAdmin = localStorage.getItem('isAdmin');
    event: EventEmitter<any> = new EventEmitter();
    router: any;
    private mergedMerchantCache = new Map<string, boolean>();
    private destroy$ = new Subject<void>();
    private mergedStatusRequests = new Map<string, Observable<boolean>>();

    constructor(
    private fb: FormBuilder,
    private merchantService: MerchantService,
    private datePipe: DatePipe,
    private toastr: ToastrService,
    private modalService: BsModalService,
    private orderPipe: OrderPipe,
    private paginatePipe: PaginatePipe
    ) {
    }

    ngOnInit(): void {
    this.isLoading = true;
    this.loadAllMerchants();
    }

    loadAllMerchants() {
    this.isLoading = true;
    this.merchantService.getAllMerchantsFilter(this.filter)
    .pipe(
    finalize(() => this.isLoading = false),
    takeUntil(this.destroy$)
    )
    .subscribe({
    next: (result) => {
    this.pageResult = result;
    this.allMerchantList = result.pageItems;
    // Clear cache for non-visible merchants
    this.cleanupCache();
    },
    error: (error) => {
    this.toastr.error('Error loading merchants');
    this.isLoading = false;
    }
    });
    }

    hasMergedMerchants(merchantId: string): boolean | null {
    if (!this.mergedMerchantCache.has(merchantId)) {
    if (!this.mergedStatusRequests.has(merchantId)) {
    const request = this.merchantService.getMerchantAggregators(merchantId).pipe(
    map(response => response.data?.length > 0),
    tap(hasMerged => this.mergedMerchantCache.set(merchantId, hasMerged)),
    shareReplay(1),
    takeUntil(this.destroy$)
    );
    this.mergedStatusRequests.set(merchantId, request);
    }
    this.mergedStatusRequests.get(merchantId)?.subscribe();
    return null;
    }
    return this.mergedMerchantCache.get(merchantId) ?? null;
    }

    mergeMerchants(merchant: IMerchantList) {
    this.bsModalRef = this.modalService.show(MerchantMergeComponent, {
    class: 'modal-lg',
    initialState: {
    merchantId: merchant.id,
    merchantName: merchant.aggregatorName,
    isUpdate: false
    }
    });

    this.bsModalRef.content.event.subscribe(() => {
    this.mergedMerchantCache.delete(merchant.id);
    this.loadAllMerchants();
    });
    }

    updateMerge(merchant: IMerchantList) {
    this.bsModalRef = this.modalService.show(MerchantMergeComponent, {
    class: 'modal-lg',
    initialState: {
    merchantId: merchant.id,
    aggregatorId: merchant.aggregatorId,
    merchantName: merchant.aggregatorName,
    isUpdate: true
    }
    });

    this.bsModalRef.content.event.subscribe(() => {
    this.mergedMerchantCache.delete(merchant.id);
    this.loadAllMerchants();
    });
    }

    private cleanupCache() {
    const visibleIds = new Set(this.allMerchantList.map(m => m.id));
    for (const id of this.mergedMerchantCache.keys()) {
    if (!visibleIds.has(id)) {
    this.mergedMerchantCache.delete(id);
    this.mergedStatusRequests.delete(id);
    }
    }
    }

    ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
    this.mergedMerchantCache.clear();
    this.mergedStatusRequests.clear();
    }
    }


    I expected to see this: [​IMG]

    but I am seeing this:

    [​IMG]

    Kindly help resolve it

    Continue reading...

Compartilhe esta Página