MVC – Updating multiple partial views from a single (AJAX) action

Sometimes you need to make an AJAX call from your MVC view to perform some server side actions on user changes. You may then want to return some calculated data in a partial view so that you can update the view as presented to the user.

What if you have modified data in more than one partial view?

An application I worked on required some server side calculations that updated both data in a table (one partial view) and summary data displayed in the footer of the page (a second partial view). I would not want to make two separate service side calls to update the two partial views, so I looked into a mechanism to allow me to return the two partial views in the response from the server side call.

I realised that if I could package the two partial view responses into a single object, I could pass this to the client in the response, then use my client side script (JQuery in my case) to unpack the responses and update the two partial views.

The tricky bit was how to package up the partial views. Google pointed to this method to render a Razor view into a string(obtained from ):

public static String RenderRazorViewToString(ControllerContext controllerContext, String viewName, Object model)
{
  controllerContext.Controller.ViewData.Model = model;

  using (var sw = new StringWriter())
  {
    var ViewResult = ViewEngines.Engines.FindPartialView(controllerContext, viewName);
    var ViewContext = new ViewContext(controllerContext, ViewResult.View, controllerContext.Controller.ViewData, controllerContext.Controller.TempData, sw);
    ViewResult.View.Render(ViewContext, sw);
    ViewResult.ViewEngine.ReleaseView(controllerContext, ViewResult.View);
    return sw.GetStringBuilder().ToString();
  }
}

Using this method, I was able to create a controller action that returned a JSON object containing the rendering for my two partial views:

[HttpPost]
public ActionResult _CalculateValues(MyViewModel model)
{
  ModelState.Clear();
  calculationService.CalculateValues(model, User.Identity.Name);
  // The total values and summary values are displayed in two partial views
  // We can't normally return two partial views from an action, but we don't want to have another server
  // call to get the second one, so we render the two partial views into HTML strings and package them into an
  // an anonymous object, which we then serialize into a JSON object for sending to the client
  // the client side script will then load these two partial views into the relevant page elements
  var totalValuesPartialView = RenderRazorViewToString(this.ControllerContext, "_TotalValues", model);
  var summaryValuesPartialView = RenderRazorViewToString(this.ControllerContext, "_SummaryValues", model);
  var json = Json(new { totalValuesPartialView, summaryValuesPartialView });
  return json;
}

Note that my two partial views (“_TotalValues.cshtml” and “_SummaryValues.cshtml”) both use the same model in my example as the view that they are displayed on (MyModelView). I don’t believe this would be a necessary restriction though.

In my view I render the two partial views using mark up similar to:

<div id="_TotalValues" data-url='@Url.Action("_CalculateValues", "Improvement")'>
 @{
   Html.RenderPartial("_TotalValues", Model);
 }
</div>

and

<div id="_SummaryValues">
 @{
   Html.RenderPartial("_SummaryValues", Model);
 }
</div>

Finally, I have a JScript method to make the AJAX call on some event (such as the user changing a value or clicking a button – whatever event the method is bound to) and update the partial views with the response:

// Make an ajax call to recalculate the total values and summary values
function calculateTotalAndSummaryValues() {
  // Get the controlller action url 
  var url = $("#_TotalValues").data('url');
  var data = $("form").serialize();

  // Post the current contents of the form, so we show the Year 1 to 5 benefit values (which might be unsaved)
  $.post(url, data, function (response) {
    // This post will return a JSON object with two properties called totalValuesPartialView and summaryValuesPartialView
    // these will contain the rendering for the two partial views _TotalValues and _SummaryValues
    // by packaging these two up, we can update two partial views from this ajax post
    // with one single server method call
    $("#_TotalValues").html(response.totalValuesPartialView);
    $("#_SummaryValues").html(response.summaryValuesPartialView);
    });
}

This is a very simplistic implementation, with no error handling, just to show the technique.

The key features are:

  • Use a helper method to render a razor partial view into a string
  • Package the partial views into a JSON object
  • Use JScript / JQuery to unpack the AJAX call response and update HTML elements (DIVs) containing the partial views
Advertisement

Use JQuery to automatically update edited data on other parts of a form

If you have a form that has multiple tabs, or other display sections, you might have a need to display data that is entered on one part of the form on other parts. For example the first tab could allow the user to enter a name, this name may then be displayed as read only on other tabs.

There are various methods to do this, but if the read only display is positioned in different parts of the form and in more than one place, you might want to have separate elements for each display.

This little bit of jQuery will allow any changes to the data to be automatically copied to all of the relevant read only display elements.

There are three steps to implement this functionality:

  1. Include the following jQuery in your form (e.g. via a js file)
$(document).ready(function () {
  // On change event for any element with the hasDisplayElement 
  // class
  $(".hasDisplayElement").change(function () {
    updateElementDisplay($(this));
  });

  // Initialise the display element values
  $(".hasDisplayElement").each(function () {
    updateElementDisplay($(this));
  });
});

// Any element with a hasDisplayElement class will check on change
// for other elements with the classes isElementDisplay id, where 
// id is the id of the triggering element (the one with the 
// hasDisplayElement class) if using @HtmlTextFor, etc. the id will 
// be the same as the model property name e.g.
//  @Html.TextAreaFor(model =&gt; model.Description, 
//    new { rows = "8", @class = "form-control asDisplayElement"})
//
// changes will update value in any element like this:
// <textarea class="form-control isDisplayElement Description" 
//    rows="8" disabled="true">@Model.Description</textarea>

function updateElementDisplay(element) {
  var id = element.attr("id");
  var displayElements = $(".isDisplayElement." + id)

  if (element.is("select")) {
    var value = element.children("option:selected").text();
    displayElements.val(value);
  }
  else
    displayElements.val(element.val());
}
  1. Add the “hasDisplayElement” class to all editors that will need to update corresponding display elements elsewhere on the form. Also make sure that all the editor elements have an id.
  2. Add the class “isDisplayElement” followed by the id of the relevant editor element to all of the display elements. By including the id of the editor as a class on the display element, the jQuery script will be able to find the correct display elements whenever a edited value is changed.

Example:

In a MVC project, I needed to displayed the selected Start Date on other parts of the form. The editor for the start date was defined as:

@Html.TextBoxFor(model => model.StartDate, new { @class = "hasDisplayElement" })

MVC will automatically give the text box element an Id of “StartDate”

Elsewhere on the form, where I needed to display the specified start date, I used:

<input type="text" class="isDisplayElement BenefitStartDate" disabled="disabled" />

N.B. The updateElementDisplay method uses the jQuery val() method to update the display elements. Therefore, you need to ensure that any elements that have the “isDisplayElement” class support val() (such as Input elements). If you want to use other elements, such as div, you will need to modify the updateElementDisplay method to detect these and use text() instead.

Adding support for a single connection string with multiple Entity Framework models using Dependency Injection

Recently I was working on a project that used more than one Entity Framework model to access different sections of the same database. The Entity Framework models were created using Model first to create edmx model files. The problem with this approach is that the connection string are unique to each model, as they contain model meta-data. This means that we have the same database connection string in several places.

Using our IoC container, we can create a dependency that will inject a single database connection string into each database context, which means that we can have a single database connection stored in our config file (web.config or app.config).

Creating a Entity Framework connection string

An Entity Framework connection string may be created in code using the EntityConnectionStringBuilder class (located in the System.Data.Entity.Core.EntityClient namespace). We will be using Castle Windsor to inject this connection string into our DbContext objects, so we should create an interface in a common project (i.e. a project that all the Entity Framework models will be able to access). We will call this interface IEntityFrameworkConnectionBuilder

    /// <summary>
    /// This interface allows the connection for an entity framework context to be determined from the model name. This will allow different EF models to use a single connection string
    /// </summary>
    public interface IEntityFrameworkConnectionBuilder
    {
        /// <summary>
        /// Create a EF connection string from the mode name
        /// </summary>
        /// <param name="modelName"></param>
        /// <returns></returns>
        string createEntityFrameworkConnection(string modelName);
    }

We can create the implementation of this interface in a project that has references to the EntityFramework libraries (installed by NuGet). The EntityFrameworkConnectionBuilder class is shown below:

using System;
using System.Collections.Generic;
using System.Linq;

using System.Data.Entity.Core.EntityClient;

namespace EF.Demo.Common
{
    /// <summary>
    /// This allows the connection for an entity framework context to be determined from the model name. This will allow different EF models to use a single connection string
    /// </summary>
    public class EntityFrameworkConnectionBuilder : IEntityFrameworkConnectionBuilder
    {

        public string _providerName;
        public string _dbConnectionstring;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="providerName"></param>
        /// <param name="dbConnectionstring"></param>
        public EntityFrameworkConnectionBuilder(string providerName, string dbConnectionstring)
        {
            _providerName = providerName;
            _dbConnectionstring = dbConnectionstring;
        }


        public string createEntityFrameworkConnection(string modelName)
        {
            var entityConnectionStringBuilder = new EntityConnectionStringBuilder();
            entityConnectionStringBuilder.Provider = _providerName;
            entityConnectionStringBuilder.ProviderConnectionString = _dbConnectionstring;
            entityConnectionStringBuilder.Metadata = string.Format(@"res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl", modelName);

            return entityConnectionStringBuilder.ToString();
        }
    }
}

As the provider and database connection string will be the same for all Entity Framework models, we can pass these into the EntityFrameworkConnectionBuilder as constructor parameters, which will be set by the IoC container.

The implementation of the createEntityFrameworkConnection method uses the EntityConnectionStringBuilder to build the correct entity framework connection string based on the supplied model name and the injected database provider and connection string.

Injecting the IEntityFrameworkConnectionBuilder into the Entity Framework context

We will use our IoC container to inject the IEntityFrameworkConnectionBuilder object into our Entity framework DB context classes. As the context classes are automatically generated by the Entity Framework model, we do not really want to manually alter these, as any changes would be lost if the model was regenerated. We can use partial classes to get around this problem.

When you created the model, you would have specified the EntityContainerName property. A class that inherits from DbContext will have been created in the model as the custom context for your model. In the same namespace, we can create a new class with this same name, and mark it as partial so that it will be merged with the generated class when compiled.

For example, if we created the Entity framework model with the EntityContainerName “MyModel”, we can create a MyModel partial class:

using EF.Demo.Common;

namespace EF.Demo.Common.Domain.Models
{
    /// <summary>
    /// A DB Context for the data model
    /// </summary>
    /// <remarks>
    /// This partial class allows a specific constructor to be specified for use with the IoC container
    /// As it is a partial file, the auto generated context may be re-generated without breaking this IoC specific bit
    /// </remarks>
    public partial class MyModel
    {
        /// <summary>
        /// Construct db context with the injected IEntityFrameworkConnectionBuilder and model name
        /// </summary>
        /// <param name="entityFrameworkConnectionBuilder"></param>
        /// <param name="modelName"></param>
        /// <remarks>
        /// This constructor is intended for use with an IoC container (e.g. Castle Windsor) to inject the properties
        /// and generate the correct connection string, using IEntityFrameworkConnectionBuilder
        /// </remarks>
        public MyModel(IEntityFrameworkConnectionBuilder entityFrameworkConnectionBuilder, string modelName)
            : base(entityFrameworkConnectionBuilder.createEntityFrameworkConnection(modelName))
        {
            
        }
    }
}

This class contains a constructor for the context that receives an instance of the IEntityFrameworkConnectionBuilder interface and the relevant model name. This constructor will be called by the IoC container, and the parameters will be resolved automatically (this will be described later when we configure the IoC container).

The connection string for the context is passed to the base DbContext by calling the base constructor and passing in the connection string generated from the IEntityFrameworkConnectionBuilder.createEntityFrameworkConnection(modelName) method.

Creating DbContext instances

Typically the Db Context objects are created with a using statement, to ensure that they are dispose of correctly when finished with.

using (var dbContext = new Models.OnlineFinances_Common())
{

}

However, we need a mechanism to create our context objects by resolving them through our IoC container so that the IEntityFrameworkConnectionBuilder object and model name may be passed to it, via the new constructor that we created. As we are going to use castle Windsor, we can make use of the Typed Factory Facility.

In order to create a typed factory, we need to create an interface with a Create<> and a Release<> method:

using System;
using System.Data.Entity;

namespace EF.Demo.Common
{
    /// <summary>
    /// A factory for creating and removing data base contexts
    /// </summary>
    /// <remarks>
    /// The purpose of this factory is to allow us to create dbContext instances 
    /// within code (probably in a using statement so it is disposed of correctly)
    /// but allow any dependencies in the context class to be resolved. The main dependency is
    /// the IEntityFrameworkConnectionBuilder, which will be added to a partial class for each specific
    /// DbContext based class. This interface will allow use to specify a single DB connection
    /// string for multiple Entity Framework models
    /// 
    /// This factory interface is not actually implemented anywhere. Instead
    /// we use Castle Windsor's Typed Factory Facility to auto create the abstract factory
    /// see http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx   
    /// and http://docs.castleproject.org/Windsor.Typed-Factory-Facility-interface-based-factories.ashx
    /// </remarks>
    public interface IEntityFrameworkContextFactory
    {
        T Create<T>()
            where T : DbContext;

        void Release<T>(T context)
            where T : DbContext;

    }
}

Wherever we are making use of the DbContext (e.g. a repository, a unit of work, or some other pattern), we can inject the IEntityFrameworkContextFactory and then use this to create our DbContext objects.

    public class MyRepository : IMyRepository
    {
        #region IoC injected dependencies

        private IEntityFrameworkContextFactory _entityFrameworkContextFactory;

        public MyRepository(IEntityFrameworkContextFactory entityFrameworkContextFactory)
        {
            if (entityFrameworkContextFactory == null)
                throw new ArgumentNullException("entityFrameworkContextFactory");

            _entityFrameworkContextFactory = entityFrameworkContextFactory;
        }

        #endregion

        public List<Models.SomeModel> getAllSomeModels()
        {
            // Use the IEntityFrameworkContextFactory to create the context. This will allow the IoC container to inject dependencies, such as the single connection string
            using (var dbContext = _entityFrameworkContextFactory.Create<Models.MyModel>())
            {
                return dbContext.SomeModel.ToList();
            }
        }
    }
}

Note that we are not calling the Release method from the factory, as in this case the context objects will be created with a transient lifecycle, which I believe means that they will be released when the using statement completes.

Castle Windsor Configuration

The final step is to register our various interfaces and classes with Castle Windsor.

In our root project, we can create an installer. The mechanism to run this installer will depend on the type of project, but for example in an ASP.NET MVC project, it would be installed from the Application_Start event in Global.asax.

using Castle.Windsor;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Facilities.TypedFactory;

using EF.Demo.Common;
using EF.Demo.Common.Domain;
using EF.Demo.Common.Domain.Models;


namespace EF.Demo.Web
{
    public class EntityFrameworkInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            // Register the IEntityFrameworkConnectionBuilder implementation
            // with the OnlineFinances db connection string
            container.Register(Component.For<IEntityFrameworkConnectionBuilder>()
                .ImplementedBy<EntityFrameworkConnectionBuilder>()
                .DependsOn(new
                    {
                        providerName = "System.Data.SqlClient",
                        dbConnectionString = System.Configuration.ConfigurationManager
                            .ConnectionStrings["MyConnectionString"]
                            .ConnectionString
                    })
                .LifestyleSingleton()
                );

            // Register the IEntityFrameworkContextFactory as a Typed Factory
            container.AddFacility<TypedFactoryFacility>()
                .Register(
                    Component.For<IEntityFrameworkContextFactory>()
                    .AsFactory());

            // Register the MyModel Entity framework context, with the correct model name
            container.Register(Component.For<MyModel>()
                .DependsOn(new
                    {
                        modelName = "Models.MyModel"
                    })
                .LifestyleTransient()
                );

        }
    }
}

In this installer, we are first registering the IEntityFrameworkConnectionBuilder interface with its implementation, and retrieving the database connection string from the configuration with the name MyConnectionString. We are also passing the provider type, which in this case is System.Data.SqlClient.

Next we register the IEntityFrameworkContextFactory as a Typed Factory.

Finally, we register all of the Entity Framework context objects. It is important that the correct model name is configured here, as this will be passed to the database context. To check what the model name should be, when you created the Entity Framework model, a connection string will have been created in the configuration file, this will have the model name embedded in it.

  <connectionStrings>
	  <add name="MyModel" connectionString="metadata=res://*/Models.MyModel.csdl|res://*/Models.MyModel.ssdl|res://*/Models.MyModel.msl;provider= ..
          />
  </connectionStrings>

Using JQuery Ajax method to pass Json to a MVC3 Action and return a partial view

ASP.NET MVC3 provides out of the box support for binding a Json data object into a Model on postback. This maybe used with a JQuery Ajax function call to post selected data back to an action. It is also possible to return a partial view from the same action, and refresh this from the ajax success callback function. For example:

In my MVC3 Razor project, I create two model classes:

public class NameResponseModel
{
    public string Name { get; set; }
    public DateTime CurrentDateTime { get; set; }
    public IList<int> Numbers { get; set; }
}
public class UpdateNameModel
{
    public string Name { get; set; }
    public IEnumerable<int> Numbers { get; set; }
}

The UpdateNameModel will be used to retrieve data submitted by the Ajax method call. It is this model that the Json object will bind to. The NameResponseModel class is used to pass information back to the UI via a template view.

In the Views/Shared/DisplayTemplates folder, I created a template that is strongly typed to the NameResponseModel class, called NameResponseModel.cshtml:

@model MvcJQuery.Models.NameResponseModel

<div>
<div class="display-label">
    Name: @Html.DisplayFor(m => m.Name)
</div>
<div class="display-label">
    Current Date Time: @Html.DisplayFor(m => m.CurrentDateTime)
</div>
@{
    var numbersCount = Model.Numbers.Count;

    for(int numberIndex = 0; numberIndex < numbersCount; numberIndex++)
    {
        <div class="display-label">
            Number: @numberIndex = @Html.DisplayFor(m => m.Numbers[numberIndex])
        </div>
    }
}
</div>

This template simply displays the contents of the associated NameResponseModel object.

Next, in the Home Controller, I added the following action:

public ActionResult UpdateName(UpdateNameModel updateNameModel)
{
    return PartialView("DisplayTemplates/NameResponseModel", new NameResponseModel
    {
        Name = updateNameModel.Name,
        CurrentDateTime = DateTime.Now,
        Numbers = updateNameModel.Numbers.ToList()
    });
}

This action takes a UpdateNameModel object as a parameter and simply copies this into a new instance of the NameResponseModel. It then returns the display template as a partial view.

Finally, my Home/Index.cshtml view looks like this:

@{
    ViewBag.Title = "Home Page";
}

<script type="text/javascript">
    $(function () {
        $('#UpdateName').click(function () {
            var inputName = $('#Name').val();
            var dataResponse = { Name: inputName, Numbers: [1, 45, 67, 89] };
            $.ajax({
                type: 'POST',
                contentType: 'application/json; charset=utf-8', 
                data: JSON.stringify(dataResponse),
                dataType: 'html',
                url: '@Url.Action("UpdateName")',
                success: function(result) {
                    $('#Response').html(result);
                }
            });
        });
    });
</script>

<h2>@ViewBag.Message</h2>
<p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
</p>

<div id="Response"></div>

<input type="text" id="Name" />
<button type="button" id="UpdateName">Update</button>

I’ve added a div with the Id=”Response”. This will be used to display the partial view following an Ajax update. When the “Update” button is clicked, the click event creates a Json object with the contents of the “Name” text input and an array of integers. Note that the names of the Json items must match exactly the names within the data model that the Json will be bound to. MVC3 will quite happily handle complex data binding, so the array of integers will be bound successfully to the Numbers IEnumerable<int> parameter in the UpdateNameModel class.

Other points to note are the contentType parameter of the Ajax call is set to ‘application/json; charset=utf-8’. This indicates that the parameter passed by the Ajax call will be a Json object. The Json object itself needs to be converted to a string, JSON.stringify will perform this function for us. The dataType parameter in the Ajax call is set to ‘html’. This indicates that the data returned by the server is expected to be html (the rendered partial view in this case). The success callback then simply loads this returned html into the “Response” div.

Hope this helps somebody! It took me a while, and a lot of googling, to figure it out!