views:

210

answers:

3

I'm tryping to use JSON to update records in a database without a postback and I'm having trouble implementing it. This is my first time doing this so I would appreciate being pointed in the right direction.

(Explanation, irrelevant to my question: I am displaying a list of items that are sortable using a jquery plugin. The text of the items can be edited too. When people click submit I want their records to be updated. Functionality will be very similar to this.).

This javascript function creates an array of the objects. I just don't know what to do with them afterwards. It is called by the button's onClick event.

 function SaveLinks() {
     var list = document.getElementById('sortable1');
     var links = [];

     for (var i = 0; i < list.childNodes.length; i++) {
         var link = {};
         link.id = list.childNodes[i].childNodes[0].innerText;
         link.title = list.childNodes[i].childNodes[1].innerText;
         link.description = list.childNodes[i].childNodes[2].innerText;
         link.url = list.childNodes[i].childNodes[3].innerText;

         links.push(link);
     }

     //This is where I don't know what to do with my array.         
 }

I am trying to get this to call an update method that will persist the information to the database. Here is my codebehind function that will be called from the javascript.

    public void SaveList(object o )
    {
        //cast and process, I assume
    }

Any help is appreciated!

A: 

You don't use CodeBehind for this, you use a new action.

Your action will take an argument which can be materialized from your posted data (which, in your case, is a JavaScript object, not JSON). So you'll need a type like:

public class Link
{
    public int? Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string Url { get; set; }
}

Note the nullable int. If you have non-nullable types in your edit models, binding will fail if the user does not submit a value for that property. Using nullable types allows you to detect the null in your controller and give the user an informative message instead of just returning null for the whole model.

Now you add an action:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DoStuff(IEnumerable<Link> saveList)
{
    Repository.SaveLinks(saveList);
    return Json(true);
}

Change your JS object to a form that MVC's DefaultModelBinder will understand:

 var links = {};
 for (var i = 0; i < list.childNodes.length; i++) {
     links["id[" + i + "]"] = list.childNodes[i].childNodes[0].innerText;
     links["title[" + i + "]"] = list.childNodes[i].childNodes[1].innerText;
     links["description[" + i + "]"] = list.childNodes[i].childNodes[2].innerText;
     links["url[" + i + "]"] = list.childNodes[i].childNodes[3].innerText;
 }

Finally, call the action in your JS:

//This is where I don't know what to do with my array.  Now you do!
// presumes jQuery -- this is much easier with jQuery
$.post("/path/to/DoStuff", links, function() { 
    // success!
    },
    'json');
Craig Stuntz
Will that work for a nested array of links?
friedo
Sure, up to the limits of the `DefaultModelBinder`. You can use a custom model binder if your posted data gets hairy.
Craig Stuntz
Note that `DefaultModelBinder` is finicky about lists. It wants to see form keys like `Id[0]`, `Id[1]`, etc. If you're seeing `saveList` come through as null, check the posted form using Firebug or Fiddler. You might need to change the JavaScript field names to the format I show (with the square brackets and index starting from 0).
Craig Stuntz
A: 

Unfortunately, JavaScript does not have a built-in function for serializing a structure to JSON. So if you want to POST some JSON in an Ajax query, you'll either have to munge the string yourself or use a third-party serializer. (jQuery has a a plugin or two that does it, for example.)

That said, you usually don't need to send JSON to the HTTP server to process it. You can simply use an Ajax POST request and encode the form the usual way (application/x-www-form-urlencoded).

You can't send structured data like nested arrays this way, but you might be able to get away with naming the fields in your links structure with a counter. (links.id_1, links.id_2, etc.)

If you do that, then with something like jQuery it's as simple as

jQuery.post( '/foo/yourapp', links, function() { alert 'posted stuff' } );

Then you would have to restructure the data on the server side.

friedo
+2  A: 

I have recently done this. I'm using MVC though it shouldn't be too different.

It's not vital but I find it helpful to create the contracts in JS on the client side and in C# on the server side so you can be sure of your interface.

Here's a bit of sample Javascript (with the jQuery library):

var item = new Item();
item.id = 1;
item.name = 2;
$.post("Item/Save", $.toJSON(item), function(data, testStatus) {
  /*User can be notified that the item was saved successfully*/
  window.location.reload();
}, "text");

In the above case I am expecting text back from the server but this can be XML, HTML or more JSON.

The server code is something like this:

public ActionResult Save()
{
    string json = Request.Form[0];

    var serializer = new DataContractJsonSerializer(typeof(JsonItem));
    var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(json));
    JsonItem item = (JsonItem)serializer.ReadObject(memoryStream);
    memoryStream.Close();

    SaveItem(item);

    return Content("success");
}

Hope this makes sense.

Chris Simpson
He's also using MVC.
Craig Stuntz
Yeah just read that sorry. Must be the holiday season or something! :)
Chris Simpson
Might be worth adding that the "toJSON" function comes from a jquery plugin by mark gibson
Chris Simpson
Thanks Chris, that's exactly what I'm looking for. bonus question - Do you know how to get the new order of the elements?
splatto
not entirely sure what you mean as you are setting up your array yourself but presumably you are talking about the order they are now stored in the database? You don't just have to return a "success" string in your content, this can be more json or a delimeted string, so you could send back the information this way if you want.
Chris Simpson
what I meant was - the link class has a "displayorder" property on which instances are sorted for display. the point of this page is to be able to edit the link title, url, and display order, the latter which is done by clicking and dragging. I'm just having trouble now with the sort order being correct, each one is getting the value 1 for some reason.
splatto
you know what..nevermind. I refactored a bit and got it to work another way.
splatto