Search code examples
jqueryajaxasp.net-corerazor-pagespartial-views

Create dynamic html table of dates using Razor Page Partial


I'm developing a .NET Core 3.1 Razor Page Application.

Request

I need to create a page that allows a user to select a date, add X amount of days to that date and then generate a table comprising of those dates with the ability to add a value against each date.

Approach

I have a page that allows the user to select a start date (using a Date Picker) and a number (from a drop-down list) that represents the total number of days to add to the start date.

enter image description here

Once the user hits the Set button, a JQuery and Ajax (GET) call is made to a Get Handler in my Page Model:

JQuery/ Ajax

<script type='text/javascript'>
$(document).ready(function () {
    //alert("Ready!");

    $("#load").click(function () {
        //alert("button");
        var startDate = $("#StartDate").val();
        var duration = $('#Duration').val();
        var turl = '/SPC/Index?handler=CarPartial&StartDate=' + startDate + '&Duration=' + duration;        
            $('#spc_data_table').load(turl);
        
    });

});

CSHTML

<div class="row">
 <div id="spc_data_table" class="col-md-12">


 </div>
</div>

Page Model

[BindProperty]
public IList<SpcTable> data { get; set; }

public class SpcTable
{
    public int id { get; set; }
    public string tableDate { get; set; }
    public string tableValue { get; set; }
}

public PartialViewResult OnGetCarPartial(string StartDate, string Duration)
    {
        data = new List<SpcTable>();
        var EndDate = Convert.ToDateTime(StartDate).AddDays(Convert.ToDouble(Duration) - 1);
        int count = 0;

        for (var date = Convert.ToDateTime(StartDate); date <= EndDate; date = date.AddDays(1))
        {
            data.Add(new SpcTable { id = count, tableDate = date.ToShortDateString(), tableValue = null });
            count++;
        }

        return Partial("_SpcTable", data);
    }

Partial

<script type='text/javascript'>
$(document).ready(function () {

    $('#submit').on('click', function (evt) {
        evt.preventDefault();
        $.post('', $('form').serialize(), function () {
            alert('Posted using jQuery');
        });
    });

});
<table class="table">
    <tr>
        <td></td>
        <td>Date</td>
        <td>Value</td>
    </tr>

    @foreach (var item in Model)
    {
        <tr>
            <td>
                <input type="hidden" name="data.Index" value="@item.id" />
                <input type="hidden" name="data[@item.id].id" value="@item.id" />
                @item.id
            </td>
            <td><input name="data[@item.id].tableDate" value="@item.tableDate" class="form-control form-control-sm" readonly/></td>
            <td><input name="data[@item.id].tableValue" value="@item.tableValue" class="form-control form-control-sm" /></td>

        </tr>
    }

</table>

<div class="form-group row">
    <button class="btn btn-primary" id="submit">Submit</button>
</div>

A partial page is then returned in the form of a HTML table and it will contain a row for every date. The user then adds a value beside each date and then submits the form via another JQuery/ Ajax call (POST)

enter image description here

This works, however, if the user wants to increase or decrease the number of days in the table, they select a different number from the drop down list and reload the partial, however, all of the initial values they would have input against the initial dates within the table are lost.

My Questions

  1. Is there a better approach to creating my request?
  2. If not, is there a way I can keep the table values in the partial even if it is reloaded?

Thank you.


Solution

  • When you set the tableValue for tableDate, you can add an onchange event to monitor if the tableValue changed or not. Then add the value to list and pass it to backend as a json string. In backend, you can deserialize this json and compare with new tableDate, add the value to the existing tableDate.

    Here is a working demo:

    View:

    @page
    @model IndexModel
    <form>
        <input id="StartDate" type="date" />
        <select id="Duration">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <input type="button" id="load" value="set" />
        <div class="row">
            <div id="spc_data_table" class="col-md-12">
       
            </div>
        </div>
    </form>
    

    JS in View:

    @section Scripts
    {
    <script>
        var obj = {};
        $(document).ready(function () {
            $("#load").click(function () {
                var startDate = $("#StartDate").val();
                var duration = $('#Duration').val();
                var turl = '?handler=CarPartial&StartDate=' + startDate + '&Duration=' + duration;
                if (jQuery.isEmptyObject(obj)) {
                    $('#spc_data_table').load(turl);
                }
                else {
                    turl = turl + "&json=" + JSON.stringify(obj);
                    $('#spc_data_table').load(turl);
                    obj = {};   
                }
            });
    
        });
        function saveValue(objectValue) {
            var value = objectValue.value;
            var keyName = $(objectValue).closest("td").prev("td").find("input").val()
            obj[keyName] = value;
        }
    </script>
    }
    

    Partial View:

    <table class="table">
        <tr>
            <td></td>
            <td>Date</td>
            <td>Value</td>
        </tr>  
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    <input type="hidden" name="data.Index" value="@item.id" />
                    <input type="hidden" name="data[@item.id].id" value="@item.id" />
                    @item.id
                </td>
                <td><input name="data[@item.id].tableDate" value="@item.tableDate" class="form-control form-control-sm" readonly /></td> 
                             //add this event....
                <td><input onchange="saveValue(this);" name="data[@item.id].tableValue" value="@item.tableValue" class="form-control form-control-sm" /></td>
    
            </tr>
        }
    
    </table>
    

    PageModel:

    public class IndexModel : PageModel
    {
        public IActionResult OnGet()
        {
    
            return Page();
        }
    
        [BindProperty]
        public IList<SpcTable> data { get; set; }
    
        public class SpcTable
        {
            public int id { get; set; }
            public string tableDate { get; set; }
            public string tableValue { get; set; }
        }
        public PartialViewResult OnGetCarPartial(string StartDate, string Duration, string json)
        {
    
            data = new List<SpcTable>();
            var EndDate = Convert.ToDateTime(StartDate).AddDays(Convert.ToDouble(Duration) - 1);
            int count = 0;
    
            for (var date = Convert.ToDateTime(StartDate); date <= EndDate; date = date.AddDays(1))
            {
                data.Add(new SpcTable { id = count, tableDate = date.ToShortDateString(), tableValue = null });
                count++;
            }
            //judge if the set tableValue for tableDate in frontend
            if (!string.IsNullOrEmpty(json))
            {
                //deserialize to the model
                var model = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(json);
                foreach (var item in model)
                {
                    foreach (var date in data)
                    {
                        //if you set value for tableDate
                        //and the tableDate still exist after filtering
                        if (item.Key == date.tableDate)
                        {
                            //set the value for this tableDate
                            date.tableValue = item.Value;
                        }
                    }
                }
            }
            return Partial("_SpcTable", data);
        }
    
    }
    

    Note:

    The onchange event will trigger if you did a second operation. That is to say, when you fill an input, you need choose a select or fill another input or click the white space of the page to trigger the onclick event.