Building OData $filter URLs with an Angular provider

twinb picture twinb · Nov 20, 2014 · Viewed 9.6k times · Source

I have an angular provider for querying an OData service.

Now I'm trying to build a $filter function onto that provider, so I can use it throughout my app.

The problem I'm running into, and so far haven't been able to solve, is that the query part of the URL needs to start with '$filter=' which I can handle fine when there is one filter, but multiple filters are added as ' and {query goes here}'.

A sample query would be:

http://www.example.com/odata/Restaurants/?$filter=id eq 2 and city eq 'Los Angeles' and substringof("Lagasse", chef)&$top=20

I'm sending all the filters in an array to the provider. Ideally, I would use "$filter=#{firstFilter}" for the first filter in the array and for the remaining filters use " and #{remainingFilter}" but I'm unsure how to structure this code.

My current code uses multiple if statements to check if a filter is there, but with the nature of building the url, it makes one of the filters mandatory at all times. I'd like to avoid this.

For example:

var filter = "$filter=id eq 2";
if (city) {
  filter += " and city eq #{cityName}";
}
if (chef) {
  filter += " and substringof(#{chefName}, chef)";
}

Now everytime a user inputs a query, they have to specify an id.

We are NOT using BreezeJS, JayData, or any other library. Strictly AngularJS and specifically $http, NOT $resource.

Answer

Bohdan Lyzanets picture Bohdan Lyzanets · Mar 21, 2016

You can use odata-filter-builder to build $filter part for OData URL query options.

Then just use $http with config params.

Short example:

var filter = ODataFilterBuilder()
      .eq('id', 2)
      .eq('city', 'Los Angeles')
      .and('substringof("Lagasse", chef)')
      .toString();

$http
  .get(resourceUrl, {params: {$filter: filter, $top: 20}})
  .then(function(response) {
    // Handle response
  })

Full example:

angular
  .module('OData', [])
  .constant('ODataFilterBuilder', ODataFilterBuilder)
  .factory('ODataService', function($http) {
    return {
      load: function(resourceUrl, queryParams) {
        return $http.get(resourceUrl, {params: queryParams})
          .then(function(response) {
            return response.data.value;
          });
      }
    }
  });

angular
  .module('app', ['OData'])
  .controller('appController', function($http, ODataService, ODataFilterBuilder, $httpParamSerializer) {

    // 1. inject ODataFilterBuilder
    // use short name for filter builder
    var f = ODataFilterBuilder;

    // 2. build filter
    var filter = f()
      .eq('id', 2)
      .eq('city', 'Los Angeles')
      .and('substringof("Lagasse", chef)')

    // 3. creater odata query params
    var queryParams = {
      $filter: filter.toString(),
      $top: 20
      // TODO: add other params
    };

    // 4. prepare odata resourse URL
    var odataServiceUrl = 'http://path/to/odata/service/';
    var odataResourseUrl = odataServiceUrl + 'entity';

    // 5. Do http request with odataResourseUrl and queryParams
    // use ODataService or angular $http service

    // ODataService
    //  .load(odataResourseUrl, queryParams)
    //  .then(function(value) {
    //    // handle value
    //  });

    // OR

    // $http.get(odataResourseUrl, {params: queryParams})
    //  .then(function(respons) {
    //    // handle respons.data.value
    //  });


    // Result examles:

    // NOTE: $httpParamSerializer - default $http params serializer that converts objects to strings
    var queryParamsSerialised = $httpParamSerializer(queryParams);

    // put values to 'this' to use it in html
    this.queryParams = queryParams;
    this.queryParamsSerialised = queryParamsSerialised;
    this.queryUrl = odataResourseUrl + '?' + queryParamsSerialised;
  });
<div ng-app="app" ng-controller="appController as vm">
  <pre>queryParams: {{vm.queryParams|json}}</pre>
  <pre>queryParamsSerialised: {{vm.queryParamsSerialised}}</pre>
  <pre>queryUrl: {{vm.queryUrl}}</pre>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script src="https://npmcdn.com/odata-filter-builder@^0.1/dist/odata-filter-builder.js"></script>