Elegant web forms: wrap up and example

To conclude the series on elegant web forms, I will walk you through a full example of a simple company and contact management tool. The app in itself doesn’t do much, but it serves to illustrate the points mentioned in the previous posts.

elegant_web_forms_example

1. Screenshot of the sample app

The example I’m using has a list of companies on the left and a list of contacts on the right, in other words, a simple master-detail view. To create, delete and associate companies and contacts I won’t be using any postbacks but will do everything with jQuery and the aforementioned AJAX-methods and web services. In the complete app, there’s not a single form.

Quick view on the solution explorer

solution_explorer_elegant_web_forms

1. App_code folder

In this folder we find the following items

- Entities (Person, Company) and their managers (CompanyManager, PersonManager).

- Extensions-module (see jQuery Templates and ASP.NET User Controls)

- Inventory: An entity-framework based Data Access Layer

- AjaxServices: Our fa├žade to the underlying domain model

2. Views folder

In this folder we store the views for a company and a person as well as the control to create both of them.

 

The scripts

Now, how would we handle this scenario in traditional web forms?

In a traditional web form application we would use server buttons for all “action” elements on the page:

- Selection button for the selected company
- Create button for a new company/person
- Delete button for a person/company

Then, on the server we would catch all the events and update the HTML accordingly and send the response back out into the wild.

Let’s first tackle the company selection pattern.

Selecting a company

In this case we will split this up into two parts:

1. What has to happen visually to the company (it has to turn green)
2. What has to happen to the rest of the page (we need to load data from the server)

The visual part we will take care of through the jQuery extensions we used in the previous samples:

jQuery.fn.selectitem = function () {
    /// <summary>Selects an item by applying a CSS-class to it.</summary>
    /// <returns>null</returns>
    this.addClass(SELECTEDCLASS);
};
jQuery.fn.deselectitem = function () {
    /// <summary>Deselects an item by removing the selected CSS-class</summary>
    /// <returns></returns>
    this.removeClass(SELECTEDCLASS);
};
jQuery.fn.mutuallyExclusive = function () {
    $(this).live("click", function () {
        $(this).siblings().deselectitem();
        $(this).selectitem();
    })
}

You can see here that I added a mutuallyExclusive method, which will deselect all other companies and select the one that was clicked. Then in CSS I can do the following:

.company.selected{
    background-color: #339933;
    }

OK, so the visual part has been taken care of, let’s take a look at what should happen to the rest of the page:

function onCompanyClicked() {
    var self = $(this);
    // Hide the people div, we're going to update it
    elements.people.slideUp(function () {
        // Collect the data from the clicked element
        var data = {                                                            
            ID: self.attr("data-id")
        };

        // Call the service
        $.callAsmx(LOADPEOPLEBYCOMPANYID, data, databind({                      
            template: elements.personTemplate,
            target: elements.people,
            // Before we bind the new list, we first clear the current items
            onbinding: function () {                                            
                elements.people.html("");
            },
            // After the bind, we slide the div down
            onbound: function () {
                elements.people.slideDown();
                // Enable the create button for a person
                elements.btnCreatePerson.removeAttr("disabled");                
            }
        }));
    });
}

In short, this is what happens:

1. We hide the div with people
2. We collect the ID by reading the attribute of the clicked element
3. We call the service to load the people for this company
4. When the service returns, before we databind, we empty the people-div
5. We bind the data to the people-div
6. We open the people-div again
7. We enable the button to create a person in this company

Because step 4,5 and 6 is a common scenario, I have refactored them into the method DataBind, which you can find in the source.

Create/delete Company/Person

The create and delete operations for companies as well as persons work in a similar way. Here’s the code to create a person for example:

function onBtnCreatePersonClicked() {
    // Obtain the Person-object and wrap it in a data-object
    var data = { Person: _getPerson() }
    // Call our service and specify how to handle the results
    $.callAsmx(CREATEPERSON, data, databind({                               
                                        template: elements.personTemplate,
                                        target: elements.people
                                        }));
}

 

This method is even simpler:

1. First we construct a person from the values in the DOM (refactored into the method _getPerson)
2. We call the method on the server and databind the results to the DOM

We can do the same thing for the other methods. For more details you can check out the attached source code.

Conclusion

So as you can see, the flow is still the same as with traditional web forms:

event => access object model and underlying data source => update layout accordingly.

The difference is that we don’t send all our data over the wire, but limit the transfer to just necessary items and we don’t mix UI code with business logic. (In this example there’s no business logic, but it’s obvious that it should go into the Person and Company classes.)

All in all, my opinion is that, whether you’re using ASP.NET Web Forms, MVC, plain old JavaScript and HTML or whatever other language, it’s not the tool which determines how elegant your code is, it’s the developer who writes it. It’s certain that ASP.NET MVC will encourage you from the start to write better code whereas ASP.NET Web Forms is aimed at RAD and as such will encourage you to do things the “quick” way, without thinking about consequences.

However, if you stand still for a moment, you will see that it still offers you the possibility to do things right and it allows you to create clean, fast and loosely coupled web apps, just as MVC does.

With the advent of .NET 4.5 which has a lot of good stuff coming for Web Forms as well as for MVC, it should even get easier to create slick apps for the web.

I hope you enjoyed the series and picked up some hints along the way. If you have any questions, feel free to contact me either through the comments section here or via the contact form.

Stay tuned for more examples in ASP.NET with jQuery!

Comments are closed.