The reason for Ajax Generators

time to read 21 min | 4179 words

In my last post, I show how to use the Ajax Generators, but I didn't really explain why it is good to learn yet another way to do Ajax. Here is the example that I used:

page.ReplaceHtml('paginatedGrid', {@partial : 'faq/grid' })
page.VisualEffect('Highlight', 'paginatedGrid')

This will send the following to the client:

try 
{
 Element.update("paginatedGrid","gridContent");
 new Effect.Highlight('paginatedGrid', {});
}
catch(e)
{
 alert('JS error ' + e.toString());
 alert('Generated content: \nElement.update(\"paginatedGrid\",\"gridContent\");\nnew Effect.Highlight(\'paginatedGrid\', {});\n');
}

(Replace gridContent with a lot of HTML, of course). Okay, so I get nicer error handling if I have an error (always a good thing), but I don't see the value of it. Let us consider the case where we don't have something as simple as straight rendering to the client, shall we?

A good example will be in a shopping cart, if the total price of the items reach above a certain threshold, the user should get free shipping. Now that we have a conditional, the code isn't this simple to write. Add about 15 lines of JS for each condition, and you are in a world of hurt.

This is where the generators come real handy, you aren't manipulating text, but objects, which means that you get to take advantage of advance programming language constructs like conditionals and looping. Let us see how we can implement the shopping cart functionality. We will start with the logic, which in this case is sitting in the controller (probably not a good idea for non-demo scenarios):

private string[] products = { "Milk", "Honey", "Pie" };

 

public void Index()

{

       PropertyBag["cart"] = Cart;

       PropertyBag["products"] = products;

}

 

public void AddItem(int id)

{

       PropertyBag["alreadyHadfreeShipping"] = Cart.Count > 4;

       Cart.Add(products[id]);

       PropertyBag["produdct"] = products[id];

       PropertyBag["freeShipping"] = Cart.Count > 4;

       RenderView("AddItem.brailjs");

}

 

public void ClearAllItems()

{

       PropertyBag["alreadyHadfreeShipping"] = Cart.Count > 4;

       InitCart();

       RenderView("ClearAllItems.brailjs");

}

Cart is a property that exposed an ArrayList saved to the user session. You can see that we are passing the decisions to the views, to act upon. There is a business rules that says that if you buy more than 4 items you get free shipping, and that is what is going on here. No dealing with the UI at all.

Now, let us see the views, we will start with the main one, index.brail

<?brail import Boo.Lang.Builtins ?>

<h2>Demo shopping cart:</h2>

<ul>

       <?brail for i in range(products.Length):?>

              <li>

                      ${ajax.LinkToRemote(products[i],'addItem.rails?id='+i,{})}

              </li>

       <?brail end ?>

</ul>

<div id="freeShipping" style="display: none;">

       <b>You are elegible for free shipping!</b>

</div>

<h2>Purchased Products:</h2>

<p>

       ${ajax.LinkToRemote('Clear','clearAllItems.rails',{})}

</p>

<ul id="products">

       <?brail for product in cart: ?>

              <li>${product}</li>

       <?brail end ?>

</ul>

The first line shows an interesting trick, by default Brail removes the builtin namespace, because common names such as list and date exist there. Here, I want to use the range function, so I just import it and continue as usual. Beyond that, there is nNothing particulary interesting about this view, I think.

Let us see the addItem.brailjs file first, it handles updating the page to reflect the new item in the cart:

page.InsertHtml('bottom', 'products',"<li>${produdct}</li>")

if freeShipping:

      page.Show('freeShipping')

     

      if not alreadyHadfreeShipping:     

            page.VisualEffect('Highlight','freeShipping')

      end

end

As you can see, it contains conditional logic, but this logic is strictly UI focused. If the user is applicable for free shipping show it, if this is the first time, highlight the fact that they got a free shipping, so they will notice.

I would like to see similar functionality done the other way, but I do not think that I would care to write it...

Using generators gives you code that is highly maintainable in the future, and doesn't move business logic to the UI or UI logic to the business logic like often happen (by neccesaity) using other methods.