angularangular-bootstrap

Type number is not assignable to type string | readonly string[] in angular bootstrap table sorting feature


I am creating a project in Angular 13 version. I have used the Angular Bootstrap library to create a list which is sortable and searchable. I have followed the below link to create the list: https://ng-bootstrap.github.io/#/components/table/examples#sortable The code is below:

Service

interface SearchResult {
    incomeHeadLists: IncomeHeadInfoModel[];
    total: number;
}

interface IncomeHeadListState {
    page: number;
    pageSize: number;
    searchTitle: string;
    searchType: string;
    searchAmount: number;
    searchTotalIncome: number;
    sortColumn: string;
    sortDirection: SortDirection;
}

const compare = (v1: string | number | boolean, v2: string | number | boolean) => v1 < v2 ? -1 : v1 > v2 ? 1 : 0;

function sort(headLists: IncomeHeadInfoModel[], column: string, direction: string): IncomeHeadInfoModel[] {
    if (direction === '' || column === '') {
        return headLists;
    }
    else {
        return [...headLists].sort((a: any, b: any) => {
            const res = compare(a[column], b[column]);
            return direction === 'asc' ? res : -res;
        });
    }
}


@Injectable({
    providedIn: 'root'
})
export class IncomeHeadListInfoService {

    private apiPATH = 'incomeHead/';
    public _search$ = new Subject<void>();
    private _incomeHeadLists$ = new BehaviorSubject<IncomeHeadInfoModel[]>([]);
    private _total$ = new BehaviorSubject<number>(0);

    private _state: IncomeHeadListState = {
        page: 1,
        pageSize: 5,
        searchTitle: '',
        searchType: '',
        searchAmount: null,
        searchTotalIncome: null,
        sortColumn: '',
        sortDirection: ''
    };

    constructor(private service: ApplicationService) {
        this._search$.pipe(
            debounceTime(200),
            switchMap(() => this._search()),
            delay(200)).subscribe(result => {
                this._incomeHeadLists$.next(result.incomeHeadLists);
                this._total$.next(result.total);
            });

        this._search$.next();
    }

    // getter functions
    get incomeHeadLists$() { return this._incomeHeadLists$.asObservable(); }
    get total$() { return this._total$.asObservable(); }
    get page() { return this._state.page; }
    get pageSize() { return this._state.pageSize; }
    get searchTitle() { return this._state.searchTitle; }
    get searchType() { return this._state.searchType; }
    get searchAmount() { return this._state.searchAmount; }
    get searchTotalIncome() { return this._state.searchTotalIncome; }

    // setter functions
    set page(page: number) { this._set({ page }); }
    set pageSize(pageSize: number) { this._set({ pageSize }); }
    set searchTitle(searchTitle: string) { this._set({ searchTitle }); }
    set searchType(searchType: string) { this._set({ searchType }); }
    set searchAmount(searchAmount: number) { this._set({ searchAmount }); }
    set searchTotalIncome(searchTotalIncome: number) { this._set({ searchTotalIncome }); }
    set sortColumn(sortColumn: string) { this._set({ sortColumn }); }
    set sortDirection(sortDirection: SortDirection) { this._set({ sortDirection }); }

    private _set(patch: Partial<IncomeHeadListState>) {
        Object.assign(this._state, patch);
        this._search$.next();
    }

    private _search(): Observable<SearchResult> {
        return this.service.get<IncomeHeadListInfoResultModel>(`${this.apiPATH}lists`).pipe(map(data => {
            const {
                sortColumn,
                sortDirection,
                pageSize,
                page,
                searchTitle,
                searchType,
                searchAmount,
                searchTotalIncome
            } = this._state;

            if (data) {
                let lists = data.IncomeHeads

                // 1. sort              
                let incomeHeadLists = sort(lists, sortColumn, sortDirection);

                // 2. filter
                if (searchTitle) {
                    incomeHeadLists = incomeHeadLists.filter(x => x.Title.toLowerCase().includes(searchTitle.toLowerCase()))
                }

                if (searchType) {
                    incomeHeadLists = incomeHeadLists.filter(x => x.Type.toLowerCase().includes(searchType.toLowerCase()))
                }

                if (searchAmount) {
                    incomeHeadLists = incomeHeadLists.filter(x => x.Amount === searchAmount)
                }

                if (searchTotalIncome) {
                    incomeHeadLists = incomeHeadLists.filter(x => x.TotalIncome === searchTotalIncome)
                }

                const total = incomeHeadLists.length;

                // 3. paginate
                incomeHeadLists = incomeHeadLists.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize);

                return { incomeHeadLists, total };
            }
            else {
                return null;
            }
        }));
    }
}

HTML Component:

<table class="table table-striped table-bordered">
    <thead>
        <tr>
            <th scope="col">#</th>
            <th scope="col" sortable="Title" (sort)="onSort($event)">Title</th>
            <th scope="col" sortable="Type" (sort)="onSort($event)">Type</th>
            <th scope="col" sortable="Amount" (sort)="onSort($event)">Amount</th>
            <th scope="col" sortable="TotalIncome" (sort)="onSort($event)">Total Bill</th>
            <th scope="col" colspan="2">Action</th>
        </tr>
        <tr>
            <th></th>
            <th><input class="form-control" type="text" name="searchTitle" [(ngModel)]="service.searchTitle" /></th>
            <th><input class="form-control" type="text" name="searchType" [(ngModel)]="service.searchType" /></th>
            <th><input class="form-control" type="text" name="searchAmount" [(ngModel)]="service.searchAmount" /></th>
            <th><input class="form-control" type="text" name="searchTotalIncome" [(ngModel)]="service.searchTotalIncome" /></th>
            <th></th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let incomeHead of incomeHeadLists$ | async; let i = index">
            <th scope="row">{{((service.page * service.pageSize) + (i + 1))-service.pageSize}}</th>
            <td><ngb-highlight [result]="incomeHead.Title" [term]="service.searchTitle"></ngb-highlight></td>
            <td><ngb-highlight [result]="incomeHead.Type" [term]="service.searchType"></ngb-highlight></td>
            <td><ngb-highlight [result]="incomeHead.Amount | number" [term]="service.searchAmount"></ngb-highlight></td>
            <td><ngb-highlight [result]="incomeHead.TotalIncome | number" [term]="service.searchTotalIncome"></ngb-highlight></td>
            <td>
                <toogle-button [selectedCheckStatus]="incomeHead.IsActive"
                    (onChangeCheckStatus)="activeOrInactiveIncomeHead($event, incomeHead, confirmContent)">
                </toogle-button>
            </td>
            <td>
                <button type="button" class="btn btn-outline-info"
                    (click)="editIncomeHead(incomeHead, incomeHeadModalContent)"><i class="fas fa-edit"></i>
                </button>
            </td>
        </tr>
    </tbody>
</table>

But I got the below errors:

ERROR

src/app/pages/setup/income-head-list/income-head-list.component.html:32:71

  • error TS2322: Type 'number' is not assignable to type 'string | readonly string[]'.

32 <ngb-highlight [result]="incomeHead.Amount | number" [term]="service.searchAmount"> ~~~~

src/app/pages/setup/income-head-list/income-head-list.component.ts:15:15 15 templateUrl: './income-head-list.component.html', ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Error occurs in the template of component IncomeHeadListComponent.

ERROR

src/app/pages/setup/income-head-list/income-head-list.component.html:33:76

  • error TS2322: Type 'number' is not assignable to type 'string | readonly string[]'.

33 <ngb-highlight [result]="incomeHead.TotalIncome | number" [term]="service.searchTotalIncome"> ~~~~

src/app/pages/setup/income-head-list/income-head-list.component.ts:15:15 15 templateUrl: './income-head-list.component.html', ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Error occurs in the template of component IncomeHeadListComponent.

enter image description here


Solution

  • The problem is in your "ngb-highlight" ((that need two strings). To convert a number to string simply add '' at first of the number. You need make it in the two [term] than mannage number

    <td><ngb-highlight [result]="incomeHead.Amount | number" 
                       [term]="''+service.searchAmount">
        </ngb-highlight></td>
    <td><ngb-highlight [result]="incomeHead.TotalIncome | number"
                       [term]="''+service.searchTotalIncome">
        </ngb-highlight></td>