Hi I'm currently working on a project that needs paging numbers to navigate through products. I am currently using ReflectionIT.Paging.MVC.
My problem is that the paging buttons are showing however, when clicked it doesn't change pages. I've followed pretty much all tutorials and none of them has gotten the paging buttons to work.
Controller Index Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using MVCManukauTech.Models.DB;
using MVCManukauTech.ViewModels;
using ReflectionIT.Mvc.Paging;
using Pagination;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace MVCManukauTech.Controllers
{
public class CatalogController : Controller
{
private readonly F191_Grace_ProjectContext _context;
public CatalogController(F191_Grace_ProjectContext context)
{
_context = context;
}
public async Task<IActionResult> Index(int page = 1 )
{
//140903 JPC add CategoryName to SELECT list of fields
string SQL = "SELECT ProductId, Product.CategoryId AS CategoryId, Name, ImageFileName, UnitCost"
+ ", SUBSTRING(Description, 1, 100) + '...' AS Description, CategoryName "
+ "FROM Product INNER JOIN Category ON Product.CategoryId = Category.CategoryId ";
string categoryName = Request.Query["CategoryName"];
if (categoryName != null)
{
//140903 JPC security check - if ProductId is dodgy then return bad request and log the fact
// of a possible hacker attack. Excessive length or containing possible control characters
// are cause for concern! TODO move this into a separate reusable code method with more sophistication.
if (categoryName.Length > 20 || categoryName.IndexOf("'") > -1 || categoryName.IndexOf("#") > -1)
{
//TODO Code to log this event and send alert email to admin
return BadRequest(); // Http status code 400
}
//140903 JPC Passed the above test so extend SQL
//150807 JPC Security improvement #p0
SQL += " WHERE CategoryName = #p0";
//SQL += " WHERE CategoryName = '{0}'";
//SQL = String.Format(SQL, CategoryName);
//Send extra info to the view that this is the selected CategoryName
ViewBag.CategoryName = categoryName;
}
//150807 JPC Security improvement implementation of #p0
var products = _context.CatalogViewModel.FromSql(SQL, categoryName).AsNoTracking().OrderBy(s=>s.Name);
var model = await PagingList.CreateAsync(products, 6, page);
return View(model);
View Index Code:
#model ReflectionIT.Mvc.Paging.PagingList <MVCManukauTech.ViewModels.CatalogViewModel>
#using ReflectionIT.Mvc.Paging
#addTagHelper*,ReflectionIT.Mvc.Paging
#{
//Are we showing all the products or only one category?
if (ViewBag.CategoryName == null)
{
ViewBag.Title = "Catalog";
}
else
{
ViewBag.Title = "Catalog - " + ViewBag.CategoryName;
}
}
<link href="~/css/StyleSheet.css" rel="stylesheet" />
<div class="bg">
<h2>#ViewBag.Title</h2>
<nav aria-label="Product Paging">
#await this.Component.InvokeAsync("Pager", new { pagingList = this.Model });
</nav>
<div class="text-center">
<button type="button" class="btn btn-lg">All</button>
<button type="button" class="btn btn-lg">Transports</button>
<button type="button" class="btn btn-lg">Gadgets</button>
<button type="button" class="btn btn-lg">Furnitures</button>
<button type="button" class="btn btn-lg">Kitchen</button>
<button type="button" class="btn btn-lg">Entertainment</button>
<button type="button" class="btn btn-lg">Bathroom</button>
<button type="button" class="btn btn-lg"> Technology</button>
</div>
<table class="table" style="background-color:snow">
<tr>
<th>
Name
</th>
<th>
Image
</th>
<th>
Unit Cost
</th>
<th>
Description
</th>
<th>
Category
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr class="d-block">
<td>
#item.Name
</td>
<td>
<img src="~/Images/Product_Images/#item.ImageFileName" style="width:100px" />
</td>
<td style="text-align: right">
#item.UnitCost
</td>
<td>
#item.Description
</td>
<td>
#item.CategoryName
</td>
<td>
<button>Add to Cart</button>
</td>
<td>
<button>Details</button>
</td>
</tr>
<tr></tr>
}
</table>
<nav aria-label="Product Paging">
<vc:pager paging-list=#Model />;
</nav>
For this issue, it is caused by that page is the reserved routing names.
I have submit a pull request, and check change page to pageindex #24.
You could get this pull request and reference it in your project before the author have merged it.
Note, change the page to pageIndex in your Index action like
public async Task<IActionResult> Index(int pageIndex = 1)
{
var qry = _context.Products.AsNoTracking().OrderBy(u => u.Id);
var model = await PagingList.CreateAsync(qry, 6, pageIndex);
return View(model);
}
Related
I am new to Asp.Net Mvc. I couldn't find a solution that worked for me here, if I am blind just redirect me.
I am trying to make a web-app where i can search through clients, without displaying the entire table of clients. Only after the user presses search, the search result should show as a partial view. I understand that using Ajax is the most popular way of handling something like this.
Any pointers on how to accomplish this?
My first thought was to just make a display: block/none script connected to the submit button but the page updates each time you search rendering this idea useless. That's why i could use some help with how to asynchronously update the web page with the search result.
HomeController:
using testForAutofill.Models;
//Search Functionality
[HttpPost]
public PartialViewResult Index(string searchTerm)
{
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
if (string.IsNullOrEmpty(searchTerm))//Fix this.
{
orderSums = db.ViewNewOrderSum.ToList();
}
else
{
orderSums = db.ViewNewOrderSum.Where(x =>
x.ClientName.Equals(searchTerm)).ToList();
}
return PartialView(orderSums);
}
Index View:
#model IEnumerable<testForAutofill.Models.ViewNewOrderSum>
#using (Html.BeginForm())
{
<b>Kundenavn:</b>
#Html.TextBox("searchTerm", null, new { id = "txtSearch" })
<input type="submit" value="🔍 Search" class="btn btn-primary" id="btn-search" />
}
<div id="posts-wrapper"></div>
<div class="client-div" runat="server" style="max-width: 20rem;">
<div class="card-header">Header</div>
<div class="card-body" id="client-Card">
<h4 class="card-title">Client info</h4>
<table id="client-table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ClientName)
</th>
</tr>
#foreach (var item in Model)
{
#Html.Partial("_OrderSum", item)
}
</table>
</div>
</div>
Partial View:
#model testForAutofill.Models.ViewNewOrderSum
<tr>
<td>
#Html.DisplayFor(modelItem => Model.ClientName)
</td>
</tr>
No need of using Ajax. You can submit search text in Form Post. Fetch your data and filter based on your searchTerm retun to View with model. If your model is not null or empty show table else do not display table.
Checkout the below code :
View :
#model List<testForAutofill.Models.ViewNewOrderSum>
#using (Html.BeginForm()) {
<b>Kundenavn:</b>
#Html.TextBox("searchTerm", null, new { id = "txtSearch" })
<input type="submit" value="🔍 Search" class="btn btn-primary" id="btn-search" />
}
#if (Model != null && Model.Count() > 0) {
<div class="client-div" runat="server" style="max-width: 20rem;">
<div class="card-header">Header</div>
<div class="card-body" id="client-Card">
<h4 class="card-title">Client info</h4>
<table id="client-table">
<tr>
<th>
ClientName
</th>
</tr>
#foreach (var item in Model) {
#Html.Partial("_OrderSum", item)
}
</table>
</div>
</div>
}
Controller :
public ActionResult Index()
{
//if you want to load all the clients by default
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
orderSums = db.ViewNewOrderSum.ToList();
return View(orderSums);
}
[HttpPost]
public ActionResult Index(string searchTerm) {
test_Db_Context db = test_Db_Context();
List<ViewNewOrderSum> orderSums;
if (!string.IsNullOrEmpty(searchTerm))
{
orderSums = db.ViewNewOrderSum.Where(x =>
x.ClientName.Equals(searchTerm)).ToList();
}
return View(result);
}
My first thought was to just make a display: block/none script
connected to the submit button but the page updates each time you
search rendering this idea useless.
You can prevent the page from updating using something like the following (using jQuery):
<script type="text/javascript">
$('form').submit(function (evt) {
evt.preventDefault();
... your code
});
</script>
Then you can make your ajax POST call, get the data, unhide table headers and append the html results from your partial view.
Just wanted to ask if there's something I missed here.
I have two tables that I joined together in my controller
using .NET MVC btw
anyway, here's my controller:
namespace Review.Controllers
{
public class ReviewController : Controller
{
ReviewContext db = new ReviewContext();
ReviewItemsContext db2 = new ReviewItemsContext();
MainDataModel db3 = new MainDataModel();
List<UAR_Review> uar_review = new List<UAR_Review>();
List<UAR_ReviewItems> uar_reviewitems = new List<UAR_ReviewItems>();
public ActionResult Index(int? page) {
ViewBag.AccList = (from r in db2.UAR_ReviewItems
select r.Account).Distinct();
/*var entities = from s in db2.UAR_ReviewItems
orderby s.Account
select s;*/
var entities = from s in uar_review
join st in uar_reviewitems on s.ID equals st.ReviewID into st2
from st in st2.DefaultIfEmpty()
select new MainDataModel { UAR_Review = s, UAR_ReviewItems = st };
int pageSize = 15;
int pageNumber = (page ?? 1);
return View(entities.ToPagedList(pageNumber, pageSize));
}
}
}
and here's my view:
#model PagedList.IPagedList<Review.Models.MainDataModel>
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("Index", "Review", FormMethod.Get))
{
<h2>Index</h2>
#section NavBar{
<h3>Period: 2 of 2018</h3>
#Html.DropDownList("userAccount", new SelectList(ViewBag.AccList), "Select Account to Filter")
<input type="submit" value="Search" />
}
#section MiddleSection{
<table class="table">
<tr>
<th>
<span class="arrow-link contrast-large light-blue">#Html.DisplayName("Name")</span>
</th>
<th>
<span class="arrow-link contrast-large light-blue">#Html.DisplayName("Role")</span>
</th>
<th>
<span class="arrow-link contrast-large light-blue">#Html.DisplayName("Action")</span>
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
<span class="font-gotham-narrow">#item.UAR_Review.DisplayName</span>
</td>
<td>
<span class="font-gotham-narrow">#item.UAR_ReviewItems.Role</span>
</td>
<td>
#Html.HiddenFor(modelItem => item.UAR_ReviewItems.Response)
<span class="font-gotham-narrow">
#Html.RadioButton("Response", "Retain")#Html.Label("Retain")
</span>
<span class="font-gotham-narrow">
#Html.RadioButton("Response", "Remove")#Html.Label("Remove")
</span>
</td>
</tr>
}
</table>
<br />
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("Index",
new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))
}
}
}
The problem is, when I run it there is nothing being displayed in the Site.here's the page.
I just wanted to ask if there's anything I missed.
Also, please disregard the dropdownlist on the left side, I've been planning to use it to sort the shown elements in the page by AccountType(This is stored in my UAR_ReviewItems table). For now, I'm just trying to figure out what's wrong here and why there are no elements being displayed.
Your screen grab does say the PageCount is 0.
Your Model in empty. Check your queries.
I'm going round in circles now so if anyone can identify the issue here I would greatly appreciate it.
I have a partial I'm using to list out items and that works fine.
The post back to the controller works if the items passed back is just set as an ienumerable of the items but I need to pass back a model as it contains more information than just the list.
On doing this the list is empty each time and I cannot see why.
The partial:
#model RequestModel
#section Scripts {
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
<style>
.btn span.glyphicon {
opacity: 0;
}
.btn.active span.glyphicon {
opacity: 1;
}
</style>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
}
#for (var i = 0; i < Model.Requests.Count(); i++)
{
<div #(Model.Requests.Count == 3 ? "class=col-md-4" : Model.Requests.Count() == 2 ? "class=col-md-6" : "class=col-md-12")>
#Html.HiddenFor(m => Model.Requests[i].RequestID)
<table class="table table-responsive img-rounded">
<thead>
<tr class="alert-cascade">
<th colspan="2">
<div class="btn-group btn-group-sm pull-right" role="group" data-toggle="buttons">
<button class="btn btn-success" data-toggle="tooltip" title="Accept" id="acceptradio">
#Html.RadioButtonFor(m => Model.Requests[i].AcceptChecked, Model.Requests[i].AcceptChecked, new { #id = Model.Requests[i].RequestID, #style = "display:none" })
<span>Accept </span>
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</button>
<button class="btn btn-warning" data-toggle="tooltip" title="Reject" id="rejectradio">
#Html.RadioButtonFor(m => Model.Requests[i].RejectChecked, Model.Requests[i].RejectChecked, new { #id = Model.Requests[i].RequestID, #style = "display:none" })
<span>Reject </span>
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
</button>
</div>
#Model.Requests[i].EmployeeDescription
</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Request Type</strong></td>
<td class="text-right">#Model.Requests[i].RequestType</td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td class="text-right">#Model.Requests[i].DurationDescription</td>
</tr>
<tr>
<td><strong>Dates</strong></td>
<td class="text-right">#Model.Requests[i].DatesDescription</td>
</tr>
</tbody>
</table>
</div>
}
The view:
#model RequestPageModel
#{
ViewData["Title"] = "Requests";
ViewData["SubTitle"] = "Welcome to Cascade Mobile";
}
#{Layout = "~/Views/Shared/_MainLayout.cshtml";}
#section Scripts {
<script type="text/javascript">
//Submit count setter
$(document).ready(function () {
var accepted = 0;
var rejected = 0;
$("#acceptradio").click(function () {
console.log("ready 2!");
$("#acceptCount").text("4");
});
});
</script>
}
<div class="container">
<h3>#ViewBag.Warning</h3>
#*Existing requests*#
<h4><strong>Requests</strong> <span class="badge alert-cascade">#Model.Pager.TotalRecords</span></h4><br />
#using (Html.BeginForm("Index", "Request", new { #id = "requestsform" }))
{
<div class="row">
#Html.Partial("_MultiSelectPartial", Model.RequestModel)
</div>
<div>
<textarea class="span6" rows="3" placeholder="Comments.." required></textarea>
</div>
<input type="submit" value="submit"/>
}
<button id="submitbtn" class="btn btn-primary pull-right" type="button">
Accept
<span class="badge" id="acceptCount">0</span>
Reject
<span class="badge" id="rejectCount">0</span>
</button>
#Html.Partial("_Pager", Model.Pager)
</div>
The controller action:
[HttpPost]
public IActionResult Index(RequestModel requests)
{
ViewBag.Warning = "We have: ";
foreach (var request in requests.Requests)
{
ViewBag.Warning += request.RequestID + " : ** : ";
}
var requestModel = GetRequestPageModel(3,1);
//requestModel.Requests[0].AcceptChecked = true;
return View("~/Views/User/Requests.cshtml", requestModel);
}
The model:
using System;
using System.Collections.Generic;
namespace Mobile.Models
{
/// <summary>
/// Request data model for the requests page
/// </summary>
public class RequestModel
{
public List<Request> Requests;
public RequestModel(List<Request> requests)
{
Requests = requests;
}
public RequestModel()
{
Requests = new List<Request>();
}
}
}
Again, if the post method takes just a list of request items its fine, but I will need to pass more information and cannot get it to post the list as part of the model. Can anyone see whats wrong here?
The model in your partial is RequestModel and your loop is generating controls with
name="Requests[0].RequestID"
name="Requests[1].RequestID"
etc, but the model in your POST method should be RequestPageModel so the correct name attributes would need to be
name="RequestModel.Requests[0].RequestID"
name="RequestModel.Requests[1].RequestID"
which will post back to
[HttpPost]
public IActionResult Index(RequestPageModel model)
You need to change the model in the partial to #model RequestPageModel (and adjust the HtmlHelper methods accordingly) and in the main view, use #Html.Partial("_MultiSelectPartial", Model).
In addition, change the name of the parameter to (say) RequestModel model so there is no conflict with the equivalent property name (refer this answer for an explanation).
I would however recommend that you use custom EditorTemplate's for your types rather than a partial, so that you controls are correctly named (refer the 2nd part of this answer for an example)
Side notes:
Your radio buttons do not makes sense, since they have different
names so you can select both (and once selected, you cannot
un-select them)
Your have a <textarea> without a name attribute so it will not
submit a value.
EDIT: Basically want to change the current method of adding more than one of the same product from adding them individually to the cart to when one has been added you can just input how many you want.
Hi guys so currently in my system the quantity of an item added to the cart is updated by the user clicking to continue shopping and going back and selecting a new item each time they wish to add it. I would like for this to be done through either an editor or text box so they can select the quantity they want without having to go back to view the product like 3 times.
Add method (Shopping Cart Controller)
public ActionResult AddToCart(int id)
{
var addedProduct = db.Products.Single(product => product.ID == id);
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.AddToCart(addedProduct);
return RedirectToAction("Index");
}
Add method (Shopping Cart Model)
public void AddToCart(Product product)
{
var cartItem = db.Carts.SingleOrDefault(c => c.CartId == ShoppingCartId && c.ProductId == product.ID);
if (cartItem == null)
{
cartItem = new Cart
{
ProductId = product.ID,
CartId = ShoppingCartId,
Count = 1,
DateCreated = DateTime.Now
};
db.Carts.Add(cartItem);
}
else
{
cartItem.Count++;
}
db.SaveChanges();
}
shopping cart view model
public class ShoppingCartViewModel
{
public List<Cart> CartItems { get; set; }
public decimal CartTotal { get; set; }
}
}
Shopping Cart View
#{
ViewBag.Title = "Store Checkout";
CultureInfo us = new CultureInfo("en-GB");
}
<h3 class="text-center">
<span><img src="~/Content/Images/shoping_cart.png" />Your shopping cart:</span>
</h3>
<div id="update-message" class="text-info">
</div>
#if (Model.CartItems.Count == 0)
{
<a class="btn-danger" href="~/Products/Index">Your shopping cart is empty, continue shopping---></a>
}
else
{
<table class="table-responsive table-bordered table-striped">
<tr>
<th>
Product Name
</th>
<th>
Price (each)
</th>
<th>
Quantity
</th>
<th>Sub-total</th>
<th></th>
</tr>
#foreach (var item in Model.CartItems)
{
<tr id="row-#item.ProductId">
<td>
#Html.ActionLink(item.Product.Name, "Details", "Products", new { id = item.ProductId }, null)
</td>
<td>
#item.Product.Price
</td>
<td id="item-count-#item.ProductId">
#item.Count
</td>
<td>
#((item.Product.Price * item.Count).ToString("c", us))
</td>
<td>
<a href="" class="RemoveLink" data-id="#item.ProductId">
Remove from cart
</a>
</td>
</tr>
}
<tr>
<td>
Total
</td>
<td></td>
<td></td>
<td id="cart-total" class="text-success">
<b>#Model.CartTotal.ToString("C", us)</b>
</td>
</tr>
</table>
<p class="button">
<a>#Html.ActionLink("Continue Shopping", "Index", "Products")</a>
</p>
<p class="button">
#Html.ActionLink("Click and Collect Order>> ", "AddressAndPayment", "Checkout") #Html.ActionLink("Checkout With Braintree>> ", "AddressAndPaymentBraintree", "Checkout")
</p>
}
Any Help to solving this would be much appreciated.
This is a basic form submission
#using(Html.BeginForm("UpdateQuantity", "ShoppingCart", FormMethod.Post))
{
<input name="cartId" value="#cart.Id" type="hidden" />
<input name="productId" value="#product.Id" type="hidden" />
<input name="quantity" type="text" />
<button type="submit">Update</button>
}
The update action
[HttpPost]
public ActionResult UpdateQuantity(int cartId, int productId, int quantity)
{
var cart = db.Carts.FirstOrDefault(c => c.CartId == cartId);
cart.Count = quantity;
db.SaveChanges();
return RedirectToAction("MyCart", routeValues: new { cartId = cartId });
}
[HttpGet]
public ActionResult MyCart(int cartId)
{
var cart = db.Carts.FirstOrDefault(c = c.CartId == cartId);
return View(cart);
}
Hi thanks for the answer Jasen but I found a simplier fix that serves my purpose. basically created a new row in my shopping cart table and added this to it #Html.ActionLink("Add Another?", "AddToCart", "ShoppingCart", new { id = item.ProductId }, new { #class = "btn btn-info" }) thanks for your answer though it might help others but this worked for me.
I am currently writing a simple MVC 3 application that displays a list of items and allows the user to filter the items (on the same page). The user can then click on an item and will be redirected to a details page.
The problem I have is that when the user clicks 'back to list' the search criteria is lost and so is the current page (search results are paged).
I am new to MVC and cannot seem to figure out how this should be done.
Controller
....
public ActionResult Index(PacketSearch search)
{
const int pageSize = 20;
var allPackets = this.repository.GetAllPackets().Where(p => (string.IsNullOrEmpty(search.FromIp)) || p.FromIp == search.FromIp);
var pagedPackets = new PaginatedList<RawPacket>(allPackets, search.Page ?? 0, pageSize);
search.SearchResults = pagedPackets;
return View(search);
}
public ActionResult Details(int id)
{
var packet = this.repository.GetPacket(id);
return View(packet);
}
Main page
....
#if (Model.SearchResults != null && Model.SearchResults.Count > 0)
{
<table>
<tr>
<th>
Timestamp
</th>
<th>
From IP
</th>
</tr>
#foreach (var item in Model.SearchResults) {
<tr>
<td>
#Html.ActionLink(item.TimestampString, "Details", "Packets", new { id = item.Id }, null)
</td>
<td>
#Html.DisplayFor(modelItem => item.FromIp)
</td>
</tr>
}
</table>
}
<br />
#if (Model.SearchResults.HasPreviousPage)
{
#Html.RouteLink("<<<", "Packets", new { page = (Model.SearchResults.PageIndex - 1) })
}
Page #(Model.SearchResults.PageIndex + 1) of #Model.SearchResults.TotalPages
#if (Model.SearchResults.HasNextPage)
{
#Html.RouteLink(">>>", "Packets", new { page = (Model.SearchResults.PageIndex + 1) })
}
Details Page
<h2>Packet Details</h2>
<fieldset>
<legend>RawPacket</legend>
<div class="display-label">Timestamp</div>
<div class="display-field">
#Html.DisplayFor(model => model.TimestampString)
</div>
<div class="display-label">FromIp</div>
<div class="display-field">
#Html.DisplayFor(model => model.FromIp)
</div>
</fieldset>
<p>
#Html.ActionLink("Back to List", "Index")
</p>
Any help would be much appreciated, thanks.
You must persist the input somewhere and the best place is url(because it can be cached and remembered by user).
Define another Action in controller which will do the main job of searching and send the search parameters through a query string to it.