Question

I have a UI that looks like this:

enter image description here

I am trying to data of the newly created row to the server so that the server may save it.

I am sending data in JSON format from the client to my MVC application. Here's my ajax request:

var values = []; // an array with each item being an object/associative array

// more code to get values into variables

for (var i = 0; i < cultures.length; i++) {
  var cultureName = cultures[i];
  var valueTextBox = $(row).find(...);
  var value = $(valueTextBox).val();

  var cultureNameAndValue = { 'CultureShortName' : cultureName, 'StringValue' : value };

  values.push(cultureNameAndValue);
}

var stringTableRow = 
  { 
    'ResourceKeyId': resourceKeyId, 
    'Key': resourceKeyName, 
    'CategoryId': categoryId, 
    'CategoryName': categoryName, 
    'StringValues': values 
  };

var stringified = JSON.stringify({ StringTableRow: stringTableRow });

$.ajax('/Strings/JsonCreateNew', 
      { 
        cache: false, 
        async: false, 
        type: 'POST', 
        contentType: 'application/json; charset=UTF-8', 
        data: stringified, 
        dataType: 'json', 
        error: SaveNewResourceClientSideHandler.OnError, 
        success: SaveNewResourceClientSideHandler.OnSuccess 
      });

Here's the data it sends (as seen in Firebug):

{"StringTableRow":{"ResourceKeyId":"","Key":"Foo",
"CategoryId":"1","CategoryName":"JavaScript",
"StringValues":[
{"CultureShortName":"en-US","StringValue":"Something"},
{"CultureShortName":"fr-FR","StringValue":""}]
}}

Here's my server side code:

public ActionResult JsonCreateNew(StringTableRow row)
{
  // CreateNewStringTableRow(row);

  // return a success code, new resource key id, new category id
  // return Json(
              new { Success = true, ResourceKeyId = row.ResourceKeyId, 
                    CategoryId = row.CategoryId }, 
                JsonRequestBehavior.AllowGet);

  return new EmptyResult();
}

And here's the business object that I want my incoming POST'ed data to be bound to:

public class StringTableRow
{
    public StringTableRow()
    {
        StringValues = new List<CultureNameAndStringValue>();
    }

    public long ResourceKeyId { get; set; }

    public string Key { get; set; }

    public long CategoryId { get; set; }

    public string CategoryName { get; set; }

    public IList<CultureNameAndStringValue> StringValues { get; set; }  
}


public class CultureNameAndStringValue
{
  public string CultureShortName { get; set; }

  public string StringValue { get; set; }
}

Global.asax

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
        }
    }

Problem:

The action JsonCreateNew receives an object that is not null but has all properties uninitialized, i.e all nullable properties are null and value properties are at their default values. Therefore, effectively I get no data at all even when the client sends a perfectly valid Json string.

Do I need to do custom model binding?

Was it helpful?

Solution

Okay, I solved my problem and this might be an important insight for other programmers as well, because I learnt something while solving my own problem.

My solution: I resorted to not using JSON and instead used the default encoding that HTTP uses for encoding form posted values, and then I used something that smacks of custom model binding (without actually creating a model binder).

Explanation: Normally, when you make an ajax request and pass data from the client to any server platform, if you do not specify the encoding, i.e. the contentType parameter in the settings object of jQuery's ajax method (or if you are using any other means to make the ajax request other than jQuery, then however that thing sets the ContentType HTTP header is what you're after), HTTP encodes your posted data using its default encoding, which is much like what you post with a GET request after the query string, only it is binary encoded and not sent as a part of the URL.

If that's the case, and you're posting a collection of any type (IList, IEnumerable, ICollection, IDictionary, etc.) to ASP.NET MVC, then don't create an associative array in JavaScript to embody the collection. Don't even create an array. Don't create anything.

In fact, just pass it along inside the main big object using the convention:

data = 
{ /*scalar property */ Gar: 'har', 
  /* collection */ 'Foo[index++].Bar' : 'value1', 
  'Foo[index++].Bar' : 'value2'
}

And don't use JSON. That will solve half your problem.

On the server side, receive the posted data in a FormCollection and use its IValueProvider methods (GetValue) to dig out the data you need. You don't have to explicitly create a model binder to do it. You can do it in a private method of your controller itself.

I will improve this answer a bit later when I can find more time.

OTHER TIPS

Use Backbone or KnockoutJs for data binding.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top