In my last blog I introduced the basics of using ASP.NET AJAX and WebAPI together in the same project.  In addition, I showed how to bind results from WebAPI to a Telerik’s ASP.NET AJAX Grid. In this blog I will continue down this path and implement insert, update, and delete operations using WebAPI and RadGrid!

Note: While this post focuses on using WebAPI in conjunction with RadGrid, most of the client code can be used when working with RadGrid in client mode.

Set It Up!

To get started an additional reference needs to be added to the project:

  • System.Net.Http.Formatting

Adding this reference allows us to use HttpResponseMessage<T>, which will be used to return data to the client. 

Now on to the fun stuff!

Let There Be Customers!

Customers can’t be updated or deleted if they don’t exist; so I will start off by building a way to create customers. 

Creating customers requires:

A Post action method in the controller to create a customer.

A button in the UI to create a new customer.

A form to enter customer information.

A way to submit the new customer’s information to the server.

A way to refresh the grid to show the new customer.

 

Starting with step one, I am going to create the Post method in the CustomerController as shown here: 

public HttpResponseMessage Post(Customer newCustomer)
{
    repository.Add(newCustomer);
    repository.Save();
 
    var response = new HttpResponseMessage<Customer>(newCustomer, HttpStatusCode.Created);
    response.Headers.Location = new Uri(Request.RequestUri, "/api/customers/" + newCustomer.Id.ToString());
 return response;
}

This method adds a new customer to the repository, saves the customer, and then returns a new HttpResponseMessage<Customer>.  The response message passes the created customer in its body, and returns a status code of 201 – Created. The 201 status code indicates to the client that the new resource was actually created.  We will look at how to handle that client side in a moment.

Before we can focus on client code, the UI needs to be setup.  The first thing to do is add a new command item in the grid header. This will be where a user clicks to add a new customer:

<CommandItemTemplate>
 <button onclick="addCustomer(); return false;">Add New Customer</button>
</CommandItemTemplate>

Now I need a form for users to enter the new customer information.  In this example I use Telerik’s ASP.NET AJAX Window control to create a modal dialog, and add a form inside the dialog:

<telerik:RadWindow runat="server" ID="CustomerEditWindow" Modal="true">
 <ContentTemplate>
 <form id="customerInfoForm">
 <p>
                Enter Customer Information:
 </p>
 <asp:HiddenField ID="customerId" runat="server" />
 <div>
 <asp:TextBox ID="customerFirstName" runat="server" /></div>
 <div>
 <asp:TextBox ID="customerLastName" runat="server" /></div>
 <div>
<asp:Button ID="btnCancelCustomer" Text="Cancel" CssClass="secondaryAction" runat="server" OnClientClick="closeCustomerEditor(); return false;" />
 <asp:Button ID="btnSaveCustomer" Text="Save" runat="server" CssClass="primaryAction" OnClientClick="saveCustomer(); return false;" />
 </div>
 </form>
 </ContentTemplate>
</telerik:RadWindow>

I went ahead and added an extra hidden field to store the customer id.  This will be used later when implementing updates.  I also created two buttons, one to submit the form, and one to cancel customer creation. 

The last step to implementing customer creation is wiring everything up with JavaScript. When the Add Customer button in the grid header is pressed, an addCustomer JavaScript function is called. This JavaScript function simply shows the editor form, as shown here:

function addCustomer() {
   $find("<%=CustomerEditWindow.ClientID %>").show();
}

Once you click the cancel button, you must clear the form and close the window. Use the closeCustomerEditor JavaScript function:

function closeCustomerEditor() {
 //close dialog
    $find("<%=CustomerEditWindow.ClientID %>").close();
 
 //reset form
    $get("<%= customerFirstName.ClientID %>").value = "";
    $get("<%= customerLastName.ClientID %>").value = "";
    $get("<%= customerId.ClientID %>").value = "";
}

Pressing submit calls the saveCustomer JavaScript function. In the saveCustomer function, a client-side customer object is created using the values from the form, then the createResource function is called passing the customer object and a callback function.

function saveCustomer() {
 var customer = {
        FirstName: $get("<%= customerFirstName.ClientID %>").value,
        LastName: $get("<%= customerLastName.ClientID %>").value,
        Id: $get("<%= customerId.ClientID %>").value
                    };
 
    closeCustomerEditor();
    createResource(customer, refreshGrid);   
}

The createResource method sends an AJAX POST containing the new customer’s information to the Post action created earlier. If everything goes as expected, the server will return a 204 Created result, and the callback function will be executed.

function createResource(model, success) {
    $.ajax({
        url: baseUrl,
        data: model,
        type: "POST",
        statusCode: {
            201: function (newResource) {
 if (success) {
                    success(newResource);
                }
            }
        }
    });
}

At this point, you can create, and its time to implement editing!  

Oh No, I Miss Spelled His Name!

At this point we can move on to editing a customer.  Updating a customer is very similar to creating a new one. The main differences are:

  • The form needs to be pre-populated with the edited customers information.
  • When sending the updated information to the server the PUT verb should be used.

Let’s dig in!

The first thing needed here is a way to retrieve a customer by id from the server.  For this I will create a new action method on the CustomerController named Get, and it will take a single parameter named id. The Get method will attempt to retrieve a customer based on the specified id, and if one is found, the server will return a new HttpResponseMessage<Customer> with a status code of 200 - OK. If no customer is found, it should throw a new HttpResponseException, and use the 404 – Not Found status.

public HttpResponseMessage Get(int id)
{
  HttpResponseMessage response;
 
  var customer = repository.GetById(id);
 
 if (customer == null)
  {
 throw new HttpResponseException("Invalid Customer", HttpStatusCode.NotFound);
  }
 else
  {
      response = new HttpResponseMessage<Customer>(customer, HttpStatusCode.OK);
  }
 
 return response;
}

Next, create an action method in the CustomerController that will handle update operations for the customer resource. The Put method retrieves the customer by its id, updates it and returns a HttpResponseMessage with a 200 – OK status code, if it was successful:

public HttpResponseMessage Put(Customer updatedCustomer)
{
    HttpResponseMessage response;
    var customer = repository.GetById(updatedCustomer.Id);
 
 if (customer == null)
    {
 throw new HttpResponseException("Invalid Customer", HttpStatusCode.NotFound);
    }
 else
    {
        customer.FirstName = updatedCustomer.FirstName;
        customer.LastName = updatedCustomer.LastName;
 
        repository.Save();
 
        response = new HttpResponseMessage(HttpStatusCode.OK);
    }
 
 return response;
}

Note 1: By convention, WebAPI will automatically use a method that starts with Put to handle requests using the PUT verb. If you want to use a different method, simply add the [HttpPut] attribute to the method.

Note 2: If you want to add validation (everyone should!) please see the validation section below.

At this point the client side needs to be wired up.

First, add a new column to the grid, then add a client-side handler for grid commands.

Since the goal is to edit a customer, add a column of type GridEditCommandColumn:

<telerik:GridEditCommandColumn ButtonType="ImageButton" ItemStyle-Width="20"/>

Next, configure the grid’s client events, and add a client-side handler for OnCommand:

<ClientSettings ClientEvents-OnCommand="handleGridCommand">
 <DataBinding Location="~/api" ResponseType="JSON">
 <DataService TableName="Customers" Type="OData" />
 </DataBinding>
 <Scrolling AllowScroll="True" UseStaticHeaders="True" SaveScrollPosition="True" />
</ClientSettings>

Create a client method to handle the grid commands. The handleGridCommand function inspects the command, and if it is an edit command, the default grid action is cancelled, and the editCustomer client function is called:

function handleGridCommand(sender, args) {
 var command = args.get_commandName().toLowerCase();
 
 if (command == "edit") {
 
 //get the id for the data key for the command's row
 var customerId = getKeyValueByRowIndex(args.get_tableView(), args.get_commandArgument());
       editCustomer(customerId);
 
       args.set_cancel(true);
   }
}

The editCustomer function, queries the service for the customer by its id, sets the form’s input values and shows the edit customer modal dialog using a few additional methods:

function editCustomer(id) {
    getResource(id, loadEditScreen)
}
 
function loadEditScreen(resource) {
    $get("<%= customerFirstName.ClientID %>").value = resource.FirstName;
    $get("<%= customerLastName.ClientID %>").value = resource.LastName;
    $get("<%= customerId.ClientID %>").value = resource.Id;
    $find("<%=CustomerEditWindow.ClientID %>").show();
}
 
function getResource(id, success) {
    $.ajax({
        url: baseUrl + '/' + id,
        type: "GET",
        dataType: 'JSON',
        statusCode: {
            200: function (resource) {
 if (success) {
                    success(resource);
                }
            }
        }
    });
}

At this point you should see the modal dialog, populated with the customer’s information. After editing, click the save button in the dialog, and the saveCustomer JavaScript method is called just like when creating a customer; however, the method needs to be tweaked slightly to handle updating customers:

function saveCustomer() {
 var customer = {
        FirstName: $get("<%= customerFirstName.ClientID %>").value,
        LastName: $get("<%= customerLastName.ClientID %>").value,
        Id: $get("<%= customerId.ClientID %>").value
                    };
 
    closeCustomerEditor();
 
 if (customer.Id == '' || customer.Id == 0) {
        createResource(customer, refreshGrid);
    }
 else {
        updateResource(customer, refreshGrid);
    }
}

When saveCustomer is called, the updateResource method will be called when saving an existing customer.

The updateResource method sends an AJAX request using the PUT Http verb. Using the PUT verb directs the controller to the Put action in the CustomersController. Once again, if everything goes as planned, the server returns a status code of 200 – OK, and the grid is refreshed:

function updateResource(model, success) {
  $.ajax({
      url: baseUrl + '/' + model.Id,
      data: model,
      type: "PUT",
      dataType:'JSON',
      statusCode: {
          200: function (resource) {
 if (success) {
                  success(resource);
              }
          }
      }
  });
}

Now we can fix any typo in a customer’s name, but what about when we need to remove a customer?

Deleting Customers

The final step in this journey is adding the ability to delete customers. Configuring deletes is very similar to the previous sections. 

In this case I need to add the following:

  • A server side action on the controller to handle the delete.
  • A new column in the grid to display the delete button.
  • Some JavaScript to handle the client side delete command.
  • A grid refresh once the customer is deleted.

Once again, I will start off with step one and setup the server side code first! In the CustomersController I created a Delete action method. The Delete method attempts to retrieve a customer by the specified id, and if a valid customer is found, the server returns a 204 – No Content status. If the specified customer was not found, we will throw a new HttpResponseException, set a message and use the 404 – Not Found status.

public HttpResponseMessage Delete(int id)
{
   HttpResponseMessage response;
 
   var customer = repository.GetById(id);
 
 if (customer == null)
   {
 throw new HttpResponseException("Invalid Customer", HttpStatusCode.NotFound);
   }
 else
   {
       repository.Remove(customer);
       repository.Save();
       response = new HttpResponseMessage(HttpStatusCode.NoContent);
   }
 
 return response;
}

 

Now we need to configure the UI. First, add a new column to the grid, except this time, add a GridClientDeleteColumn:

<telerik:GridClientDeleteColumn ButtonType="ImageButton" ItemStyle-Width="20"/>

Now I need to update the handleGridCommand to make it handle the delete command, as well:

function handleGridCommand(sender, args) {
 var command = args.get_commandName().toLowerCase();
 
 if (command == "delete" || command == "edit") {
 
 //get the id for the data key for the command's row
 var customerId = getKeyValueByRowIndex(args.get_tableView(), args.get_commandArgument());
 
 if (command == "delete") {
           deleteResource(customerId, refreshGrid);
       }
 else {
           editCustomer(customerId);
       }
 
       args.set_cancel(true);
   }
}

Check to see if the command is the delete command, and if it is, cancel the default grid command and call the deleteResource JavaScript method. The deleteResource method send an AJAX request to the server using the Http Delete verb. If the server returns a status code of 204, we know the delete was successful, and call the success callback function (if it was passed in).

function deleteResource(id, success, fail) {
    $.ajax({
        url: baseUrl + '/' + id,
        type: "DELETE",
        statusCode: {
            204: function () {
 if (success) {
                    success();
                }
            }
        }
    });
}

At this point, delete operations should be working.

In Action

Now for the fun part: seeing if it all works. Go ahead and run the application, and you should see the grid populated with some customers (I like this store apparently):

image

Now open your favorite developer tools, create/update/delete some customers and inspect the network traffic:

image

As you can see highlighted in red, everything is working as expected.

Validating the Request

In this blog, I didn't cover validating the data model other than checking to see if valid ids were sent to the server. That said, the way the controller and client code are configured, it's rather easy to return different status codes and include the validation errors in the response. This will allow you to leverage data annotations applied to your objects. 

As an example, if I applied an attribute to the Customer entity and the data sent to the server was invalid, I could return ModelValidation errors like this:

if (!ModelState.IsValid)
{
 return new HttpResponseMessage<JsonValue>(ModelState.ToJson(), HttpStatusCode.BadRequest);
}

The ToJson method serializes the model errors to JSON, and the controller returns a new HttpResponseMessage<JsonValue> with a status code of 400 – Bad Request.

ToJson Extension method:

public static class ModalStateHelpers
{
 public static JsonArray ToJson(this ModelStateDictionary modalState)
    {
        var errors = new JsonArray();
 foreach (var prop in modalState.Values)
        {
 if (prop.Errors.Any())
            {
                errors.Add(prop.Errors.First().ErrorMessage);
            }
        }
 
 return errors;
    }
}

The client can then handle this status code and show the errors to these errors to the user:

function showErrors(errors) {
    var strErrors = "";
 if ($.isArray(errors)) {
        $.each(errors, function (index, err) {
            strErrors += "*" + err + "\n";
        });
    }
 else {
        strErrors = errors;
    }
    alert(strErrors);
};

While I didn’t go into validation in depth in this article, the sample code does have data attributes applied to the Customer object, and validation is handled; so make sure to check it out.

That's a Wrap!

Wow, this blog came out quite a bit longer than I expected! That said, hopefully it will help you get started using ASP.NET AJAX, and WebAPI!

Download Source

Note that the WebAPI is subject to change and you may need to tweak the code. To run the sample, restore the packages and add a reference to the Telerik.Web.UI.dll assembly.

Happy coding!

Download the RadControls trial today!


About the Author

Iana Tsolova

is Product Manager at Telerik’s DevTools division. She joined the company back in the beginning of 2008 as a Support Officer and has since occupied various positions at Telerik, including Senior Support Officer, Team Lead at one of the ASP.NET AJAX teams and Technical Support Director. Iana’s main interests are web development, reading articles related to geography, wild nature and latest renewable energy technologies.

Related Posts

Comments