2

I work on a ASP.NET MVC project that needs to be able to filter data in an extensible way. I've decided to use a class to represent a single filter criterion (I am aware that it could be a Dictionary at this point, but I might need to add other properties later):

public class FilterCriterion
{
    public string FilterName { get; set; }

    public string FilterValue { get; set; }
}

And this is the controller method signature I had in mind that will return filtered data:

public JsonResult GetMultipleShowDetailsByFilter(IEnumerable<FilterCriterion> filters, int pageNumber, int itemsPerPage)

This method will be called from the web page (using JavaScript) and will return a JSON containing a set of entities that will be then displayed on the page.

I assume I will need some kind of converter that will accept the arguments in a raw state and create and enumerable object implementing IEnumerable<FilterCriterion>. Maybe I could use a ModelBinder, but I am not really sure how.

Can you nudge me in the right direction? Thanks.

EDIT: I not pass an array as suggested. When I step into the controller, there is a correct number of items inside filters, but their properties (both FilterName and FilterValue) are null. Below I post the complete code as well as the contents of filter object on the client side. What am I doing wrong? Javscript code:

function filterChanged() {
    var activeFilters = $(':checked');
    var filters = new Array();
    $.each(activeFilters, function (i, val) {
        var newItem = new Object();
        newItem.FilterName = $(val).attr('data-filter-type');
        newItem.FilterValue = $(val).attr('data-filter-value');
        filters[i] = newItem;
    });

    $.getJSON('../DatabaseApi/GetMultipleShowDetailsByFilter',
        {
            'filters': filters,
            'pageNumber': 1,
            'itemsPerPage': 10
        },
        function(data) {
            fill(data);
        });
}

How do the created object look (proof that there is data, taken from VS Immediate Window):

?filters
[[object Object],[object Object]]
    [0]: {...}
    [1]: {...}
    [prototype]: []
?filters[0]
{...}
    [prototype]: {...}
    FilterName: "Genre"
    FilterValue: "Animation"
11
  • 1
    if you are already using the class FilterCriterion why not add int pageNumber, int itemsPerPage as a variable in a class it would be a lot easier for you to parse it later Commented Jul 28, 2012 at 19:52
  • Good idea, plus it will simplify my method signature, which is always a good thing :-) Commented Jul 28, 2012 at 19:56
  • @COLDTOLD in fact, that's bad idea. There may be several FilterCriterions, and how should code decide which pageNumber to use? Commented Jul 28, 2012 at 20:12
  • @Serg Rogovtsevthe page number is part of the object and will stay or change based on filter but passing an array is a really bad idea in this specific situation since array maintenance will a lot harder Commented Jul 29, 2012 at 1:30
  • @COLDTOLD part of which object? And what "array maintenance" will be a lot harder? Commented Jul 29, 2012 at 6:53

2 Answers 2

3
  1. Change your controller signature to

    public JsonResult GetMultipleShowDetailsByFilter(FilterCriterion[] filters, int pageNumber, int itemsPerPage)
    
  2. use following POST structure:

    filters[0].FilterName = 'aaa'
    filters[0].FilterValue = 'bbbb'
    filters[1].FilterName = 'ccc'
    filters[1].FilterValue = 'ddd'
    ...
    filters[n].FilterName = 'xxx'
    filters[n].FilterValue = 'yyy'
    

    (you can easily do that with jQuery)

One caveat: your index numbering should be contiguous, i.e. have no gaps.

Update to topic starter's update: you have to be very careful with POST structure. Nested JavaScript objects won't do. Try using this one:

function filterChanged() {
    var activeFilters = $(':checked');
    var data = {
        'pageNumber': 1,
        'itemsPerPage': 10
    }
    $.each(activeFilters, function (i, val) {
        data['filters[' + i + '].FilterName'] = $(val).attr('data-filter-type');
        data['filters[' + i + '].FilterValue'] = $(val).attr('data-filter-value');
    });

    $.getJSON('../DatabaseApi/GetMultipleShowDetailsByFilter',
        data,
        function(data) {
            fill(data);
        });
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, could you take look at my modified question and tell me what am I doing wrong?
Your POST structure is wrong. It has to be exactly as specified. I'll try to write an example in update to my post.
Thanks, this works. Could you maybe include some link or brief explanation of why the structure has to be precisely like this? (I'm not an experienced JavaScript dev and the structure of 'data' seems a bit bizarre to me :-) )
FYI I've tried it and IEnumerable<FilterCriterion> in the signature works fine (since arrays in .NET implement IEnumerable).
hanselman.com/blog/… It has nothing to do with JavaScript, it is MVC's default model binder requirement.
2

see this link, here you can see the plugin ToDictionary works great and solves these problems, allowing us to have the code much cleaner:

JS:

function filterChanged() {

    var filters = [];
    $(':checked').each(function (i, el) {
        filters.push({
            'FilterName': $(this).data('filter-type'),
            'FilterValue': $(this).data('filter-value')
        });
    });

    var data = $.toDictionary({
        'filters': filters,
        'pageNumber': 1,
        'itemsPerPage': 10
    });

    var response = function (data) {
        fill(data);
    };

    $.getJSON('../DatabaseApi/GetMultipleShowDetailsByFilter', data, response);
}

based on the response: link

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.