I've created a dropdown to filter results. This works great the 1st time, but if the user selects another option it filters on the filtered results. Is there a way to clear the results before it filters again?
RAZOR
#using (Html.BeginForm("Index", "Admin", FormMethod.Get))
{
#Html.DropDownList("category", (SelectList)ViewBag.Categories, "--Select One--")
<input type="submit" value="Filter" />
}
Server code
public ViewResult Index(string category, int page = 1)
{
var categoryList = categoriesRepository.Categories.Select(c => c.CategoryName).Distinct();
ViewBag.Categories = new SelectList(categoryList.AsEnumerable());
var viewModel = new ProductsListViewModel
{
Products = repository.Products
.Where(p => category == null || p.Category == category)
.OrderBy(p => p.ProductID)
.Skip((page - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = category == null
? repository.Products.Count()
: repository.Products.Where(e => e.Category == category).Count()
},
CurrentCategory = category
};
return View(viewModel);
}
Related
I have been stuck on this for over a day now and it's driving me nuts. I don't know what I'm doing wrong or what I'm missing. Anyways, I have a Users List page that is using ASP Identity framework. I have some filters at the top of the page that will filter the users by: UserName(txtbox), CompanyName(dropdown), and by Role(dropdown). My current code is filtering it, but it won't filter it with the other criteria's if they use multiple filters (like CompanyName and Role). I had to query a List string to populate my model to display in the view. So my problem I'm having trouble figuring out is how to add the "Roles" (which is the List string) to my controller code that incorporates 3 strings from the filters together. But since Roles is already a string, if I put .ToString() after it, the filter will not show any records when triggered. If I take the .ToString() off of Roles, it works only if I were to filter the Roles by itself, but it will not work with the other filters. Any suggestions on what I can do would be greatly appreciated. Here is my model:
public class UserList
{
public UserList() { }
public ApplicationUser User { set; get; }
public List<string> Roles { get; set; }
}
Here is my view:
#using (Html.BeginForm("ListUsers", "Administrator", FormMethod.Get))
{
<div class="form-inline title">
<h4>Users</h4>
<button class="create-btn"><span class="fa-solid fa-plus"></span> #Html.ActionLink("Create New User", "Register", "Account", new { #class = "CreateLink" })</button>
</div>
<p class="filter-section">
<label class="filter-lbl1">By Company:</label><label class="filter-ddl">#Html.DropDownList("strComp", Extensions.GetCompanyList(), "All", new { #class = "comp-ctl" })</label>
<label class="filter-lbl2">By Role:</label><label class="filter-ddl">#Html.DropDownList("strRole", Extensions.GetRolesList(), "All", new { #class = "role-ctn" })</label><br />
<label class="filter-lbl3">By User Name:</label><label class="filter-txtbox">#Html.TextBox("strName", "", new { #class = "name-ctl" })</label>
<button type="button" class="red" onclick="location.href='#Url.Action("ListUsers", new { })'"><i class="fa solid fa-trash-o"></i> Reset</button>
<button type="submit" class="filter-btn"><i class="fa solid fa-filter"></i> Filter</button>
</p>
}
<table class="table">
<tr>
<th>
Email
</th>
<th>
Company
</th>
<th>
Role
</th>
<th>
Last Login
</th>
<th></th>
</tr>
#foreach (var user in Model)
{
var appUser = user.User;
var roles = user.Roles;
<tr>
<td class="email">
#Html.HiddenFor(modelitem => user.User.Id)
#Html.DisplayFor(modelitem => user.User.Email)
</td>
<td>
#Html.DisplayFor(modelitem => user.User.CompanyName)
</td>
<td>
#Html.DisplayFor(modelitem => user.Roles, string.Join(",", roles))
</td>
<td>
</td>
<td>
#Html.ActionLink("Edit", "EditUser", new { id = appUser.Id }, new { #class = "edit-link" }) |
#Html.ActionLink("Delete", "DeleteUser", new { id = appUser.Id }, new { #class = "delete-link" })
</td>
</tr>
}
</table>
<link href="~/Content/PagedList.css" rel="stylesheet" />
<div></div>
Page #(Model.PageCount < Model.PageNumber ? 0: Model.PageNumber)/#Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("ListUsers", new { page, strComp = ViewData["strFilter1"], strRole = ViewData["strFilter2"], strName = ViewData["strFilter3"] }))
And here is my code for my controller part:
public ActionResult ListUsers(string strFilter1, string strFilter2, string strFilter3, string strComp, string strName, string strRole, int? page)
{
if (strComp != null && strRole != null && strName != null)
{
strFilter1 = strComp;
strFilter2 = strRole;
strFilter3 = strName;
}
ViewBag.strFilter1 = strComp;
ViewBag.strFilter2 = strRole;
ViewBag.strFilter3 = strName;
var user = (from u in context.Users
let query = from ur in context.Set<IdentityUserRole>()
where ur.UserId.Equals(u.Id)
join r in context.Roles on ur.RoleId equals r.Id
select r.Name
select new UserList() { User = u, Roles = query.ToList<string>() })
.ToList();
if (!String.IsNullOrEmpty(strComp))
{
//Filter results based on company selected.
var pageNumber = page ?? 1;
var pageSize = 25;
if (strRole == null && strName == null)
{
var comp = user.Where(u => u.User.CompanyName.ToString().Contains(strComp));
return View(comp.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
}
else
{
var compuser = user.Where(u => u.User.CompanyName.ToString().Contains(strComp) &&
u.Roles.ToString().Contains(strRole) &&
u.User.UserName.ToString().Contains(strName));
return View(compuser.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
}
}
if (!String.IsNullOrEmpty(strRole))
{
//Filter results based on role selected.
var pageNumber = page ?? 1;
var pageSize = 25;
if (strComp == null && strName == null)
{
var roll = user.Where(u => u.Roles.Contains(strRole));
return View(roll.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
}
else
{
var rolluser = user.Where(u => u.Roles.Contains(strRole) &&
u.User.CompanyName.ToString().Contains(strComp) &&
u.User.UserName.ToString().Contains(strName));
return View(rolluser.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
}
}
if (!String.IsNullOrEmpty(strName))
{
//Filter results based on the username typed in.
var pageNumber = page ?? 1;
var pageSize = 25;
if (strComp == null && strRole == null)
{
var uname = user.Where(u => u.User.UserName.ToString().Contains(strName));
return View(uname.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
}
else
{
var nameuser = user.Where(u => u.User.UserName.ToString().Contains(strName) &&
u.User.CompanyName.ToString().Contains(strComp) &&
u.Roles.ToString().Contains(strRole));
return View(nameuser.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
}
}
//var userList = user.OrderBy(u => u.User.Id).ToList();
{
var pageNumber = page ?? 1;
var pageSize = 25;
var userList = user.OrderBy(u => u.User.CompanyName).ToList();
return View(userList.ToPagedList(pageNumber, pageSize));
}
}
The controller is where I believe the issue is. If I leave the .ToString() off of the Roles, its works perfect if I am just filtering by the role. But if I try to filter with anything else, it filters it to empty results. I'm thinking since the Roles is a list string, for some reason it's not able to properly add to the other strings. What's weird is with my current code, it will filter records by UserName and by Role at the same time, but the CompanyName and Role do not work in sync with each other.
I think the bit of logic you want is Enumerable.Any.
e.g. your Where clause for roles can be:
u.Roles.Any(role => role.Contains(strRole)))
this will return true if any of the u.Roles contain the specified strRole string.
Edit: Sidenote, if I've read this correctly, all of your code after the user declaration can be simplified to just:
public ActionResult ListUsers(/*...*/)
{
var user = /*...*/;
var pageNumber = page ?? 1;
var pageSize = 25;
IEnumerable<ApplicationUser> filteredUsers = user;
if (!String.IsNullOrEmpty(strComp)) {
filteredUsers = filteredUsers.Where(u => u.User.CompanyName.ToString().Contains(strComp));
}
if (!String.IsNullOrEmpty(strName)) {
filteredUsers = filteredUsers.Where(u => u.User.UserName.ToString().Contains(strName));
}
if (!String.IsNullOrEmpty(strRole)) {
filteredUsers = filteredUsers.Where(u => u.Roles.Any(role => role.Contains(strRole)));
}
return View(filteredUsers.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
}
I am trying to show a list of products and show pagination. So, I decided to use PagedList plugin. I read some examples of how to modify controller and view to do so in the link below.
https://github.com/TroyGoode/PagedList
But, instead of using ViewBag, I managed to send a group of products to controller by doing some modifications to controller:
public ActionResult Index(string name)
{
IEnumerable<ProductModel> productList = new HashSet<ProductModel>();
if (string.IsNullOrWhiteSpace(name))
{
}
else
{
CategoryModel category = db.Categories.Where(x => x.CategorytUrl == name).FirstOrDefault();
if (category != null)
{
int catId = category.Id;
string catName = category.Name;
ViewBag.categoryName = category.Name;
List<SpecItemsModel> options = Products.GetFilter(category.Id);
//initialize the list
//productList = Products.ProductLists(catId);
ViewBag.filters = options;
var childrenIDs = db.Categories.Where(x => x.ParentId == category.Id).Select(x => x.Id);
var grandchildrenIDs = db.Categories.Where(x => childrenIDs.Contains((int)x.ParentId)).Select(x => x.Id);
List<int> catIds = new List<int>();
catIds.Add(category.Id);
catIds.AddRange(childrenIDs);
catIds.AddRange(grandchildrenIDs);
var templates = db.ProductTemplates.Where(t => catIds.Contains(t.CategoryId)).Select(x => x.Id);
productList = db.Products.Where(p => templates.Contains(p.ProductTemplateId));
}
}
var pageNumber = page ?? 1;
//var onePageOfProducts = productList.OrderBy(i => i.AdminRate).ToPagedList(pageNumber, 2);
//ViewBag.OnePageOfProducts = onePageOfProducts;
return View(productList.OrderBy(i => i.AdminRate).ToPagedList(pageNumber, 2));
}
As the result, products are showing up in he view in 2-product groups. the only thing I don't know is how to modify html pager of the example I mentioned to show paging controls. :
#model IEnumerable<fardashahr.Models.ProductModel>
<div class="col-md-9">
#foreach (var item in Model)
{
<a href="#Url.Action("Details","Home",new { name = item.ProductUrl })">
<img class="bd-placeholder-img card-img-top mw-100" src="/Images/Uploads/Products/#item.Id/Thumbs/#item.ImageName" />
</a>
<p class="card-text">
<h3>
#item.ShortName
</h3>
</p>
}
</div>
</div>
Any suggestions?
Sorry, I didn't check the github project you posted.
In Controller
Change
public ActionResult Index(string name)
to
public ActionResult Index(int page, string name)
In View
Use
#Html.PagedListPager( (IPagedList)Model, page => Url.Action("Index", new { page = page, name = "***" }) )
to show page navigator.
I am trying to use an ActionLink to pass an instance of a ViewModel from my view to a controller. However, the ViewModel is always null when it reaches the controller.
ActionLink:
#Html.ActionLink("Begin Test", "TakeTest", new { vm = Model }, new { #class = "btn btn-default" })
Controller:
public ActionResult TakeTest(QuizViewModel vm) {
ViewBag.Title = vm.vmTest.TestName;
string userId = User.Identity.GetUserId();
vm.Student = db.Users.SingleOrDefault(s => s.Id == userId);
vm.Attempt = db.Attempts.SingleOrDefault(a => a.Student == vm.Student && a.Test == vm.vmTest);
if (vm.vmTest.AttemptsAllowed > 0 && vm.Attempt.Count >= vm.vmTest.AttemptsAllowed) {
return View();
}
vm.Attempt.Count++;
if (vm.QuestionList == null) {
vm.QuestionList = db.Questions.Where(q => q.TID == vm.vmTest.ID).ToList();
}
Random r = new Random();
int randQid = r.Next(0, vm.QuestionList.Count - 1);
vm.ShowQuestion = vm.QuestionList[randQid];
vm.QuestionList.Remove(vm.ShowQuestion);
vm.Asets = db.AnswerSets.Where(a => a.Question == vm.ShowQuestion).ToList();
return View(vm);
}
I'm trying to set the selected value for a dropdown menu in my controller and pass it to my view via ViewBag. When I debug, all the correct values are being passed to the controller and to the view as well, but when it actually renders on the page, the selected value is not set.
View
<tr class="form-group">
<th>
<label for="CompanyID" class="control-label col-md-9">Company</label>
</th>
<td>
#Html.DropDownList("CompanyID", (IEnumerable<SelectListItem>)ViewBag.CompanyID, "", new { #class = "required-validation" })
#Html.ValidationMessageFor(model => model.CompanyID)
</td>
Controller
public ActionResult Edit(int? id) {
if (id == null) {
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var sale = db.SalesOpportunities.Find(id);
if (sale == null) {
return HttpNotFound();
}
ViewBag.CompanyID = new SelectList(db.Companies, "CompanyID", "Name", sale.CompanyID);
var technicians = db.UserProfiles.Select(t => new {
ID = t.ID,
Name = t.FirstName + " " + t.LastName,
}).OrderBy(t => t.Name);
ViewBag.UserID = new SelectList(technicians, "ID", "Name", sale.UserID);
return View(sale);
}
The name of the field and the name of the variable in ViewBag for your choices cannot be the same. Change it from ViewBag.CompanyID to something like ViewBag.CompanyChoices and it will work.
I'm implementing a simple paged list Index using the example at http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
My problem is that the search string is 'lost' when I page to the second page, so instead of a filtered set of results, I'm shown all the records.
My index.cshtml:
#using (Html.BeginForm("Index", "", FormMethod.Get))
{
<p>
#Html.TextBox("searchString", ViewBag.currentFilter as string, new { #placeholder = "Search by title or author" })
<input type="submit" value="Search" />
</p>
}
#if (Model.PageCount > 1)
{
#Html.PagedListPager( Model, page => Url.Action("Index", new { page }) )
}
My controller:
public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
{
ViewBag.TitleSortParm = sortOrder == "Title" ? "Title desc" : "Title";
ViewBag.AuthorSortParm = sortOrder == "Author" ? "Author desc" : "Author";
ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date";
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
ViewBag.currentFilter = searchString;
var Articles = from a in db.Articles
select a;
if (!String.IsNullOrEmpty(searchString))
{
//page = 1;
Insights = Articles.Where(s => s.Title.ToUpper().Contains(searchString.ToUpper())
|| s.Author.ToUpper().Contains(searchString.ToUpper()));
}
switch (sortOrder)
{
case "Author":
Insights = Articles.OrderBy(s => s.Author);
break;
case "Author desc":
Insights = Articles.OrderByDescending(s => s.Author);
break;
case "Title":
Insights = Articles.OrderBy(s => s.Title);
break;
case "Title desc":
Insights = Articles.OrderByDescending(s => s.Title);
break;
case "Date":
Insights = Articles.OrderBy(s => s.DatePublished);
break;
default:
Insights = Articles.OrderByDescending(s => s.DatePublished);
break;
}
int pageSize = 3;
int pageNumber = (page ?? 1);
return View(Articles.ToPagedList(pageNumber, pageSize));
}
When I go to Page 2 as an example, all my variables, sortOrder, currentFilter and searchString are all null.
Robbie
The problem is your PagedList entry doesn't include your sort order nor your current filter.
In addition to adding ViewBag.CurrentSort as suggested by Vasanth, you also need to change your PagedListPager to:
#Html.PagedListPager( Model, page => Url.Action("Index", new { page, currentFilter=ViewBag.CurrentFilter, sortOrder = ViewBag.sortOrder}) )
Hello I figured this out, Use a Tempdata to hold the search parameters. When the search method is called with some values, store the values in a tempdata. When the page list calls the method for page 2, collect the Search parameters from the TempData.
See this:
if (SearchParameter != null)
{
TempData["HoldSearch"] = SearchParameter;
TempData.Keep();
}
else
{
SearchParameter = (CastBacktoType)TempData["HoldSearch"];
TempData.Keep();
}
Have tried this, it works well
If you have a complex search/filtering section with more than one field, you might need to use something like :
<div class="pagedList">
#Html.PagedListPager(Model, page => Url.Action("Index", new {
page, sortOrder = ViewBag.CurrentSort,
currentFilter = ViewBag.CurrentFilter,
filter2= Request.QueryString["filter2"],
filter3= Request.QueryString["filter3"],
filter4= Request.QueryString["filter4"],
filter5= Request.QueryString["filter5"] }))
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
</div>
This worked for me and now I can use complex filtering and view the results on multiple pages.
To filter the data by applying multiple filters we can use ViewBag like this in View.
For example if we want to apply filter for three fields(i.e EmpFirstName, EmpLastName, EmpLocation).
#Html.PagedListPager(Model, page => Url.Action("Index", new {
page,
sortOrder = ViewBag.sortOrder,
currentFilter1 =ViewBag.CurrentFilterForEmpFirstName,
currentFilter2 =ViewBag.CurrentFilterForEmpLastName,
currentFilter3 =ViewBag.CurrentFilterForEmpLocation}))
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
In controller i wroted code like this:
public ActionResult Index( int ?page, string currentFilter1, string currentFilter2, string currentFilter3,
string searchEmpFirstName , string searchEmpLastName, string searchEmpLocation)
int pageSize = 10;
int pageNumber = (page??1);
var emp_master = db.Emp_Master.Include(l => l.Emp_Location);
if (searchEmpFirstName != null || searchEmpLastName != null || searchEmpLocation != null)
{
page = 1;
}
else
{
searchEmpFirstName = currentFilter1;
searchEmpLastName = currentFilter2;
searchEmpLocation = currentFilter3;
}
ViewBag.CurrentFilterForEmpFirstName = searchEmpFirstName;
ViewBag.CurrentFilterForEmpLastName = searchEmpLastName;
ViewBag.CurrentFilterForEmpLocation = searchEmpLocation;
if(!String.IsNullOrEmpty(searchEmpFirstName))
{
emp = emp.Where(s => s.ModelName == searchEmpFirstName)
}
if(!String.IsNullOrEmpty(searchEmpLastName))
{
emp = emp.Where(s => s.ModelName == searchEmpLastName)
}
if(!String.IsNullOrEmpty(searchEmpLocation))
{
emp = emp.Where(s => s.ModelName == searchEmpLocation)
}