3

How can I convert complex Object to a Query String intended for WebApi:

export enum FilterCondition {
    Equal,
    NotEqual,
    GreaterThan,
    LessThan,
    GreaterThanEqual,
    LessThanEqual
}

export class QueryParameter {
    propertyName: string;
    filterCondition: FilterCondition
    value: string;
}

export class QueryOptions {
    queryParameters: QueryParameter[] = new Array<QueryParameter>();
}

I'm trying to write a generic function to convert a complex type to basic query I wrote this function to convert a simple object to query string

export class QueryStringBuilder {
    static BuildParametersFromSearch<T>(obj: T): URLSearchParams {
        let params: URLSearchParams = new URLSearchParams();

        const objectKeys = Object.keys(obj) as Array<keyof T>;
        for (let key of objectKeys) {
            params.set(key, obj[key])
        }

        return params;
    }
}

But I'm having trouble grasping how I can do this recursively for complex object with enums and arrays etc? Can anyone point me in the right direction or to something build in that already does this?

1 Answer 1

3

Incase anyone was wondering how to do this, I've written an extension that should work with c# .Net Core 1.1 and Typescript 2.2.2 WebApi which looks like so.

Remember to include these two imports where you are using it as well

import { URLSearchParams } from '@angular/http';
import 'rxjs/add/operator/map'

export class QueryStringBuilder {
    static BuildParametersFromSearch<T>(obj: T): URLSearchParams {
        let params: URLSearchParams = new URLSearchParams();

        if (obj == null)
        {
            return params;
        }

        QueryStringBuilder.PopulateSearchParams(params, '', obj);

        return params;
    }

    private static PopulateArray<T>(params: URLSearchParams, prefix: string, val: Array<T>) {
        for (let index in val) {
            let key = prefix + '[' + index + ']';
            let value: any = val[index];
            QueryStringBuilder.PopulateSearchParams(params, key, value);
        }
    }

    private static PopulateObject<T>(params: URLSearchParams, prefix: string, val: T) {
        const objectKeys = Object.keys(val) as Array<keyof T>;

        if (prefix) {
            prefix = prefix + '.';
        }

        for (let objKey of objectKeys) {

            let value = val[objKey];
            let key = prefix + objKey;

            QueryStringBuilder.PopulateSearchParams(params, key, value);
        }
    }

    private static PopulateSearchParams<T>(params: URLSearchParams, key: string, value: any) {
        if (value instanceof Array) {
            QueryStringBuilder.PopulateArray(params, key, value);
        }
        else if (value instanceof Date) {
            params.set(key, value.toISOString());
        }
        else if (value instanceof Object) {
            QueryStringBuilder.PopulateObject(params, key, value);
        }
        else {
            params.set(key, value.toString());
        }
    }
}

This is working for all the complex types I've used so far.

EDIT Usage I've included all of the import statements I believe the important ones are RequestOptionsArgs, RequestOptions, but I remembered their required so just incase I've included them all.

import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptionsArgs, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { IHasId } from '../interfaces/interfaces';
import 'rxjs/add/operator/map';

import { QueryOptions, IFilterNode } from "../models/queryOptions";
import { QueryStringBuilder } from "../models/QueryStringBuilder";

import 'rxjs/add/operator/map'

@Injectable()
export class ProviderBase<T extends IHasId> {

    getList(filterParams?: IFilterNode): Observable<T[]> {
        var searchParams = QueryStringBuilder.BuildParametersFromSearch(filterParams);

        let requestArguments: RequestOptionsArgs = new RequestOptions({ search: searchParams });
        return this.http.get(`${this.apiUrl}/${this.route}`, requestArguments).map(res => <T[]>res.json());
    }
 }
Sign up to request clarification or add additional context in comments.

5 Comments

A usage example could be useful also
@RobMcCabe I'll post one in a second. Gotta find it on my github
@RobMcCabe I've updated to include an example usage, some of the Imports are required IHasId is custom for me so you can ignore that etc
If you have any troubles with it send post a comment on here and I'll try and fix any issues
Thank you very much @johnny5 worked perfectly! I added a check into the PopulateSearchParams<T>() method - if (value == null) return; as some of my properties start life as a null and I wanted to omit them from the querystring. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.