sunny
sunny

Reputation: 2729

Changing dropdownlist value makes duplicate page in razor pages

I want to implement searching, pagination and sorting in razor pages with Ajax. I use viewcomponents to show data from database, for that these are my code by files in ViewComponent.cs

    public async Task<IViewComponentResult> InvokeAsync(int categoryId, string search, string sort, int page, int pageSize)
     {

    var items = await _equipmentRepository.GetEquipmentActive();

    // Filtering by category
    if (categoryId > 0)
    {
        items = items.Where(x => x.HealthProjectUsefilCategoryId == categoryId).ToList();
    }

    // Searching
    if (!string.IsNullOrEmpty(search))
    {
        items = items.Where(x => x.Name != null && x.Name.Contains(search, StringComparison.OrdinalIgnoreCase)).ToList();
    }

    // Sorting
    items = sort switch
    {
        "Price" => items.OrderBy(x => x.Price).ToList(),
        "Priority" => items.OrderBy(x => x.Priority).ToList(),
        _ => items
    };

    // Pagination
    var pagedItems = items.Skip((page - 1) * pageSize).Take(pageSize).ToList();

    // JSON response
    var result = new
    {
       Items = pagedItems.Select(x => new
        {
            x.Id,
            x.Name,
            x.Price,
            x.Priority,
            x.Description,
            x.ImageUrl,
            x.ModifyDate
        }),
        TotalCount = items.Count,
        CurrentPage = page,
        TotalPages = (int)Math.Ceiling(items.Count / (double)pageSize)
  
    };
    
 
  foreach (var item in items)
  {
     Console.WriteLine($"Equipment: {item.Name}, ID: {item.Id}");
  }


    var viewModel = new EquipmentViewModel
    {
        Equipments = pagedItems,
        TotalCount = items.Count,
        CurrentPage = page,
        TotalPages = (int)Math.Ceiling(items.Count / (double)pageSize)
    };
 return View(viewModel);
 

}

In View in Default.cshtml these are my code

@using DNTPersianUtils.Core
@using KH.Services;
@model KH.Models.EquipmentViewModel;

@foreach (var card in Model.Equipments)
{
<article class="postcard light blue">

    <a class="postcard__img_link" href="#">
        <img class="postcard__img" src="@card.ImageUrl" alt="@card.Name" />
    </a>


    <div class="postcard__text t-dark">
        <h1 class="postcard__title"><a href="#">@card.Name</a></h1>
        <div class="postcard__subtitle small">
            <time datetime="@card.ModifyDate.ToString("yyyy-MM-ddTHH:mm:ss")">
                <i class="fas fa-calendar-alt mr-2"></i>
                @DNTPersianUtils.Core.PersianDateTimeUtils.ToShortPersianDateString(card.ModifyDate).ToPersianNumbers()

            </time>
        </div>
        <div class="postcard__bar"></div>
        <div class="postcard__preview-txt">@card.Description</div>
        <ul class="postcard__tagbox">
            <li class="tag__item"><i class="fas fa-tag mr-2"></i>تعداد مورد نیاز:
                @card.Quantity.ToPersianNumbers()</li>
            <li class="tag__item"><i class="fas fa-tag mr-2"></i>
                قیمت:@DNTPersianUtils.Core.PersianNumbersUtils.ToPersianNumbers(string.Format("{0:N0}",
                    (int)@card.Price)) ریال
                @* (@card.Price.ToPersianNumbers())*@</li>
            <li class="tag__item"><i class="fas fa-clock mr-2"></i>تعداد تهیه شده تاکنون .</li>
            <li class="tag__item play blue">
                <a asp-page="/ReceiptPages/DefineDonation" asp-route-Amount="@card.Price" asp-area=""><i
                        class="fas fa-play mr-2"></i>کمک مالی</a>
            </li>
        </ul>
    </div>
</article>

}

also this is my ViewModel that I use in View

public class EquipmentViewModel
{
  public IEnumerable<Equipment>? Equipments { get; set; } // The list of equipment items
  public int TotalCount { get; set; }                   // Total number of filtered items
  public int CurrentPage { get; set; }                  // The current page number
  public int TotalPages { get; set; }                   // Total number of pages
}

And this is my code in view that invokes viewcomponent in Index.cshtml also I remine Ajax code

 <section class="light mt-4">
<div class="container py-2">
    <div class="h1 text-center text-dark" id="pageHeaderTitle">Equipments</div>
    <!-- Filters -->
    <select id="category-filter" class="form-control">
        <option value="0">all category</option>
        @foreach (var category in Model.Categories!)
        {
            <option value="@category.Id">@category.Name</option>
        }
    </select>

    <select id="sort-filter" class="form-control mt-2">
        <option value="Price">price</option>
        <option value="Priority">priority</option>
    </select>

    <input id="search-box" type="text" class="form-control mt-2" placeholder="جستجو 
(min 3 character)">
   <div id="equipment-list">
    @await Component.InvokeAsync("EquipmentsList", new
        {
            categoryId = 0,
            search = "",
            sort = "Date",
            page = 1,
            pageSize = 10
        })
   </div>
    <div id="pagination" class="pagination mt-3">
        @for (int i = 1; i <= ViewBag.TotalPages; i++)
        {
            <button class="page-link" data-page="@i">@i</button>
        }
    </div>

</div>
@section Scripts {
  <script>
    $(document).ready(function () {
        function loadEquipmentData(page = 1) {
            const categoryId = $('#category-filter').val();
            const sort = $('#sort-filter').val();
            const search = $('#search-box').val();

            // Make an AJAX request to fetch filtered data
            $.ajax({
                url: "/Equipment", // Ensure this endpoint handles 
       filtering/sorting/pagination
                data: { categoryId, sort, search, page, pageSize: 10 },
                success: function (result) {
                    // Replace only the equipment list content
                    $('#equipment-list').html(result);
                },
                error: function () {
                    alert("Failed to load data!");
                }
            });
        }

        // Attach event handlers (ensure no duplicates using .off())
        $('#category-filter').off('change').on('change', function () {
            loadEquipmentData(1); // Load data for the first page
        });

        $('#sort-filter').off('change').on('change', function () {
            loadEquipmentData(1); // Load data for the first page
        });

        $('#search-box').off('input').on('input', function () {
            if ($(this).val().length >= 3 || $(this).val().length === 0) {
                loadEquipmentData(1); // Load data for the first page
            }
        });

        $(document).off('click', '.page-link').on('click', '.page-link', function (e) {
            e.preventDefault(); // Prevent default navigation behavior
            const page = $(this).data('page');
            loadEquipmentData(page); // Load data for the clicked page
        }); 
    });


  

  </script>
}

I checked page source after changing any field of dropdownlist it will create the duplicate page that starts from HTMl tag to the container. I think I have a problem with loading Ajax or jquery. What's your suggestion?

Upvotes: 0

Views: 43

Answers (1)

Tiny Wang
Tiny Wang

Reputation: 16066

it will create the duplicate page that starts from HTMl tag to the container. I think I have a problem with loading Ajax or jquery

I'm not sure what do you mean here. In your code snippet, you are now trying to change the view content through an ajax request. In the response of the ajax request, it should have the html content which has same format as what you could get from

@await Component.InvokeAsync("Equipment", new
    {
        categoryId = 0,
        search = "",
        sort = "Date",
        page = 1,
        pageSize = 10
    })

Then in your ajax request, you set the URL as url: "/Equipment" but you didn't share that, so that in my side, except hard coding DB query result, I also create a POST endpoint in HomeController like below.

public IActionResult Equipment() { 
    return View();
}

[HttpPost]
public IActionResult GetEquipment(int categoryId, string search, string sort, int page, int pageSize)
{
    

    //return View(viewModel);
    return ViewComponent("Equipment",
                            new
                            {
                                categoryId = categoryId,
                                search = search,
                                sort = sort,
                                page = page,
                                pageSize = 10
                            });

}

And my ajax request is like

$.ajax({
    url: "/home/GetEquipment", // Ensure this endpoint handles filtering/sorting/pagination
    type: "post",
    data: { categoryId, sort, search, page, pageSize: 10 },
    success: function (result) {
        // Replace only the equipment list content
        $('#equipment-list').html(result);
    },
    error: function () {
        alert("Failed to load data!");
    }
});

This is my invokation method.

public async Task<IViewComponentResult> InvokeAsync(int categoryId, string search, string sort, int page, int pageSize)
{

    //var items = await _equipmentRepository.GetEquipmentActive();
    var items = new List<Equipment>();
    if (sort == "Price")
    {
        items = new List<Equipment>{
            new Equipment { Id = "11", Name = "Equ11", Price = "1001", Description="descp11"} ,
            new Equipment { Id = "22", Name = "Equ22", Price = "1012", Description="descp22"} ,
            new Equipment { Id = "33", Name = "Equ33", Price = "1023", Description="descp33"} ,
            new Equipment { Id = "44", Name = "Equ44", Price = "1034", Description="descp44"} ,
            new Equipment { Id = "55", Name = "Equ55", Price = "1045", Description="descp55"} ,
            new Equipment { Id = "66", Name = "Equ66", Price = "1056", Description="descp66"} ,
            new Equipment { Id = "77", Name = "Equ77", Price = "1067", Description="descp77"} ,
            new Equipment { Id = "88", Name = "Equ88", Price = "1078", Description="descp88"} ,
        };
    }
    else {
        items = new List<Equipment>() {
            new Equipment { Id = "1", Name = "Equ1", Price = "100", Description="descp1"} ,
            new Equipment { Id = "2", Name = "Equ2", Price = "101", Description="descp2"} ,
            new Equipment { Id = "3", Name = "Equ3", Price = "102", Description="descp3"} ,
            new Equipment { Id = "4", Name = "Equ4", Price = "103", Description="descp4"} ,
            new Equipment { Id = "5", Name = "Equ5", Price = "104", Description="descp5"} ,
            new Equipment { Id = "6", Name = "Equ5", Price = "105", Description="descp6"} ,
            new Equipment { Id = "7", Name = "Equ6", Price = "106", Description="descp7"} ,
            new Equipment { Id = "8", Name = "Equ7", Price = "107", Description="descp8"} ,
        };
    }
    var pagedItems = items.Skip((page - 1) * pageSize).Take(pageSize).ToList();
    var result = new
    {
        Items = pagedItems.Select(x => new
        {
            x.Id,
            x.Name,
            x.Price,
            //x.Priority,
            x.Description
            //x.ImageUrl,
            //x.ModifyDate
        }),
        TotalCount = items.Count,
        CurrentPage = page,
        TotalPages = (int)Math.Ceiling(items.Count / (double)pageSize)
    };
    foreach (var item in items)
    {
        Console.WriteLine($"Equipment: {item.Name}, ID: {item.Id}");
    }
    var viewModel = new EquipmentViewModel
    {
        Equipments = pagedItems,
        TotalCount = items.Count,
        CurrentPage = page,
        TotalPages = (int)Math.Ceiling(items.Count / (double)pageSize)
    };
    return View(viewModel);
}

And my test result. enter image description here enter image description here

Upvotes: 0

Related Questions