Tuesday, December 10, 2013

A little JQuery <-> Json <-> Mvc

It has been quite awhile since I've dealt with complex model binding across a set of inputs in mvc.

If one controller action's params (or a property on one of those params) is an IEnumerable<T> then Mvc will not write the element names in a way that will properly go back to the server.

Short and sweet (and probably hacky way) to do it:

I Json serialized the parent model into an html attribute on to some parent of the value(s) I want to submit. Some questions allow many answers, and I want to save each time a question answer is changed.

I'm working on making that serialization not contain the about to be consumed IEnumerable<T>property.
var save = function (element, value, event, previousValue,debug) {
    console.log('saving:' + value + ' to ' + saveUrl);

    
    var demoText = $(element).closest('[data-demographic]').attr('data-demographic');
    var demoJson = htmlDecode(demoText);
    var demoModel = JSON.parse(demoJson);
    var oldAnswers = demoModel.PossibleAnswers;
    demoModel.PossibleAnswers = [];
    //TODO: handle multi-select, single-select radio, etc...
    if (Object.prototype.toString.call(value) === '[object Array]') {
        //value and previousValue could be arrays for multi-select answers
        $.each(value, function (i, e) {
            demoModel.PossibleAnswers.push({ Id: e });
        });
    } else {
        demoModel.PossibleAnswers.push({ Id: value,Text:$(element).parent().text().trim()  });
    }
    var data = JSON.stringify(demoModel);

    data=data.replace('][', '].[');
    $.ajax({
        dataType: 'json',
        url: saveUrl,
        contentType: 'application/json; charset=utf-8',
        accept: debug ? {
            json: 'application/json',
        } : {},
        type: 'POST', data: data,
        success: function (data, status, jqXhr) {
            if (debug) {
                var answerMirror = data.PossibleAnswers;
                delete data.PossibleAnswers;
                console.log(data);
                console.log(answerMirror);
            } else { //update dom to new read-only model?
                //TODO: adjust with new html
                console.log(data);
            }
            
        }
    });
};
There's the heart of it. Notice I don't actually have to use the special name=foo.bar[0].Id on my inputs, but that is probably still a good idea. This thing was almost working without doing any JSON.stringify but the IEnumerable<T> data was ignored by Mvc's model binder. With that in mind, I had to specify the contentType of the ajax request.

Wednesday, December 4, 2013

Inline markup delegates and razor helper delegate wars

So I had some markup that was being duplicated all over the place in MVC.

This is mostly boilerplate bootstrap paneling, with masonry on top.

How would you provide a generic razor to meet bootstrap/masonry without creating a bunch of classes or partials to account for special case(s)?  How in razor would you write a method that can take inline markup (for example any @<text> call)?

 One way is @helper Take note that HelperResult's documentation says specifically it is not intended to be used directly in your code. So I headed in down the path of inline razor helper delegates: