Hugoware

The product of a web developer with a little too much caffeine

Posts Tagged ‘Web Programming

Undo, Redo and Whatever… From A Web Application

with 2 comments

Long ago a brilliant developer invented the Undo command — probably one of the most important features to be added in computing history. It seems funny to me that today, in 2009, it is still a very rare feature to find on a website. This isn’t to say no one is doing it but more that it isn’t a common feature to find.

Using Lambdas For Undo Actions

In .NET you can use Lambdas to create a function and pass it around like an argument and then that argument can then be invoked at a later time in a different place. That said, with a little careful planning and a few correctly stored Lambdas, you could have a working ‘Undo’ model in your ASP.NET web sites For this example I’m going to use MVC but this would really work anywhere.

Warning: This is very crude code, more of a proof of concept, so you probably don’t want to use it in your projects as is.

Let’s start with an abstract controller that contains our Undo handling code.

[UndoController.cs]

using System;
using System.Web;
using System.Web.Mvc;

namespace UndoAction {

	//class that supports an undo action
	public class UndoController : Controller {

		#region Constants

		private const string SESSION_UNDO_ACTION = "Session:UndoAction";
		private const string SESSION_UNDO_MESSAGE = "Session:UndoMessage";
		private const string TEMP_DATA_UNDO_MESSAGE = "UndoResult";
		private const string DEFAULT_UNDO_MESSAGE = "Previous action was undone!";
		private const string DEFAULT_MISSING_UNDO_MESSAGE = "No actions to undo!";

		#endregion

		#region Undo Action Container (Session)

		//contains the current undo action
		private static Action _UndoAction {
			get {
				return System.Web.HttpContext.Current
					.Session[SESSION_UNDO_ACTION] as Action ;
			}
			set {
				System.Web.HttpContext.Current
					.Session[SESSION_UNDO_ACTION] = value;
			}
		}

		//the message to return in the view data for this action
		private static string _UndoMessage {
			get {
				return System.Web.HttpContext.Current
					.Session[SESSION_UNDO_MESSAGE] as string ;
			}
			set {
				System.Web.HttpContext.Current
					.Session[SESSION_UNDO_MESSAGE] = value;
			}
		}

		#endregion

		#region Setting Undo Actions

		/// <summary>
		/// Applies an undo action
		/// </summary>
		protected void SetUndo(Action action) {
			this.SetUndo(DEFAULT_UNDO_MESSAGE, action);
		}

		/// <summary>
		/// Applies an undo action with a return message for the user
		/// </summary>
		protected void SetUndo(string message, Action action) {
			UndoController._UndoAction = action;
			UndoController._UndoMessage = message;
		}

		/// <summary>
		/// Performs the undo action (if any) and saves the message
		/// </summary>
		protected void PerformUndo() {

			//check if there is an action
			if (UndoController._UndoAction is Action) {

				//perform the action and save the message
				Action action = UndoController._UndoAction;
				action();
				this.TempData[TEMP_DATA_UNDO_MESSAGE] =
					UndoController._UndoMessage;

				//and clear out the previous information
				UndoController._UndoAction = null;
				UndoController._UndoMessage = null;

			}
			//just save a generic message
			else {
				this.TempData[TEMP_DATA_UNDO_MESSAGE] =
					DEFAULT_MISSING_UNDO_MESSAGE;
			}

		}

		#endregion

	}

}

Basically, we create an abstract Controller that allows us to set an Undo action using a Lambda. You can also set a message for to return to the user that sumarizes what the action had done. This example uses a couple Session variables to hold the parts of the undo action, except I recommend that you create an actual class to house all of the information.

Next, let’s look at how we would actually use this controller.

[HomeController.cs]

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Linq;

namespace UndoAction {

	//example of using undo actions
	public class HomeController : UndoController {

		#region Constants

		private const string SESSION_LIST_CONTAINER = "Session:ListContainer";

		#endregion

		#region Properties

		//a simple list of items for the list
		public static List<string> ListContainer {
			get {
				List<string> container = System.Web.HttpContext.Current.Session[SESSION_LIST_CONTAINER] as List<string>;
				if (container == null) {
					container = new List<string>();
					System.Web.HttpContext.Current.Session[SESSION_LIST_CONTAINER] = container;
				}
				return container;
			}
		}

		#endregion

		#region Actions

		//shows the list of items
		public ActionResult Index() {
			return this.View(HomeController.ListContainer.OrderBy(item =&gt; item.ToLower()));
		}

		//adds an item to the list
		public ActionResult Add(string phrase) {

			//format the value
			phrase = (phrase ?? string.Empty).Trim();

			//add the item if it isn't there yet
			if (!HomeController.ListContainer.Any(item => item.Equals(phrase, StringComparison.OrdinalIgnoreCase)) &amp;&amp;
			    !string.IsNullOrEmpty(phrase)) {
				HomeController.ListContainer.Add(phrase);
			}

			//return to the list view
			return this.RedirectToAction("Index");

		}

		//removes an item from the list
		public ActionResult Delete(string phrase) {

			//make sure the item even exists first
			if (HomeController.ListContainer.Any(item => item.Equals(phrase, StringComparison.OrdinalIgnoreCase))) {

				//since it exists, save the logging message
				this.SetUndo(
					string.Format("Restored '{0}' to the list!", phrase),
					() => {
						HomeController.ListContainer.Add(phrase);
					});

				//and then actually remove it
				HomeController.ListContainer.Remove(phrase);

			}

			//return to the main page
			return this.RedirectToAction("Index");

		}

		//tries to undo the previous action
		public ActionResult Undo() {

			//attempts to undo the previous action (if any)
			this.PerformUndo();

			//return to the main page
			return this.RedirectToAction("Index");
		}

		#endregion

	}

}

This controller allows the user to manage a simple shopping list with an Undo action to allow them to restore a deleted item. It is some ugly code, but you get the idea on how it works.

The important thing you should note is that the Lambda doesn’t refer to the instance of the class (this) but instead that the list is a static property. This is important to keep in mind as you develop something like this. When you create the Lambda action, you can’t refer to the instance that it was created in. Instead, you need to work with static properties.

Finally, let’s look at the ViewPage that is used in this example.

[Index.aspx]

<%@ Page Language="C#" 
	Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="System.Collections.Generic" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>Shopping List</title>
	</head>
	<body>
		<% IEnumerable<string> list = this.Model as IEnumerable<string>; %>
		
		<h2>Shopping List</h2>
		
		<p><% =this.Html.ActionLink("Undo Previous Action", "Undo") %></p>
		
		<% if (this.TempData["UndoResult"] is string) { %>
		<p><em><% =this.TempData["UndoResult"] as string %></em></p>
		<% } %>
		
		<hr />
		
		<table>
		<% foreach(string item in list) { %>
			<tr>
				<td><% =this.Html.Encode(item) %></td>
				<td><% =this.Html.ActionLink("Remove", "Delete", new { phrase = item }) %></td>
			</tr>
		<% } %>
		</table>
		
		<hr />
		
		<form action="<% =this.Url.Action("Add") %>" method="post" >
			<strong>Add an item:</strong>
			<input type="text" name="phrase" />
			<input type="submit" value="Add" />
		</form>
		
	</body>
</html>

The ViewPage itself is nothing fancy — If you’re working in a project you should create a strongly-typed view but in this example we just read the information we need and go on.

Using The Page

Below are a few screen shots of what happens as the user makes changes to the page. This example starts with a list of a few different items.

After removing an item from the list (the ‘Orange’) pressing the ‘Undo Previous Action’ button will restore the item. The custom message that was added is displayed (from the TempData field).

If you press the ‘Undo’ button again there aren’t any actions waiting anymore and the default message is displayed instead (no actions waiting).

Things To Consider

This code is just a sample of how you could implement and Undo function into your web applications. Here are a few things that you might want to keep in mind before starting something like this.

  • Make sure that your Undo command doesn’t cause you to duplicate code. In this example, the undo command uses a different ‘Add’ approach than the actual ‘Add’ action on the controller. This could result in duplicated code or incorrect results.
  • Determine if you can queue up ‘Undo’ commands. This example can only do one action at a time, but you could potentially create a Stack of actions that are executed in the reverse order that they were added.
  • Could you use the same concept to ‘Redo’ a command after it has been undone?
  • Be careful with your context – Don’t use ‘this’ inside of your Undo Lambdas.

Anyways, just an interesting concept to share. This approach requires a lot more thought but could pay off in the end.

Do you have a web application that could benefit from an ‘Undo’ command?

Advertisements

Written by hugoware

December 21, 2009 at 12:03 am

Combining WebServices with MVC

with 7 comments

I always liked the concept behind WebServices. Having a single place to store a bunch of complex but commonly used functions is a great way to decrease complexity of other programs that all sit on the same network. If you’re like me and tend to do a lot of intranet applications, a web service can prevent a lot of duplicate code.

My only real problem with WebServices was having to use SOAP. Adding a Web Reference to a project wasn’t that big of a deal but if you ever wanted to just call a function real quick, say from a script file (yes, I do VBScript occasionally… ick) then it isn’t quite as I’d prefer. You end up spending more time making sure your XML is well formed and less time on the logic inside your quick script.

MVC helps get around that problem by allowing you to perform normal HTTP calls and plug all your arguments into the query string or the body of the request – something much easier to do. The problem, however, is that you end up losing the convenience of using a WebService with other applications.

A Simple Solution

The idea here is to create a controller that acts as a wrapper to a WebService. By doing this we can override a few methods on our Controller that use reflection to invoke the matching method on the WebService. Also, because the Controller is still a unique class, we can still attach any number of Actions to it as we normally would. Below is some code that I wrote the other day. It isn’t battle tested so if you use it be sure to verify it does everything that you need it to do.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Mvc;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml.Linq;
using System.IO;

namespace Interface.Controllers {

    /// <summary>
    /// Abstract wrapper that handles calling WebService methods on behalf of a controller
    /// </summary
    public abstract class WebServiceControllerWrapper<T> : Controller where T : WebService {

        #region Constructors

        /// <summary>
        /// Creates a new Controller Wrapper for a WebService
        /// </summary>
        public WebServiceControllerWrapper() {
            this.Service = Activator.CreateInstance<T>();
        }

        #endregion

        #region Properties

        //The service that is being used for this call
        private T Service { get; set; }

        #endregion

        #region Overriding Methods

        //finds the correct method for the WebService method
        protected override void HandleUnknownAction(string actionName) {

            //find if the method exists
            MethodInfo method = this.Service
                .GetType()
                .GetMethods()
                .Where(found =>
                    found.IsPublic &&
                    found.Name.Equals(actionName, StringComparison.OrdinalIgnoreCase) &&
                    found.GetCustomAttributes(typeof(WebMethodAttribute), true).Count() > 0
                    )
                    .FirstOrDefault();

            //if no method was found, just give up
            if (method == null) { return; }

            //check if all the arguments were found
            List<object> arguments = new List<object>();
            ParameterInfo[] parameters = method.GetParameters();
            foreach (ParameterInfo param in parameters) {

                //check if this argument was found
                object arg = this.ValueProvider[param.Name].ConvertTo(param.ParameterType);
                arguments.Add(arg);
            }

            //with the arguments try and call the result
            object result = method.Invoke(this.Service, arguments.ToArray());

            //if there is a return value, serialize it and write it
            this.Response.ContentType = "text/xml";
            if (method.ReturnType != null) {

                //if this is an XElement of some kind, just use it as is
                if (result is XObject) {

                    //write the string
                    using (StreamWriter writer = new StreamWriter(this.Response.OutputStream)) {
                        writer.Write(result.ToString());
                    }

                }
                else {

                    //try and serialize it
                    XmlSerializer serialize = new XmlSerializer(result.GetType());
                    serialize.Serialize(this.Response.OutputStream, result);
                }

            }

        }

        #endregion

    }

}

The idea here is to inherit this class instead of the standard Controller class and provide the name of the WebService we want to wrap around as our Generic argument. For example…


//that's about it - actions are mapped automatically to the correct method on the webservice
public class AccountController : WebServiceControllerWrapper<AccountWebService> { }

By doing this, when our controller receives an Action, is checks the WebService instance for the same method and then tries to call the method with the arguments it finds as part of the request!

And that’s it! A quick and simple way to map incoming actions to a matching method on a Web Service!

The Return Type

I’m not sure the best way to handle the return type at this time. It seems to me that the XmlSerializer should be sufficient for object with the exception of Xml which should probably just be written out as a string. If you have a suggestion on a better way to respond to incoming requests, please let me know. 🙂

Remember: This is just some quick and dirty code – This needs some more polish and exception handling before I’d use it in a real project, but at least it might help you get going in the right direction.

Written by hugoware

September 27, 2009 at 9:51 pm

Using WebControls In ASP.NET MVC Views – Part 2

with 5 comments

Check out my newest blog post about using WebControls inside of MVC (source code included)🙂

WebControls In MVC Series

In this post we continue the investigation if we can put WebControls inline with a MVC ViewPage. If you read my previous post on this topic, you’ll remember we got close, but couldn’t quite get it to work as expected.

You might be thinking “you shouldn’t use WebControls in Views!!” and you are absolutely right! I have two reasons for doing this project.

  1. A lot of investment has been put into creating WebControls. Some developers may not want to switch to MVC because they don’t want to lose everything they have already put together. Having this option available might help move them in that direction.
  2. I wanted to see if it was even possible to do!

I don’t really recommend mixing the two worlds together that much, but this might be handy for someone at some point somewhere (somehow…)

This is a series of posts to investigate if it is possible to use WebControls inline with MVC. The code in this post is not a final version and shouldn’t be used. Additionally, at the time of writing this I already have the code done for part 3 so I know the code gets better, but I still need do part 2 before I move to part 3. Hang in there! 🙂

It’s Just HTML

One thing I’ve kept repeating to myself while working on this is ‘it’s just HTML’ — If you think about it, we only need to get rendered content and post it back to the server in the same way that a normal WebForms page would do it. However, the ASP.net page lifecycle is not just HTML, in fact, there is a lot that goes into it.

If you remember, in the last attempt we we’re able to hook up events to our WebControls and then render them inline with the rest of our MVC content. We then used the BuildManager class to load an instance of a page and then process the request — but only to be met with a strange error message about our ViewState.

While I never did entirely figure out what the specific problem was I did suspect it had something to do that the ViewPage was some how having a problem with the ViewState that we were posting back that was actually rendered by the dynamically loaded page. If it doesn’t make sense, don’t worry — I’m still guessing here. 🙂

In any case, it appears that we were still on the right track. After a little more hacking I came up with the following code.

//snip...
/// <summary>
/// Class to allow WebForm Controls to be used inline with MVC
/// </summary>
public static class WebFormExtensionMethods {

	/// <summary>
	/// Executes a delegate to generate the control to render onto the page
	/// </summary>
	public static void RenderWebFormControl(this ViewPage page, Func<Control> gen) {
		WebFormExtensionMethods.RenderWebFormControl(page, gen());
	}

	/// <summary>
	/// Renders a WebControl onto the page that supports postbacks
	/// </summary>
	public static void RenderWebFormControl(this ViewPage page, Control control) {

		//the name of the loader container and the viewstate hack
		string path = "~/ViewPageWithWebForm.aspx";
		string mvcPrefix = "MVCSTATE";

		//load the container and add the control
		WebControlOutputPage template = (WebControlOutputPage)
			BuildManager.CreateInstanceFromVirtualPath(path, typeof(WebControlOutputPage));

		//add the control to the page
		template.Init += (s, e) => {
			template.Form.Controls.Add(control);
		};

		//capture the output to format
		template.Rendered += (output) => {

			//change the names of the viewstate items to prevent
			//postback errors
			XDocument doc = XDocument.Parse(output);
			foreach (var item in doc.Descendants()
				.Where(o => o.Attribute("name") is XAttribute &&
					o.Attribute("name").Value.StartsWith("__"))) {

				//change the state name
				item.Attribute("name").Value = string.Concat(
					mvcPrefix,
					item.Attribute("name").Value
					);
			}

			//display each of the elements inline
			foreach (XElement show in doc.XPathSelectElement("html/body/form").Elements()) {
				page.Response.Write(show.ToString());
			}

		};

		//generate a query string of all the requested items
		//there is probably a much better way to do this
		StringBuilder query = new StringBuilder();
		foreach (var item in page.Request.Params.AllKeys) {
			string name = item.Replace(mvcPrefix, string.Empty);
			query.Append(name + "=" + page.Server.UrlEncode(page.Request[item]));
			if (!page.Request.Params.AllKeys.Last().Equals(item)) { query.Append("&"); }
		}

		//create a new HttpRequest to process the request with
		HttpRequest req = new HttpRequest(
			path,
			page.Request.Url.AbsoluteUri,
			query.ToString()
			);

		//process the container page
		((IHttpHandler)template).ProcessRequest(
			new HttpContext(req, new HttpResponse(new StringWriter()))
			);
	}
}

/// <summary>
/// Page rendering class for WebControl output
/// </summary>
public class WebControlOutputPage : Page {

    //notify the calling parent the HTML is ready
    public event Action<string> Rendered;

    //render the output to work with
    protected override void Render(HtmlTextWriter writer) {
        using (StringWriter output = new StringWriter()) {
            using (HtmlTextWriter html = new HtmlTextWriter(output)) {
                base.Render(html);
                this.Rendered(output.ToString());
            }
        }
    }
}

You will also need an .ASPX page to load your controls with. Make sure it inherits from the WebControlOutputPage class above. Below is an example that will work.

<%@ Page Language="C#" Inherits="WebControlOutputPage" %>
<html>
    <head runat="server" />
    <body>
        <form id="pf" runat="server" />
    </body>
</html>

Since this code isn’t the final version I won’t explain too much of it and instead focus on the relevant parts.

A ViewState By Any Other Name Is… Uh…

I personally found HTML to be a little strange when it came to the name and id attributes on certain elements, it seemed to me that those were redundant, but hardly a real issue. In this instance, having the two attributes works to our advantage.

You’ll notice that in our code we append and remove a prefix to the names of the state containers, for example the __VIEWSTATE and __EVENTSTATE, on our page. By doing this, the id remains the same so Javascript can continue to access it but the name changes to avoid being caught as an error in the ViewPage.

You’ll also notice that we create a new HttpRequest and rename the state values to their original name. This allows any state information to slip past the ViewPage, but then be used by the WebControl Form. There is more than likely a better way to do that part, but for now it works — at least for this purpose.

So How About An Example?

The helper methods have changed up a bit to allow the control to be created by a delegate inline. This way it is a little easier to assign event handlers to your controls before they are rendered. Let’s put a simple button on the page.

<html>
    <body>
        <h2>WebControl Inline With MVC</h2>
        <form method="post" >

        <% this.RenderWebFormControl(() => {

               //create the button
               Button test = new Button() {
                   ID = "myButton"
               };

               //assign an event
               test.Click += (s, e) => {
                   test.Text += test.Text.Length.ToString();
               };               

               //return the control so it can be used
               return test;

           }); %>

        </form>
    </body>
</html>

By using a delegate we’re given a chance to assign a click handler to append some text onto our WebControl. After it is returned in the delegate it is rendered to the page. After we wrap the control with a form and then run it check out what we get.

postback-success

Success!! Our postback went through and our control is being updated as we wanted — and all inline with our MVC code!!

Not Quite There…

This code works pretty well, but there is a couple problems already. First, we can only have one control per page because each control outputs its own ViewState — so having a Button talk to a TextBox isn’t going work right now. Additionally, code that uses the __doPostback method is going to fail because that method isn’t added to the page yet.

Good news is that we solve those problems in the next post in this series! Hang in there and check back soon!

Written by hugoware

August 7, 2009 at 1:10 am

Testing Your Javascript

with 4 comments

I’ve noticed that there has been a lot of talk lately about TDD, or Test Driven Development. Personally, I see it useful in certain areas and not so much in others. But, one thing that I’ve found TDD really great for is Javascript libraries, or really any dynamic language for that matter.

Dynamic languages have a chance of being altered by anything at anytime. It makes them particularly difficult to debug. Have you ever tried this before?

window.alert = document.body;
window.alert.innerHTML = "Dude, what?";

Yep, believe it or not, that code actually runs — and works as advertised!

So you’re working on a large Javascript library, how can you test? There is plenty of libraries out there that you can download and use, but for fun we’re going to write our own.

What Makes A Test?

It’s a good question! Basically you take some code from your library, check the results of a method or series of methods and then return back if the test succeeded or not.

That’s an overly simplistic view of a test – but it’s sufficient for this example. Here is some code I wrote for my jLinq tests.

setTimeout(function() {
(function(tests) {
    var self = this;
    self.index = 0;
    self.errors = [];
    self.target = document.getElementById("results");

    this.assert = function(ok, msg) {
        if (ok) { return; }                    
        self.errors.push(msg);
    };

    var test = function() {
	
        //reset
        self.errors = [];
        
        //try the method
        try {
			//document.body.innerHTML += "<hr/>" + tests[self.index].name;
            this.current = tests[self.index].method;
            this.current();
        }
        catch (e) {
            self.errors.push("Exception: " + e);
        }
        
        //if not okay, display the errors
        var result = ["<div class='result result-"&#93;;
        if (self.errors.length > 0) {
            result.push("error' >");
            result.push("<div class='result-title'>#" + (self.index + 1) + ": " + tests[self.index].name  + "</div>");
            result.push("<div class='result-errors' >" + self.errors.join("<br />") + "</div>");
        }
        else {
            result.push("success' >");
            result.push("<div class='result-title'>#" + (self.index + 1) + ": " + tests[self.index].name  + "</div>");
        }
        result.push("</div>");
        self.target.innerHTML += result.join("");
        
        
        //set the next test
        self.index++;
        if (self.index >= tests.length) { return; }
        setTimeout(test, 1);
    
    };

    //start the tests
    test();

})([
    //Actual Tests go here
    //more explained in a moment
    //.......

])}, 250);

What we have created here is a special bit of code that only I know about. I actually invented it just today which I have named a “Recursive Function”.

…oh? You mean that’s been around for awhile now already? Nevermind then… 🙂

The code basically calls the same test routine each time, but increments our test index each time. This way all of our tests are completed, but are also done with a slight pause between each to keep our browser from locking up.

Adding A Test

So how do we actually create a test. What do we need to have for this to work? Well since we’re passing in array of tests our code will look something like this

//Rest of the class
//...snip...
})([
    //Tests
    {name:"Function charAt correctly finds values at indexes",
    method:function() {
        this.assert("abcd".charAt(0) == "a", "Did not locate correct letter checking start of string");
        this.assert("abcd".charAt(3) == "d", "Did not locate correct letter checking end of string");
    }},
    {name:"Function calculateRate correctly returns percentage based on values",
    method:function() {
        this.assert(calculateRate(11000) == 0.5, "Did not calculate correct rate from values greater than 10,000");
        this.assert(calculateRate(5100) == 0.3, "Did not calculate correct rate from values greater than 5,000");
        this.assert(calculateRate(1100) == 0.1, "Did not calculate correct rate from values greater than 1,000");
        this.assert(calculateRate(100) == 0, "Did not calculate correct rate from values less than 1,000");
    }},
    {name:"Runs a function (this test just fails for this blog example)",
    method:function() {
        this.assert(someFakeFunction(), "We won't see this message");
    }}
]);

The tests above are some examples of tests you could write. I’m not an expert on this stuff by any stretch of the imagination, but these tests seem solid enough to tell us if one of our functions fail.

The last example calls a function that doesn’t exist, which in my code, passes the exception along as the error message.

So Why Does This Help

Remember the code sample at the start of the post? Where I just up and changed what window.alert does? Let’s say we had our tests and then somewhere in our code we did this…

String.prototype.charAt = function() { return "a"; }

It’s perfectly legal and it completely breaks what we expect our results to be.

Now, jLinq is 1655 lines of code right now, clearly not a behemoth of a library like jQuery, but large enough that I don’t remember what I wrote everywhere, but now that I have tests it doesn’t matter as much. I’m able to write some changes to the code, run the test and be certain that the library is still in tact. In fact, I wish I would have done it sooner.

What’s Next?

Don’t consider this to be all there is to TDD, but instead a very basic explanation of the concept of TDD. I would recommend to you that if you’re working on large project, especially dynamic languages, make sure you write tests for all of your functions. They will save you in the long run.

Written by hugoware

July 3, 2009 at 6:31 am