Action-packed Single Page Applications with ASP.NET MVC

In my last post we saw how easy it is to build Single Page Applications using the Navigation for ASP.NET project. The uniqueness of this approach is down to the ingenious way Hyperlinks are built. It means we can create a regular Razor page then flick a switch to turn it into an SPA.

Hyperlinks are all well and good but we also need the ability to update data. From a code-maintenance perspective we’d like to keep our Update Controller Actions separate from our Index Action. But we can’t do this by registering new routes because an SPA means a single page which in turn means a single route. Keep reading to see a couple of Attributes come to our rescue or stop reading to see a working example.

Go Where the Action is

Routes aren’t the only way to distinguish Actions. By creating an Attribute inheriting from the ActionNameSelectorAttribute we can override the IsValidName method to decide which Action to call based upon a request parameter. Then each submit button’s value signifies the Action to invoke.

public override bool IsValidName(
	ControllerContext controllerContext, 
	string actionName, 
	MethodInfo methodInfo)
{
	actionName = controllerContext.Controller
		.ValueProvider.GetValue("action").AttemptedValue;
	return actionName == methodInfo.Name;
}

When the Update Action completes we must return to Index.cshtml because that’s the Single Page in our SPA. Returning the View from our Update Action won’t do the job because we also need the ViewModel constructed by the Index Action. But our aim is to keep the Actions separate so we can’t just call out to the Index Action and, because we’re building an SPA, we can’t Redirect to it either. Have we come unstuck?

An Action in Action

Redirects aren’t the only way to move between Actions. The trick is to create a new Child Action and to treat this as the Index Action instead. So we’ll move the code from the Index Action into the Child Action and the Razor from Index.cshtml into Child.cshtml. Then we’ll change Index.cshtml to execute the Child Action.

Now when the Update Action completes we can return to Index.cshtml safe in the knowledge that our Child Action will take care of the rest. We’re not out of the woods yet though because any validation messages set by the Update Action will be lost. That’s because Child Actions don’t share their Parent’s ModelState, which is where validation messages are stored. We can put things right by decorating our ChildAction with an ActionFilter Attribute that syncs up the ModelState in its OnActionExecuting method.

public void OnActionExecuting(
	ActionExecutingContext filterContext)
{
	var parentModelState = filterContext.
		ParentActionViewContext.ViewData.ModelState;
	var modelState = filterContext.
		Controller.ViewData.ModelState;
	foreach (var item in parentModelState)
	{
		if (!modelState.ContainsKey(item.Key))
		{
			modelState.Add(item.Key, item.Value);
		}
	}
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s