Roger Ospina
Roger Ospina

Reputation: 525

How to implement pagination in MVC with bootstrap datepicker?

Friends, I have implemented a solution type ASP.NetCore with a project MVC. I have a view in which I used https://github.com/cloudscribe/cloudscribe.Web.Pagination for pagination. **Honestly I took the example and used it. But I don't get the detail about this code example.

The problem that I have now is that, I have to include filters, like datepicker range. So I am using bootstrap datepicker. But the pagination stopped working.

The pagination gets this parameters in querytrings to work: pageNumber, pageSize and query. When I send the request of the filter date, I can get the dates selected in the controller, but the parameters of pagination get in null.

This is an URL example with pagination working fine: http://localhost/pager?query=1&pagesize=10&pageNumber=2

This is an URL when I send the request in the button 'Apply' with dates range, and pagination died without its parameters like 'query': http://localhost/pager?startDate=11/04/2019&endDate=11/11/2019

I suppose I have to send the current querystring in the request too, but I'm not sure, I'm kind of new at this technology. Thanks for any help.

My view →

@using (Html.BeginForm("Details", "Movements", routeValues: new { pageNumber = @Model.UserMovementsResults.PageNumber, pageSize = @Model.UserMovementsResults.PageSize, query = @Model.Query }, FormMethod.Get))
{
    <br />
    <div style="border: 2px solid #dee2e6;padding: 5px;width: 50%;">
        <br />
        <div class="input-daterange input-group" id="datepicker">
            <span style="font-weight:bold">Date</span>&nbsp;From&nbsp;

            @Html.TextBoxFor(model => model.StartDateFilter, "{0:d MMM yyyy}", new
            {
            id = "StartDateFilter",
            @class = "input-sm form-control",
            @readonly = "readonly"
            })

            <span class="input-group-addon">&nbsp;To&nbsp;</span>

            @Html.TextBoxFor(model => model.EndDateFilter, "{0:d MMM yyyy}", new
            {
            id = "EndDateFilter",
            @class = "input-sm form-control",
            @readonly = "readonly"
            })
        </div>
        <br />
        <input type="submit" value="Apply" class="btn btn-primary" name="Apply" />
    </div>

    <br />
    <br />
    <table class="table table-striped table-bordered">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.UserMovementsResults.Data.FirstOrDefault().Date)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.UserMovementsResults.Data.FirstOrDefault().Description)
                </th>
            </tr>
        </thead>

        <tbody>
            @foreach (var item in Model.UserMovementsResults.Data)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.Date)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Description)
                    </td>                        
                </tr>
            }
        </tbody>

    </table>
    <div>
        <cs-pager cs-paging-pagesize="@Model.UserMovementsResults.PageSize"
                  cs-paging-pagenumber="@Model.UserMovementsResults.PageNumber"
                  cs-paging-totalitems="@Model.UserMovementsResults.TotalItems"
                  cs-pagenumber-param="pageNumber"
                  cs-show-first-last="true"
                  cs-suppress-empty-nextprev="true"
                  cs-remove-nextprev-links="false"
                  cs-suppress-inactive-firstlast="true"
                  cs-first-page-text="First"
                  cs-last-page-text="Last"
                  cs-pager-li-current-class="active"
                  cs-pager-li-non-active-class="disabled"
                  asp-controller="Movements"
                  asp-route-query="@Model.Query"
                  asp-route-pagesize="@Model.UserMovementsResults.PageSize"
                  asp-route-startDateFilter="@Model.StartDateFilter.GetValueOrDefault()"
                  asp-route-endDateFilter="@Model.EndDateFilter.GetValueOrDefault()"
                  asp-action="Details" cs-preserve-ambient-querystring="true"></cs-pager>
    </div>
}

My Controller (I've tried to set the method HttpGet and HttpPost) →

   [HttpGet]        
   public async Task<IActionResult> Details(int? pageNumber, int? pageSize, int? query, string startDate, string endDate)
    {
        if (query == null)
        {
            return NotFound();
        }

        DateTime startDateFilter = DateTime.Now.StartOfWeek(DayOfWeek.Monday);
        DateTime endDateFilter = DateTime.Now.EndOfWeek(DayOfWeek.Monday);

        var userMovements = await GetUserMovements(user.Id, pageNumber, pageSize, query, startDateFilter, endDateFilter);

        return View(userMovements);
        }
    }

My ViewModel →

public class UserMovementsViewModel
{
    private DateTime? endDateFilter;

    public UserMovementsViewModel()
    {
        UserMovementsResults = new PagedResult<UserMovementsResult>();
    }

    public string Query { get; set; } = string.Empty;

    [Key]
    public int Id { get; set; }

    public int UserId { get; set; }

    public PagedResult<UserMovementsResult> UserMovementsResults { get; set; } = null;

    public DateTime? StartDateFilter { get; set; }

    public DateTime? EndDateFilter
    {
        get => endDateFilter;
        set
        {
            if (value != null)
            {
                endDateFilter = value;
                endDateFilter = endDateFilter.Value.AddHours(23).AddMinutes(59).AddSeconds(59);
            }                
        }
    }
}
public class UserMovementsResult
{
    public DateTime Date { get; set; }

    public string Description { get; set; }
}

Upvotes: 0

Views: 2502

Answers (2)

Roger Ospina
Roger Ospina

Reputation: 525

If anyone needs this too, here is how I got it → I was missing the setting of the parameters in the controller with ViewBag. Like this → ViewBag.StartDateFilter = starDate; Otherwise, It was becoming at null always, when I tried to change at another page. I don't why. I used a hidden field too to save the "query" (in my case this is the user id) because when I sent the request submit in the button, this was losing its value too. What a mess !!

Here is a minified version

View:

<script src="~/lib/bootstrap/bootstrap-datepicker/js/bootstrap-datepicker.js"></script>

<form class="form-inline" role="form" asp-controller="Movements" asp-action="Details" method="get" asp-antiforgery="false" asp-route-query="@ViewBag.Query">
    <div>            
        <div class="input-daterange input-group" id="datepicker">
            <span style="font-weight:bold">Date</span>&nbsp;From&nbsp;

            @Html.TextBox("startDate", null, new
       {
           id = "startDate",
           @class = "input-sm form-control",
           @readonly = "readonly"
       })

            <span class="input-group-addon">&nbsp;To&nbsp;</span>

            @Html.TextBox("endDate", null, new
       {
           id = "endDate",
           @class = "input-sm form-control",
           @readonly = "readonly"
       })
        </div>            
        <button asp-route-query="@ViewBag.Query" type="submit" value="Filter" name="Filter">Apply</button>
        @Html.Hidden("Query", (object)ViewBag.Query)
    </div>
</form>
<br />
<table>
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.UserMovementsResults.Data.FirstOrDefault().Date)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.UserMovementsResults.Data.FirstOrDefault().Description)
            </th>                
        </tr>
    </thead>

    <tbody>
        @foreach (var item in Model.UserMovementsResults.Data)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Date)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Description)
                </td>                    
            </tr>
        }
    </tbody>
</table>
<div>
    <cs-pager cs-paging-pagesize="@Model.UserMovementsResults.PageSize"
              cs-paging-pagenumber="@Model.UserMovementsResults.PageNumber"
              cs-paging-totalitems="@Model.UserMovementsResults.TotalItems"
              cs-pagenumber-param="pageNumber"                  
              asp-controller="Movements"
              asp-action="Details"
              asp-route-query="@ViewBag.Query"
              asp-route-pagesize="@Model.UserMovementsResults.PageSize"
              cs-preserve-ambient-querystring="true"
              asp-route-startDate="@ViewBag.StartDateFilter"
              asp-route-endDate="@ViewBag.EndDateFilter">
    </cs-pager>
</div>    

Controller:

[HttpGet]
    public async Task<IActionResult> Details(int? pageNumber, UserMovementsViewModel userMovementsViewModel)
    {
        userMovementsViewModel.StartDateFilter = DateTime.ParseExact(userMovementsViewModel.StartDate, "dd/MM/yyyy", CultureInfo.InvariantCulture);
        userMovementsViewModel.EndDateFilter = DateTime.ParseExact(userMovementsViewModel.EndDate, "dd/MM/yyyy", CultureInfo.InvariantCulture).SetEndOfDay();

        var userMovements = await GetUserMovements(pageNumber, userMovementsViewModel).ConfigureAwait(true);

        ViewBag.StartDateFilter = userMovementsViewModel.StartDateFilter.Value.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
        ViewBag.EndDateFilter = userMovementsViewModel.EndDateFilter.Value.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
        ViewBag.Query = userMovementsViewModel.Query;

        return View(userMovements);
    }

My Class:

public class UserMovementsViewModel
{
    public UserMovementsViewModel()
    {
        UserMovementsResults = new PagedResult<UserMovementsResult>();
    }
    public string Query { get; set; } = string.Empty;
    public PagedResult<UserMovementsResult> UserMovementsResults { get; set; } = null;
    public DateTime? StartDateFilter { get; set; }
    public DateTime? EndDateFilter { get; set; }
    public string StartDate { get; set; }
    public string EndDate { get; set; }
}

Upvotes: 0

Rena
Rena

Reputation: 36655

Here is a simple workaround like below:

1.add the following component in _ViewImports.cshtml:

@using cloudscribe.Web.Pagination
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper "*, cloudscribe.Web.Pagination"

2.Model:

public class UserMovements
{
    public string Name { get; set; } = string.Empty;
    public DateTime Date { get; set; } = DateTime.UtcNow;
    public string Description { get; set; } = string.Empty;
}
public class ViewByDateViewModel
{
    public ViewByDateViewModel()
    {
        UserMovements = new PagedResult<UserMovements>();

    }
    public PagedResult<UserMovements> UserMovements { get; set; }
    public string[] Date { get; set; }
}

3.View(ViewByDate.cshtml):

@using System.Linq
@model ViewByDateViewModel

<form class="form-inline" role="form" asp-controller="Home" asp-action="ViewByDate" method="get" asp-antiforgery="false">
    <div class="input-daterange input-group" id="datepicker">
        <span style="font-weight:bold">Date</span>&nbsp;From&nbsp;

        @Html.TextBox("startDate", null, new
   {
       id = "startDate",
       @class = "input-sm form-control",
   })

        <span class="input-group-addon">&nbsp;To&nbsp;</span>

        @Html.TextBox("endDate", null, new
   {
       id = "endDate",
       @class = "input-sm form-control",
   })
    </div>
    <input type="submit" value="Browse" class="btn btn-default" />

</form>

@if (Model.UserMovements.Data.Any())
{
    <table class="table table-striped table-bordered">
        <thead>
            <tr>
                <th>Name</th>
                <th>Date</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var product in Model.UserMovements.Data)
            {
                <tr>
                    <td>@product.Name</td>
                    <td>@product.Date</td>
                </tr>
            }
        </tbody>
    </table>
    <cs-pager cs-paging-pagesize="@Model.UserMovements.PageSize"
              cs-paging-pagenumber="@Model.UserMovements.PageNumber"
              cs-paging-totalitems="@Model.UserMovements.TotalItems"
              cs-pagenumber-param="page"
              asp-controller="Home"
              asp-action="ViewByDate"
              asp-route-categories="@Model.Date.ToCsv()"
              asp-route-pagesize="@Model.UserMovements.PageSize"
              cs-first-page-text="First"
              cs-last-page-text="Last"
              cs-previous-page-text="Prev"
              cs-next-page-text="Next"></cs-pager>


}
@section Scripts
{
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <script>
        $(document).ready(function () {
            $("#startDate").datepicker({ format: 'dd/mm/yyyy', autoclose: true, todayBtn: 'linked' });
            $("#endDate").datepicker({ format: 'dd/mm/yyyy', autoclose: true, todayBtn: 'linked' })
        });
    </script>
}

4.Controller:

public class HomeController : Controller
{
    private const int DefaultPageSize = 10;
    private List<UserMovements> allMovements = new List<UserMovements>();

    public HomeController()
    {
        InitializeMovements();
    }

    private void InitializeMovements()
    {
        // Create a list of Movements. 
        for (var i = 0; i < 527; i++)
        {
            var userMovements = new UserMovements();
            userMovements.Name = "UserMovements " + (i + 1);
            var categoryIndex = i % 4;
            if (categoryIndex > 2)
            {
                categoryIndex = categoryIndex - 3;
            }
            userMovements.Date = DateTime.Now.AddDays(i);
            allMovements.Add(userMovements);
        }
    }    

    public IActionResult ViewByDate(string startDate, string endDate, int? page)
    {

        string[] dates = { startDate, endDate };
        List<UserMovements> filtered;
        var currentPageNum = page.HasValue ? page.Value : 1;
        var offset = (DefaultPageSize * currentPageNum) - DefaultPageSize;
        var model = new ViewByDateViewModel();

        model.Date = dates ?? new string[0];
        int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
        if (startDate == null && endDate == null)
        {
            filtered = this.allMovements.ToList();
        }
        else
        {
            filtered = this.allMovements
                .Where(p => p.Date.Date >= DateTime.Parse(startDate) && p.Date.Date <= DateTime.Parse(endDate))
                .ToList();
        }

        model.UserMovements.Data = filtered
            .Skip(offset)
            .Take(DefaultPageSize)
            .ToList();

        model.UserMovements.PageNumber = currentPageNum;
        model.UserMovements.PageSize = DefaultPageSize;
        model.UserMovements.TotalItems = filtered.Count;

        return View(model);
    }
}

5.Result: enter image description here

UPDATE:

1.Model:

public class ViewByDateViewModel
{
    private DateTime? endDateFilter;
    public ViewByDateViewModel()
    {
        UserMovements = new PagedResult<UserMovements>();

    }
    public PagedResult<UserMovements> UserMovements { get; set; }
    //public string[] Date { get; set; }
    public DateTime? StartDateFilter { get; set; }

    public DateTime? EndDateFilter
    {
        get => endDateFilter;
        set
        {
            if (value != null)
            {
                endDateFilter = value;
                endDateFilter = endDateFilter.Value.AddHours(23).AddMinutes(59).AddSeconds(59);
            }
        }
    }
}

2.View:

@using System.Linq
@model ViewByDateViewModel
<form class="form-inline" role="form" asp-controller="Home" asp-action="ViewByDate" method="get" asp-antiforgery="false">
        <div class="input-daterange input-group" id="datepicker">
            <span style="font-weight:bold">Date</span>&nbsp;From&nbsp;

            @Html.TextBox("startDate", null, new
       {
           id = "startDate",
           @class = "input-sm form-control",
       })

            <span class="input-group-addon">&nbsp;To&nbsp;</span>

            @Html.TextBox("endDate", null, new
       {
           id = "endDate",
           @class = "input-sm form-control",
       })
        </div>
        <input type="submit" value="Browse" class="btn btn-default" />

    </form>

    @if (Model.UserMovements.Data.Any())
    {
        <table class="table table-striped table-bordered">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Date</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var product in Model.UserMovements.Data)
                {
                    <tr>
                        <td>@product.Name</td>
                        <td>@product.Date</td>
                    </tr>
                }
            </tbody>
        </table>
        <cs-pager cs-paging-pagesize="@Model.UserMovements.PageSize"
                  cs-paging-pagenumber="@Model.UserMovements.PageNumber"
                  cs-paging-totalitems="@Model.UserMovements.TotalItems"
                  cs-pagenumber-param="page"
                  asp-controller="Home"
                  asp-action="ViewByDate"

                  asp-route-pagesize="@Model.UserMovements.PageSize"
                  asp-route-startDateFilter="@Model.StartDateFilter"
                  asp-route-endDateFilter="@Model.EndDateFilter"
                  cs-first-page-text="First"
                  cs-last-page-text="Last"
                  cs-previous-page-text="Prev"
                  cs-next-page-text="Next"></cs-pager>
    }

@section Scripts
    {
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <script>
        $(document).ready(function () {
            $("#startDate").datepicker({ format: 'dd/mm/yyyy', autoclose: true, todayBtn: 'linked' });
            $("#endDate").datepicker({ format: 'dd/mm/yyyy', autoclose: true, todayBtn: 'linked' })
        });
    </script>
}

3.Controller:

public IActionResult ViewByDate(string startDate, string endDate, int? page)
{

    string[] dates = { startDate, endDate };
    List<UserMovements> filtered;
    var currentPageNum = page.HasValue ? page.Value : 1;
    var offset = (DefaultPageSize * currentPageNum) - DefaultPageSize;
    var model = new ViewByDateViewModel();

    model.StartDateFilter = startDate==null? DateTime.Now:DateTime.Parse(startDate);
    model.EndDateFilter = endDate == null ? DateTime.Now : DateTime.Parse(endDate);

    int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
    if (startDate == null && endDate == null)
    {
        filtered = this.allMovements.ToList();
    }
    else
    {
        filtered = this.allMovements
            .Where(p => p.Date.Date >= DateTime.Parse(startDate) && p.Date.Date <= DateTime.Parse(endDate))
            .ToList();
    }

    model.UserMovements.Data = filtered
        .Skip(offset)
        .Take(DefaultPageSize)
        .ToList();

    model.UserMovements.PageNumber = currentPageNum;
    model.UserMovements.PageSize = DefaultPageSize;
    model.UserMovements.TotalItems = filtered.Count;

    return View(model);
}

if you want to send by url,url would be like:https://localhost:44367/Home/ViewByDate?startdate=11-14-2019&enddate=11-27-2019

Upvotes: 1

Related Questions