Search code examples
c#asp.net-corerazorblazor

Filtering records by month and year - RazorPage/C#


I am trying to filter my records by month and year. The filtering for the month works fine, but the filter for the year doesn't.

For some reason if I try to change the year it always sets the value for my SelectedYear-Property to "01/01/0001", or if I set a default value for my SelectedYear-Property (e.g. DateTime.Today) and if I try to change it it always hops back to the default value.

Maybe you can help me I can't figure out where the problem is.

Probably has something to do with wrong ModelBinding?

Here is my code:

Index.cshtml.cs

public class IndexModel : PageModel
{
    private readonly CMSContext _context;
    private readonly ILogger<IndexModel> _logger;

    public IndexModel(CMSContext context, ILogger<IndexModel> logger) 
    {
        _context = context;
        _logger = logger;
    }

    public IList<TimeManagementModel> TimeManagementModel { get; set; }

    [BindProperty(SupportsGet = true)]
    public DateTime SelectedMonth { get; set; } = DateTime.Today;

    [BindProperty(SupportsGet = true)]
    public DateTime SelectedYear { get; set; } = DateTime.Today;

    public async Task OnGetAsync()
    {
        _logger.LogInformation("SelectedYear value: {SelectedYear}", SelectedYear);

        TimeManagementModel = await _context.TimeManagerModel
        .Where(x => x.Date.Month == SelectedMonth.Month && x.Date.Year == SelectedYear.Year)
        .OrderByDescending(x => x.Date)
        .ToListAsync();


        CalculateDailyWorkedHours();
        CalculateOvertime(7.7);
    }
}

Index.cshtml

@page
@model TestRazor.Pages.TimeManager.IndexModel
@{
    ViewData["Title"] = "TimeManager";
}

<h1>Time Manager</h1>

<head>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
    <script type="text/javascript" src="/js/TimeManager.js"></script>
</head>
<body>
    <p>
        <a asp-page="Create" class="btn btn-success">Create New</a>
    </p>

    <form method="get">
        <div style="display: flex">
            <div style="margin-right: 10px;">
                <label for="selectedMonth">Select Month:</label>
                <select name="SelectedMonth" id="selectedMonth" class="form-control" onchange="this.form.submit()">
                    @{
                        var today = DateTime.Today;
                        var selectedMonthValue = Model.SelectedMonth.ToString("yyyy-MM");
                        var months = Enumerable.Range(1, 12).Select(i => new DateTime(today.Year, i, 1));

                        foreach (var month in months)
                        {
                            <option value="@month.ToString("yyyy-MM")" selected="@(selectedMonthValue == month.ToString("yyyy-MM"))">
                                @month.ToString("MMMM")
                            </option>
                        }
                    }
                </select>
            </div>
            <div>
                <label for="selectedYear">Select Year:</label>
                <select name="SelectedYear" id="selectedYear" class="form-control" onchange="this.form.submit()">
                    @{
                        var currentYear = DateTime.Today.Year;

                        for (var year = currentYear - 5; year <= currentYear + 5; year++)
                        {
                            <option value="@year" selected="@(Model.SelectedYear.Year == year)">@year</option>
                        }
                    }
                </select>
            </div>
        </div>
    </form>

</body>

Solution

  • Your SelectedYear in backend is type of Datetime, but your frontend year value is an int/string type which cannot be converted to Datetime by default.

    One way is to change your backend type to int/string.

    [BindProperty(SupportsGet = true)]
    public int SelectedYear { get; set; }
    
                        
    

    And

    <option value="@year" selected="@(Model.SelectedYear == year)">@year</option>
    

    Another way is to set the value with Datetime in frontend:

    <option value="@(new DateTime(year,1,1).ToString("yyyy-MM"))" selected="@(Model.SelectedYear.Year == year)">@year</option>