Tuesday, 27 August 2013

Consuming Web API OData using $resource service of Angular JS

In one of the previous posts, we saw exposing an OData Endpoint from an ASP.NET Web API service and then we consumed it using .NET and JavaScript clients. In this post, we will continue with consuming the service using Angular JS.

$resource is a wrapper around $http service of Angular JS to interact with RESTful data services. If we use $resource, we don’t need to deal with the low level HTTP calls that we used to do with $http.

Since $resource is targeted for REST based services, it has to be configured with the service URL and a few of other optional settings to get it work. Following snippet shows syntax of a sample configuration:


$resource(url, { key:val, .. }, {
    ‘getValues’: {method: ‘GET’, params: { param: val }, … },
    …
    …
});


$resource is not a part of Angular’s core module. To use $resource in an Angular JS application, the module ngResource has to be injected into the module depending on it. To know more about $resource, visit the official documentation page on website of Angular JS.

$resource can be configured to perform all of its operations on a single URL or even the URL can be over-written in the action configuration, if required. To consume a Web API OData service, we need to use the second approach as the URL differs based on the operation we will be performing.

Following factory returns a $resource object to perform CRUD operations on the OData resource:


var app = angular.module('employeeApp', ['ngResource']);
app.factory('employeeSvc', function ($resource) {
    var odataUrl = "/odata/Employees";
    return $resource("", {},
    {
        'getAll': { method: "GET", url: odataUrl },
        'save': { method: "POST", url: odataUrl },
        'update': { method: 'PUT', params: { key: "@key" }, url: odataUrl + "(:key)" },
        'query': { method: 'GET', params: { key: "@key" }, url: odataUrl + "(:key)" },
        'remove': { method: 'DELETE', params: { key: "@key" }, url: odataUrl + "(:key)" }
     });
});


This factory can be injected inside any component and used there. Each action configured above is exposed as a method on the object returned from the factory with a dollar($) symbol prepended to each of them. Since these methods deal with AJAX, they return a $q promise.

Following controller uses the methods defined above to operate on the data service:


app.controller('EmployeeCtrl', function ($scope, employeeSvc) {

    //Getting all employees and assigning to a scope variable           
    function refreshEmployees() {
        (new employeeSvc()).$getAll()
            .then(function (data) {
                $scope.employees = data.value;
            });
    };

    //Add a new employee to the resource
    function createEmployee(emp) {
        return emp.$save();
    };

    //Modify details of an existing employee
    function editEmployee(emp) {
        return (new employeeSvc({
            "Id": emp.Id, "Name": emp.Name, "Salary": emp.Salary
        })).$update({ key: emp.Id });
    };

    //Delete an employee
    function deleteEmployee(id) {
        return (new employeeSvc()).$remove({ key: id });
    };

    //rest of the controller definition.......
});


Take a look at the refreshEmployee function created above. It calls then() on the returned object from the $getAll() method to grab the response as soon as the GET request is completed. As Web API OData sets actual data to the property value, the scope variable is assigned with this property.

Happy coding!

9 comments:

  1. What about get with filters and counts for pagination

    ReplyDelete
    Replies
    1. Check this blog post: http://sravi-kiran.blogspot.com/2013/09/ExploringWebApiODataQueryOptions.html. If you want to do this with datajs, check the documentation of datajs on codeplex.

      Delete
  2. How do authentication with resources ?

    ReplyDelete
    Replies
    1. Wander,

      I will do a blog post on that.

      Delete
  3. Is there a reason you chose to instantiate a new employeeSvc resource every time you make a request instead of reusing an existing instance?

    Example from EmployeeCtl in the article above:
    function deleteEmployee(id) {
    return (new employeeSvc()).$remove({ key: id });
    };

    ReplyDelete
  4. this article sucks, this has nothing to do with odata. Where are the odata query options for example?

    ReplyDelete
    Replies
    1. Rob, $resource is meant for interacting with REST endpoints and not OData. To use $resource with OData, you need to manually keep adding actions for each query operation that you need to perform. That is just extending the idea presented in this article.

      Delete
    2. Ravi, your article title is misleading. It implies odata specific $resource tips, but there is no odata going on here. It could reasonably be called "Consuming restful services using $resource service of Angular JS" like the thousands of other blog posts that explain the same thing.

      Delete
  5. Thank you, this was helpful. We use an oData service and I was having trouble implementing the factory.

    ReplyDelete