简体   繁体   中英

Using Asp.Net C# MVC4 and Json, how can I get my chart to update in line with my paged datatable

I am using datatables and charts . I would prefer to return standard data through the model but cant for some odd reason, and the controls only seem to work with Json. Perhaps it's just my understanding. But at this point I am completely lost.

What I want to do is actually (in theory) quite simple.

Action result 1 returns a table which has paged data to a view as a Json result because that is what datatables require. This works fine.

Here are the controller actionresults for this:

public ActionResult AjaxVitalsHandler()
{
    return PartialView();
}

[HttpPost]
[AccessRequired(AccessFeature.Vitals, AccessLevel.Read)]
public ActionResult AjaxVitalsHandler(JQueryDataTableParamModel param)
{
    int start = param.iDisplayStart;
    int perPage = Math.Min(param.iDisplayLength, 100);
    int currentPage = (start / perPage) + 1;
    int id = _accountSession.CurrentUserId;

    //...Bunch of code here to do basic queries and get the data...All working fine.

    return Json(new
    {
         sEcho = param.sEcho,
         iTotalRecords = model.TotalItemCount,
         iTotalDisplayRecords = model.TotalItemCount,
         aaData = result
    });
}

Here is the corresponding partial view (This is a partial view loaded into a main view with RenderAction, nothing special):

<table class="table table-striped table-bordered table-hover" 
                       id="vitalsTable"
                       data-request-url="@Url.Action("AjaxVitalsHandler")">
    <thead>
        <tr>
            <th class="hidden-480">Date</th>
            <th class="hidden-480">Weight (kg)</th>
            <th class="hidden-480">Height (cm)</th>
            <th class="hidden-480">BMI</th>
            <th class="hidden-480">B/P</th>
        </tr>
    </thead>
</table>

Finally the corresponding DataTables code (which for some odd reason gives errors when placed in the partial view but not in the main view, (not my question but anyway):

<script>
    $(document).ready(function ()
    {
        var urlRequest = $('#vitalsTable').data("request-url");

        $('#vitalsTable').dataTable({
            "bSort": false,
            "bServerSide": true,
            "sAjaxSource": urlRequest,
            "sServerMethod": "POST",
            "bProcessing": true,
            "bFilter": false,
            "aLengthMenu": [[10], [10]],
            "aoColumns": [
                { "mDataProp": "VitalsDate" },
                { "mDataProp": "weight" },
                { "mDataProp": "height" },
                { "mDataProp": "bmi" },
                { "mDataProp": "bp" },
            ]
        });
    });
</script>

Action result 2 returns a chart which has the same data to another view as a Json result because that is what flot charts require. This works fine but only for the first page of data.

Here are the controller actionresults for this:

    public ActionResult LoadVitalsChartData2()
    {
        return PartialView();
    }

    //[HttpPost]
    [AccessRequired(AccessFeature.Vitals, AccessLevel.Read)]
    public JsonResult LoadVitalsChartData()
    {
        int id = _accountSession.CurrentUserId;


        //Bunch of code here to retrieve the data...Problem here.
        //There seems to be no way to sync the Ajax call back to that it refreshes
        //the data here too.
        //Ideally what I want is that when the page button is pressed, it reloads this
        //chart too and passes to it the relevant columns of data for plotting.
        //Presently I can only get it to do the first page.
        //The queries work, but how do I pass the relevant page data from the above
        //action result so I am not just getting the first page of data every time.

        return Json(new
        {
            weightData = weightResult,
            xLabels = xAxisResult,
            heightData = heightResult

            //There is security issues around AllowGet inside a post method.
            //I would like to fix this but it is also not my question.
        }, JsonRequestBehavior.AllowGet);
    }

Here is the corresponding partial view (This is a partial view loaded into a main view with RenderAction, nothing special):

    <div id="VitalsChart" class="chart"
             data-request-url="@Url.Action("LoadVitalsChartData")">
    </div>

Finally the corresponding chart code this was copied and pasted from the site and only slightly modified so I put it into a separate file, if you would like to see the full code go here there is a lot of reading but I don't see anywhere to do paging:

        Charts.initCharts();

Obviously I want my chart to display the data that is currently displayed in the table. ie if my table has 100 items paged in sets of 10, then when my table is displaying items 20 to 30, my chart should show the data for those items. It only shows the data for the first items 1 to 10. The chart itself doesn't need to handle paging, it just needs the 10 items to display and to know when the table updates. This is all available from the data sent to the table and its paging event.

So how do get this out of my table and pass it to my chart.

I have tried extracting the data to an array of some sort so it can be shared. flot charts doesn't like this. I also tried extracting the data to a common action method and passing it to another actionresult but I cant figure out how to do it.

I have tried converting all the data from anonymous types to standard class types by defining the class and casting it. But I still get errors.

This has been done before. A paged table and a chart that corresponds to the data displayed in it. Why is this so difficult to do in C# MVC4.

If there was a way I could use the above with standard data instead of Json, I would be laughing cause I know the solution. In fact, I have the solution. When querying the data, wrap it in a Paging wrapper so only the data required is returned. Then do 2 queries one in each action result for the same dataset passing only the page number to the Paging wrapper. But alas, this is for standard C# and Razor with EF and Linq. My wonderful controls require Json. This should be a lot easier but I don't know what I am missing.

It is hard to post much more code on this, I have tried so many different ways that it would make the post very long. But any one in isolation or any part of one will serve only to confuse issues.

Finally: I got some clues how to do this and started making progress but the examples I am looking for on the datatables site are not good. Here is one of my many attempts so far which is not working right (it just gives me a standard datatables error, not a JavaScript error):

    $('#vitalsTable').live('page', function ()
    {
        var tbl = $('#vitalsTable').dataTable(
            {
                "aoColumnDefs": [{
                    "aTargets": [0],
                    "mData": function (source, type, val)
                    {
                        return "Hello";
                    }
                }]

            });

        alert("Data ==> " + tbl);

        Charts.initCharts();
    });

Effectively what I am looking to do, is get a column of data when the page changes and plot this data on my chart. The page contains 10 items. So I should have a column of data with 10 items in it that can be passed to my chart for plotting.

Another snippet of code suggests that I should detect the page change event like above and before it I should do this:

    var my_chart = $.plot($("#vitalsTable"), [{}]);

Then inside it I should do something like this:

    my_chart.setData([new_data_set]);
    my_chart.draw();

But again, I don't know how to make it work.

Thanks for any help.

This is really surprising for me. SO is a great resource, and I love it. But this is the first time I came across a problem that SO users never answered. Anyway, Finally after 8 days I worked out the answer to my question. So I am going to pop it here for reference for myself in the future cause I always come back here. S/O is like my new coding bible.

Essentially the controller was not the problem. Even the view was not the problem but instead the way I was doing the jQuery in the view.

I was advised to do something like this on other forums which essentially adds a new event handler to my data tables.

$('#vitalsTable').live('page', function ()
{
    var tbl = $('#vitalsTable').dataTable(
        {
            "aoColumnDefs": [{
                "aTargets": [0],
                "mData": function (source, type, val)
                {
                    return "Hello";
                }
            }]

        });

    alert("Data ==> " + tbl);

    Charts.initCharts();
});

Above this code in my datatables code I had something like this:

<script>
$(document).ready(function ()
{
    var urlRequest = $('#vitalsTable').data("request-url");

    $('#vitalsTable').dataTable({
        "bSort": false,
        "bServerSide": true,
        "sAjaxSource": urlRequest,
        "sServerMethod": "POST",
        "bProcessing": true,
        "bFilter": false,
        "aLengthMenu": [[10], [10]],
        "aoColumns": [
            { "mDataProp": "VitalsDate" },
            { "mDataProp": "weight" },
            { "mDataProp": "height" },
            { "mDataProp": "bmi" },
            { "mDataProp": "bp" },
        ]
    });
});
</script>

In actuality, all I needed to change was the code directly above, to the code below:

<script>
    $(document).ready(function ()
    {
        var urlRequest = $('#vitalsTable').data("request-url");

        $('#vitalsTable').dataTable({
            "bSort": false,
            "bServerSide": true,
            "sAjaxSource": urlRequest,
            "sServerMethod": "POST",
            "bProcessing": true,
            "bFilter": false,
            "aLengthMenu": [[10], [10]],
            "fnDrawCallback": function (oSettings)
            {
                Charts.initCharts(oSettings);
            },
            "aoColumns": [
                { "mDataProp": "VitalsDate" },
                { "mDataProp": "weight" },
                { "mDataProp": "height" },
                { "mDataProp": "bmi" },
                { "mDataProp": "bp" },
            ]
        });
    });
</script>

Once I did that, I simply needed to make minor adjustments to the client side chart.js file which takes in the data through the Charts.initCharts function and all worked well.

Later I had problems with the anonymous types, so I tried replacing them with proper classes like what C# does behind the scenes. But I found that it was even easier than this to solve. My tip, watch carefully the capitalisation of your variables on the view, controller and client side chart.js. They have to correspond exactly. Took me 2 days of messing around to find that out. It's surprising how simple changes sometimes take the longest.

I hope others looking at this find it useful. If not, post a comment and I will try to answer it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM