I want to show a list of climbs, and the user can then select the climbs.
I do it like this:
public ActionResult GetClimbs(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
UserProfile user = db.userProfiles
.Include(i => i.Climbs)
.Where(i => i.Id == id)
.Single();
PopulateAssignedClimbData(user);
if (user == null)
{
return HttpNotFound();
}
return View(user);
}
private void PopulateAssignedClimbData(UserProfile user)
{
var allClimbs = db.Climbs;
var userClimbs = new HashSet<int>(user.Climbs.Select(c => c.climbID));
var viewModel = new List<AssignedClimb>();
foreach (var climb in allClimbs)
{
viewModel.Add(new AssignedClimb
{
ClimbID = climb.climbID,
Title = climb.Name,
Assigned = userClimbs.Contains(climb.climbID)
});
}
ViewBag.Climbs = viewModel;
}
and for the view I have this:
#model ContosoUniversity.Models.UserProfile
#using ContosoUniversity.Source
#{
ViewBag.Title = "Edit";
}
#using (Html.BeginForm("GetClimbs", "Account", FormMethod.Post, new { id = "form_Id", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.HiddenFor(model => model.Id)
<table>
<tr>
#{
int cnt = 0;
List<ContosoUniversity.ViewModels.AssignedClimb> climbs = ViewBag.Climbs;
foreach (var climb in climbs)
{
if (cnt++ % 3 == 0)
{
#:</tr><tr>
}
#:<td>
<input type="checkbox"
name="selectedClimbs"
value="#climb.ClimbID"
#(Html.Raw(climb.Assigned ? "checked=\"checked\"" : ""))/>
#climb.ClimbID #: #climb.Title
#:</td>
}
#:</tr>
}
</table>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
But every time I get the message by this line:
foreach (var climb in climbs)
that climbs is null although I have some climbs in the database.
It also doesn't enter the GetClimbs method in the controller.
So the view is a tab, but how to trigger from the tab the action method: PopulateAssignedClimbData(user); this method is in the Account Controller
Thank you
this is my edit with all the tabs:
<div id="tabs">
<ul>
<li>Personal information</li>
<li>Profile Photo</li>
<li>Other Photos</li>
<li>Climbs</li>
</ul>
<div id="tabs-1">
#Html.Partial("_GetUserProfile", Model)
</div>
<div id="tabs-2">
#Html.Partial("_GetProfilePicture", Model)
</div>
<div id="tabs-3">
#Html.Partial("_GetOtherImages", Model)
</div>
<div id="tabs-4">
#Html.Partial("_GetClimbTab", Model)
</div>
</div>
this is doing the job:
$.get('#Url.Action("PopulateAssignedClimbData", "Account", new { id = Model.Id })', function (data) {
$('#_GetClimbTab').html(data);
});
Related
I have in one view two submit buttons
The First one search for users in Active directory
The Second one Add selected user to table AspNetUsers
I have specified username which is staff id in button attribute asp-route-id so that I can add that specific user from the list of users that will appear after clicking the search button. but the problem is that it add the first person in the list. it doesn't add the one I clicked on.
This is my controller
[AcceptVerbs("Get", "Post")]
public async Task<IActionResult> AddUser(SearchViewModel profile , string button, List<User> users )
{
if (button == "Search")
{
if (ModelState.IsValid)
{
users = new List<User>();
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "mydomain.com"))
{
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.DisplayName = profile.Name + "*";
using (PrincipalSearcher srch = new PrincipalSearcher(qbeUser))
{
if (!string.IsNullOrEmpty(srch.FindAll().ToString()))
{
foreach (var found in srch.FindAll())
{
if (found != null)
{
users.Add(new User()
{
Name = found.Name,
Email = found.UserPrincipalName,
SatffID = found.SamAccountName
});
}
else
{
return View();
}
}
SearchViewModel returnmodel = new SearchViewModel(users);
return View(returnmodel);
}
}
}
}
}
if(button=="Add")
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = profile.ID, Email = profile.Email, DisplayName = profile.DisplayName };
var result = await userManager.CreateAsync(user);
if (result.Succeeded)
{
if(profile.Location !=null)
{
for (int i = 0; i < profile.Location.Count; i++)
{
var newUser = await userManager.FindByNameAsync(profile.ID);
var userId = newUser.Id;
//var newUser = profile.ID;
UserLocation userLoc = new UserLocation
{
UserID = userId.ToString(),
LocID = profile.Location[i]
};
userLocation.Add(userLoc);
}
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError(string.Empty, "No locs");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
ModelState.AddModelError(string.Empty, "");
}
return View(profile);
}
return View(profile);
}
This is my View AddUser
#model SearchViewModel
<h1>Add New User</h1>
#Html.ValidationSummary(true)
<form method="post" formaction="">
<div id="content">
<fieldset>
<div class="form-group col-md-12">
#Html.LabelFor(model => Model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.EditorFor(modelItem => Model.Name, new { htmlAttributes = new { #class = "form-control", #style = "width:280px" }, })
</div>
<div>
<div class="form-group row">
<label asp-for="#Model.Location" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<select asp-for="#Model.Location" asp-items="Html.GetEnumSelectList<Location>()" class="custom-select mr-sm-2" id="Subjects_dropdown" multiple>
<option value="">Please Select</option>
</select>
<span asp-validation-for="#Model.Location" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-2">
<input type="submit" class="btn btn-default" name="button" value="Search">
</div>
<div class="col-md-3">
</div>
</div>
</fieldset>
<br>
</div>
<table id="historyTable" class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Staff Id</th>
<th>Add User</th>
</tr>
</thead>
<tbody>
#if (Model.FoundUsers != null)
{
#foreach (var user in Model.FoundUsers)
{
if (user != null)
{
<tr>
<td><label asp-for="DisplayName"></label><input asp-for="DisplayName" value="#user.Name" name="displayname" /></td>
<td><label asp-for="Email"></label><input asp-for="Email" value="#user.Email" name="Email" /></td>
<td><label asp-for="ID"></label><input asp-for="ID" value="#user.SatffID" name="ID" /></td>
<td><input type="submit" class="btn btn-primary" name="button" value="Add" asp-route-Id="#user.SatffID" asp-action="AddUser"></td>
</tr>
}
}
}
else
{
<tr>
<td colspan="4">No Record Available</td>
</tr>
}
</tbody>
</table>
</form>
}
#section Scripts{
<script>
$(document).ready(function () {
$('#Subjects_dropdown').multiselect();
});
</script>
}
I try to reproduce your issue in my side, and I found that if I click Add button, the request contains all rows data like screenshot below:
So I think the issue comes from the form submit, I tried to add form for each row, and it worked.
Here's my code snippet, just adding #using (Html.BeginForm()) for content。
Here's a similar question as yours, and you can also refer to it to write js script to achieve it.
My controller action:
[AcceptVerbs("Get", "Post")]
public IActionResult AddUser(SearchViewModel profile, string button, List<User> users)
{
ViewData["Location"] = new List<string> {
"location_a",
"location_b"
};
if (button == "Search")
{
if (ModelState.IsValid)
{
users = new List<User>();
users.Add(new User()
{
Name = "name_a",
Email = "email_a",
SatffID = "staff_a"
});
users.Add(
new User()
{
Name = "name_b",
Email = "email_b",
SatffID = "staff_b"
});
users.Add(
new User()
{
Name = "name_c",
Email = "email_c",
SatffID = "staff_c"
});
SearchViewModel returnmodel = new SearchViewModel();
returnmodel.FoundUsers = users;
return View(returnmodel);
}
}
if (button == "Add")
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = profile.ID, Email = profile.Email, DisplayName = profile.DisplayName };
//save data
return RedirectToAction("Index", "Home");
}
return View(profile);
}
return View(profile);
}
View code :
#model SearchViewModel
<h1>Add New User</h1>
#Html.ValidationSummary(true)
<form method="post" formaction="">
<div id="content">
<fieldset>
<div class="form-group col-md-12">
#Html.LabelFor(model => Model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.EditorFor(modelItem => Model.Name, new { htmlAttributes = new { #class = "form-control", #style = "width:280px" }, })
</div>
<div>
<div class="form-group row">
<label asp-for="#Model.Location" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<select asp-for="#Model.Location" asp-items="(#ViewData["Location"] as IEnumerable<SelectListItem>)" class="custom-select mr-sm-2" id="Subjects_dropdown" multiple>
<option value="">Please Select</option>
</select>
<span asp-validation-for="#Model.Location" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-2">
<input type="submit" class="btn btn-default" name="button" value="Search">
</div>
<div class="col-md-3">
</div>
</div>
</fieldset>
<br>
</div>
<table id="historyTable" class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Staff Id</th>
<th>Add User</th>
</tr>
</thead>
<tbody>
#if (Model.FoundUsers != null)
{
#foreach (var user in Model.FoundUsers)
{
if (user != null)
{
<tr>
#using (Html.BeginForm())
{
<td><label asp-for="DisplayName"></label><input asp-for="DisplayName" value="#user.Name" name="displayname" /></td>
<td><label asp-for="Email"></label><input asp-for="Email" value="#user.Email" name="Email" /></td>
<td><label asp-for="ID"></label><input asp-for="ID" value="#user.SatffID" name="ID" /></td>
<td><input type="submit" class="btn btn-primary" name="button" value="Add" asp-route-Id="#user.SatffID" asp-action="AddUser"></td>
}
</tr>
}
}
}
else
{
<tr>
<td colspan="4">No Record Available</td>
</tr>
}
</tbody>
</table>
</form>
#section Scripts{
<script>
$(document).ready(function () {
$('#Subjects_dropdown').multiselect();
});
</script>
}
This is what related code I only added
I have a button which add individualSearch partial view and individualSearch partial view also have a add button which adds individualSearcharacteristic partial view in it.
I want to bind BMRTestData model with individualSearch partial so that i can get the characteristic partial view data. So i store that data in IndividualSearch's list public List<Characteristic> Characteristics { get; set; } = new List<Characteristic>();
Please guide me to do same as i am new to .net .
Coding
//TestData(Main View)
#using ABC.Core.Models.DTOs
#model ABC.Core.Models.Api.BMRTestData
#using (Html.BeginForm())
{
<div class="card mb-3">
<h5 class="card-header">Response</h5>
<div class="card-body">
<div class="card-block">
<div class="form-group">
#Html.LabelFor(m => m.CompanyName, "Company Name", new { #class = "form-control-label" })
#Html.TextBoxFor(m => m.CompanyName, null, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.CompanyName)
</div>
<div id="searchindividuals" class="mb-3">
#if (Model?.IndividualSearches != null)
{
for (var i = 0; i < Model?.IndividualSearches.Count; i++)
{
<div class="form-group">
#{ Html.RenderPartial("IndividualSearchPartial", Model.IndividualSearches[i], new ViewDataDictionary()); }
</div>
}
}
</div>
<div class="mb-3">
<button id="add-search-individual" type="button" class="btn btn-success">Add Search Individual</button>
</div>
<button id="add-company-characteristic" type="button" class="btn btn-success">Add Characteristic</button>
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
}
#section Scripts
{
function add(element){
var action = ' #Url.Action("NewIndividualSearchCharacteristic", "Blended")';
$.post(action)
.done(function (partialView) {
$(element.previousElementSibling).append(partialView);
});
}
</script>
}
//IndividualSearchPartial
#using (Html.BeginCollectionItem("IndividualSearches"))
{
<div id="individual-details" class="card">
<div class="form-horizontal">
<div class="card-block">
<div class="form-group">
#Html.LabelFor(m => m.SearchPostcode, "Search Post Code", new { #class = "form-control-label" })
#Html.TextBoxFor(m => m.SearchPostcode, null, new { #class = "form-control" })
</div>
</div>
</div>
<div class="card-block">
<div id="Characteristics" class="mb-3">
#if (Model?.Characteristics != null)
{
for (var i = 0; i < Model?.Characteristics.Count; i++)
{
<div class="form-group">
#{ Html.RenderPartial("IndividualSearchCharacterisiticPartial", Model.Characteristics[i], new ViewDataDictionary()); }
#* #Html.EditorFor(m => m.Characteristics);*#
</div>
}
}
</div>
<button id="add-characteristics" onclick="add(this)" type="button" class="btn btn-success">Add Characteristics</button>
</div>
</div>
}
// IndividualSearchCharacterisiticPartial
#model ABC.Core.Models.DTOs.Characteristic
#using (Html.BeginCollectionItem("Characteristics"))
{
<div id="characteristic-details" class="card">
<div class="form-horizontal">
<div class="card-block">
<div class="container">
<div class="row">
<div class="col-*-*">
#Html.LabelFor(m => m.Name, "Name", new { #class = "form-control-label" })
</div>
<div class="col">
#Html.TextBoxFor(m => m.Name, null, new { #class = "form-control" })
</div>
<div class="col-*-*">
#Html.LabelFor(m => m.Value, "Value", new { #class = "form-control-label" })
</div>
<div class="col">
#Html.TextBoxFor(m => m.Value, null, new { #class = "form-control" })
</div>
<div class="col-*-*">
<a id="characteristic-remove" href="#" onclick="removeCharacteristic(this)" class="btn btn-danger pull-right">Remove</a>
</div>
</div>
</div>
</div>
</div>
</div>
}
//IndividualSearch Class
namespace ABC.Core.Models.DTOs.Individual
{
public class IndividualSearch
{
public List<Characteristic> Characteristics { get; set; } = new List<Characteristic>();
}
}
namespace ABC.Core.Models.Api
{
public class BMRTestData : BMRRequest
{
public List<IndividualSearch> IndividualSearches { get; set; } = new List<IndividualSearch>();
}
}
Update
You can add onclick event in Add Search Individual button:
<button id="add-search-individual" type="button" class="btn btn-success" onclick="addSearch(this)">Add Search Individual</button>
Add an action in controller to return IndividualSearchPartial partial view:
[HttpPost]
public ActionResult IndividualSearchCharacteristic()
{
IndividualSearch individualSearch = new IndividualSearch() { };
return PartialView("IndividualSearchPartial", individualSearch);
}
Here is all the js in main view:
#section Scripts
{
<script>
function add(element){
var action = ' #Url.Action("NewIndividualSearchCharacteristic", "Default")';
$.post(action)
.done(function (partialView) {
$(element).parents('#individual-details').find("#Characteristics").append('<div class="form-group">' + partialView + '</div>');
ResetName();
});
}
function addSearch(element){
var action = ' #Url.Action("IndividualSearchCharacteristic", "Default")';
$.post(action)
.done(function (partialView) {
$(element).parents('.mb-3').find('#searchindividuals').append('<div class="form-group search">' + partialView + '</div>');
ResetName();
});
}
function ResetName() {
var index = 0;
$(".search").each(function () {
var nameIndex = 0; var valueIndex = 0;
$(this).find(":input[type='hidden']").each(function () {
$(this).removeAttr("name");
});
$(this).find(":input[type='text']").each(function () {
if ($(this).attr("name").indexOf("Characteristics") > -1 && $(this).attr("name").indexOf("Name") > -1) {
$(this).attr("name", "IndividualSearches[" + index + "].Characteristics[" + nameIndex + "].Name");
nameIndex++;
return;
}
if ($(this).attr("name").indexOf("Characteristics") > -1 && $(this).attr("name").indexOf("Value") > -1) {
$(this).attr("name", "IndividualSearches[" + index + "].Characteristics[" + valueIndex + "].Value");
valueIndex++;
return ;
}
if ($(this).attr("name").indexOf("IndividualSearches") > -1) {
$(this).attr("name", "IndividualSearches[" + index + "].SearchPostcode");
return;
}
});
index++;
})
}
</script>
}
After submit, it will enter into following action to receive BMRTestData data:
[HttpPost]
public IActionResult TestData(BMRTestData bMRTest)
{
return View();
}
Here is the test result:
My Get function works fine and the search textbox shows but when I enter the user ID and click search, it goes directly to the post function. It is supposed to go to the Get function again to show the data . after the data shows and whether I selected from the checkboxes or not, I click save and then it is supposed to go to the POst function.
What am I doing wrong?
GET function :
[HttpGet]
public ActionResult Index(int? SearchId)
{
var viewModel = new UserViewModel();
if (SearchId != null)
{
var userDepartments = db.TBL_User_Dep_Access.Where(x => x.UserID == SearchId).Select(x => x.Dep_ID).ToList();
List<UserDepartmentViewModel> udeptVM = db.TBL_Department.Select(i => new UserDepartmentViewModel
{
Dep_Id = i.Department_ID,
Dep_Name = i.Department_Name,
IsChecked_ = userDepartments.Contains(i.Department_ID)
}).ToList();
var userPermissions = db.TBL_UserPermissions.Where(x => x.UserID == SearchId).Select(m => m.PermissionID).ToList();
List<UsrPERViewModel> upVM = db.TBL_Permissions.Select(i => new UsrPERViewModel
{
Id = i.PermissionID,
Name = i.PermissionName,
IsChecked = userPermissions.Contains(i.PermissionID)
}).ToList();
viewModel.Departments = udeptVM;
viewModel.Permissions = upVM;
}
return View(viewModel);
}
My View:
#model Staff_Requisition.Models.UserViewModel
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<style>
.list-group {
max-height: 300px;
margin-bottom: 10px;
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
</style>
#using (Html.BeginForm("Index", "TBL_UserPermission"))
{
#Html.AntiForgeryToken()
<body class="nav-md">
<div class="container body">
<div class="main_container">
<div class="title_right">
<div class="col-md-5 col-sm-5 col-xs-12 form-group pull-right top_search">
<div class="input-group">
#Html.TextBox("SearchId", "", null, new { #id = "SearchId", #placeholder = "Search for...", #class = "form-control" })
<span class="input-group-btn">
<input class="btn btn-default" value="Search" type="submit">Go! />
</span>
<ul>
#if (Model.Permissions != null)
{
foreach (var P in Model.Permissions)
{
<li>
<p>
#Html.CheckBoxFor(modelItem => P.IsChecked, new { #class = "flat", #value = P.IsChecked })
#Html.DisplayFor(modelItem => P.Name, new { #class = "DepartmentName", #value = P.Name })
#Html.HiddenFor(modelItem => P.Id, new { #class = "Dep_Id", #value = P.Id })
</p>
</li>
}
}
</ul>
<ul class="to_do">
#if (Model.Departments != null)
{
foreach (var D in Model.Departments)
{
<li>
<p>
#Html.CheckBoxFor(modelItem => D.IsChecked_, new { #class = "flat", #value = D.IsChecked_ })
#Html.DisplayFor(modelItem => D.Dep_Name, new { #class = "DepartmentName", #value = D.Dep_Name })
#Html.HiddenFor(modelItem => D.Dep_Id, new { #class = "Dep_Id", #value = D.Dep_Id })
</p>
</li>
}
}
</ul>
<div class="col-xs-12 col-sm-6 emphasis">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</body>
}
My POST function:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(UserViewModel user_Pers)
{
//remove user with specified ID from database
db.TBL_UserPermissions.RemoveRange(db.TBL_UserPermissions.Where(c => c.UserID == user_Pers.SearchId));
db.TBL_User_Dep_Access.RemoveRange(db.TBL_User_Dep_Access.Where(c => c.UserID == user_Pers.SearchId));
//for each permission that's checked add user to the table
foreach (var u in user_Pers.Permissions)
{
if (u.IsChecked)
{
TBL_UserPermissions Tup = new TBL_UserPermissions();
Tup.UserID = user_Pers.SearchId;
Tup.PermissionID = u.Id;
Tup.IsActive = true;
db.TBL_UserPermissions.Add(Tup);
}
}
db.SaveChanges();
foreach (var d in user_Pers.Departments)
{
if (d.IsChecked_)
{
TBL_User_Dep_Access Tud = new TBL_User_Dep_Access();
Tud.UserID = user_Pers.SearchId;
Tud.Dep_ID = d.Dep_Id;
Tud.IsActive = true;
db.TBL_User_Dep_Access.Add(Tud);
}
}
db.SaveChanges();
return RedirectToAction("myInfo");
}
BTW I removed most of the div in the view manually for simplicity, so it's okay if an opening or closing doesn't match.
As has been pointed out in the comments, in your code you have 1 form whereas to solve the problem you are talking about you need 2 forms. One form responsible for the search get request and the other responsible for the user post.
Here is a simple example of a search form and an update form on the same page.
The viewmodel and controller
using System.Web.Mvc;
namespace SearchAndSubmit.Controllers
{
public class UserViewModel
{
public int? Id { get; set; }
public string Name { get; set; }
}
public class HomeController : Controller
{
[HttpGet]
public ActionResult Edit(int? SearchId)
{
var viewModel = new UserViewModel();
if (SearchId != null)
{
viewModel.Id = SearchId;
//logic to search for user and create viewmodel goes here
}
return View(viewModel);
}
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult Edit(UserViewModel user_Pers)
{
//validation and create update logic goes here
return RedirectToAction("Index");
}
}
}
The view
#model SearchAndSubmit.Controllers.UserViewModel
#{
ViewBag.Title = "Edit/create user";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>#ViewBag.Title</h2>
#*This is the search form it does a get request*#
#using (Html.BeginForm("edit", "home", FormMethod.Get))
{
#Html.TextBox("SearchId", "", null, new { #id = "SearchId", #placeholder = "Search for...", #class = "form-control" })
<span>
<input value="Search" type="submit">
</span>
}
#*This is the form for updating the user it does a post*#
#using (Html.BeginForm("edit", "home", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(c => c.Id)
<p>
#Html.LabelFor(c => c.Name)
#Html.TextBoxFor(c => c.Name)
</p>
<div>
<input type="submit" value="Save" />
</div>
}
I've been struggling for far too long with this now, and I think I've finally found where the problem is!
I am making a review section in an Asp.Net Core web app, I have added 2 drop downs that filter reviews by product, and set the number of reviews per page.
For the paged list I am using Sakura.AspNetCore.PagedList.
I am trying to use ajax to return the partial view which has the filtered and sorted reviews, and all goes well, until the model is passed back. At first I couldn't figure it out, then using chrome, I found a 500 error, and from there found the following error in the resonse:
InvalidOperationException: The model item passed into the ViewDataDictionary is of Microsoft.AspNetCore.Mvc.PartialViewResult but this ViewDataDictionary instance requires a model item of type Sakura.AspNetCore.IPagedList
I can't for the life of me figure out how to fix this, the model although a pagedlist is a partialView... here's the offending part of the code in my model:
public async Task<ActionResult> ShowReviewDetails(string searchProduct, int? page, string perPage)
{
// get product via id
var prodId = Convert.ToInt32(searchProduct);
var prod = await _context.Product.FindAsync(prodId);
searchProduct = prod.ProductName;
if (perPage == "0")
{
perPage = _context.Product.Count().ToString();
}
var perPageGet = Convert.ToInt32(perPage);
if (perPageGet <= 0)
{
perPageGet = _context.Product.Count();
}
int pageSize = Convert.ToInt32(perPageGet);
int pageNumber = (page ?? 1);
IEnumerable<Review> reviews = await _context.Review.Where(r => r.ReviewApproved == true).ToListAsync();
if (!string.IsNullOrWhiteSpace(searchProduct) || !string.IsNullOrEmpty(searchProduct))
{
searchProduct = StringExtensions.UppercaseFirst(searchProduct);
}
if (!string.IsNullOrEmpty(searchProduct) || !string.IsNullOrWhiteSpace(searchProduct) || searchProduct == "0")
{
page = 1;
reviews = await _context.Review.Where(r => r.Product == searchProduct && r.ReviewApproved == true).ToListAsync();
}
if (searchProduct == "All" || string.IsNullOrEmpty(searchProduct))
{
reviews = await _context.Review.Where(r => r.ReviewApproved == true).ToListAsync();
}
reviews = reviews.ToPagedList(pageSize, pageNumber);
return PartialView(reviews);
I'm still fairly green when it comes to asp.net core and c#, so any help or suggestions would be welcomed, maybe there is a better option for paging?
Thanks for your time!
EDIT: added views and script
My partial view parent:
#{
ViewBag.Title = "Review Dashboard";
#using YaCu_2017.Controllers;
}
<p class="green-text">#ViewBag.StatusMessage</p>
<p class="red-text">#ViewBag.ErrorMessage</p>
<h2>Our Product Reviews</h2>
<div class="reviewView" id="filter">
#await Html.PartialAsync("ShowReviewDetails")
</div>
The actual partialview:
#model IPagedList<YaCu_2017.Models.Review>
#using System.Globalization
#using Sakura.AspNetCore
#using YaCu_2017.Controllers
#using YaCu_2017.Models
#{
ViewData["Title"] = "Digital Jeeves - Reviews";
}
<div class="row">
<div class="col s2">
<h5>Filter by Product:</h5>
<form method="get" >
#{
var product = ReviewController.GetProductListIncId();
var productCount = ReviewController.GetProductCountList();
ViewBag.ProductList = product;
ViewBag.ProductCount = productCount;
}
<select asp-items="#ViewBag.ProductList" id="searchProduct" class="dropdown-button btn"></select>
<h5>Reviews per page</h5>
<select asp-items="#ViewBag.ProductCount" id="perPage" class="dropdown-button btn"></select>
</form>
</div>
</div>
<div class="row">
<div class="col s12 center center-align center-block">
<p>Page #(Model.TotalPage < Model.PageIndex ? 1 : Model.PageIndex) of #Model.TotalPage<pager class="pagination" setting-link-attr-data-ajax="true" /></></p>
</div>
</div>
<hr />
<div>
#foreach (var item in Model)
{
var stars = Convert.ToDouble(item.Stars);
<div class="container opaque-parent z-depth-5">
<div class="row">
<div class="col s6"><h6 style="border-bottom:thin">Title : #Html.DisplayFor(model => item.Title)</h6></div>
<div class="col s3"><h6 style="border-bottom:thin">Product : #Html.DisplayFor(model => item.Product)</h6></div>
<div class="col s3"><h6 style="border-bottom:thin">Rated: <ej-rating value="#stars" id="#item.Id" read-only="true" /></h6></div>
</div>
<div class="row" style="">
<div class="col s12" style="border-bottom:inset">
<h6>Comment:</h6>
</div>
</div>
<div class="row" style="border-bottom:inset">
<div class="col s6 offset-s3">
<p class="flow-text">"#Html.DisplayFor(model => item.ReviewText)"</p>
</div>
</div>
<div class="row">
<div class="col s3">
<p>Date Created : #Html.DisplayFor(modelItem => item.CreatedDate)</p>
</div>
<div class="col s3">
<p>Chosen Display Name: #Html.DisplayFor(modelItem => item.DisplayName)</p>
</div>
</div>
</div>
<hr />
}
</div>
<div class="row">
<div class="col s12 center center-align center-block">
<p>Page #(Model.TotalPage < Model.PageIndex ? 1 : Model.PageIndex) of #Model.TotalPage<pager class="pagination" setting-link-attr-data-ajax="true" /></></p>
</div>
</div>
and my document ready function:
$("#searchProduct").change(function () {
var product = $("#searchProduct").val();
var perPage = $("#perPage").val();
$("#filter").load('http://LocalHost:50426/Review/GetProducts?searchProduct=' + product + '&perPage=' + perPage);
});
$("#perPage").change(function () {
var product = $("#searchProduct").val();
var perPage = $("#perPage").val();
$("#filter").load('http://LocalHost:50426/Review/GetProducts?searchProduct=' + product + '&perPage=' + perPage);
});
The answer was stupidly simple, I kicked my self so hard I won't be sitting down for a week!
I just needed to return partialView(GetReviewDetails) as IPagedList.
For the sake of completness (Is that even a word?) here is everything as it ended up!
Views:
Modified index (Parent) as I was duplicating an entire page lol:
#model Sakura.AspNetCore.IPagedList<YaCu_2017.Models.Review>
#{
ViewBag.Title = "Review Dashboard";
#using YaCu_2017.Controllers;
}
<p class="green-text">#ViewBag.StatusMessage</p>
<p class="red-text">#ViewBag.ErrorMessage</p>
<h2>Our Product Reviews</h2>
<div class="row">
<div class="col s2">
<h5>Filter by Product:</h5>
<form method="get">
#{
var product = ReviewController.GetProductListIncId();
var productCount = ReviewController.GetProductCountList();
ViewBag.ProductList = product;
ViewBag.ProductCount = productCount;
}
<select asp-items="#ViewBag.ProductList" id="searchProduct" class="dropdown-button btn"></select>
<h5>Reviews per page</h5>
<select asp-items="#ViewBag.ProductCount" id="perPage" class="dropdown-button btn"></select>
</form>
</div>
</div>
<div class="row">
<div class="col s12 center center-align center-block">
<p>Page #(Model.TotalPage < Model.PageIndex ? 1 : Model.PageIndex) of #Model.TotalPage<pager class="pagination" setting-link-attr-data-ajax="true" /></></p>
</div>
</div>
<hr />
<div>
<div class="reviewView" id="filter">
#await Html.PartialAsync("ShowReviewDetails", Model)
</div>
</div>
<div class="row">
<div class="col s12 center center-align center-block">
<p>Page #(Model.TotalPage < Model.PageIndex ? 1 : Model.PageIndex) of #Model.TotalPage<pager class="pagination" setting-link-attr-data-ajax="true" /></></p>
</div>
</div>
Modified ShowReviewDetails (Child / partial) only has the loop:
#model IPagedList<YaCu_2017.Models.Review>
#using System.Globalization
#using Sakura.AspNetCore
#using YaCu_2017.Controllers
#using YaCu_2017.Models
#{
ViewData["Title"] = "Digital Jeeves - Reviews";
}
#foreach (var item in Model)
{
var stars = Convert.ToDouble(item.Stars);
<div class="container opaque-parent z-depth-5">
<div class="row">
<div class="col s6"><h6 style="border-bottom:thin">Title : #Html.DisplayFor(model => item.Title)</h6></div>
<div class="col s3"><h6 style="border-bottom:thin">Product : #Html.DisplayFor(model => item.Product)</h6></div>
<div class="col s3"><h6 style="border-bottom:thin">Rated: <ej-rating value="#stars" id="#item.Id" read-only="true" /></h6></div>
</div>
<div class="row" style="">
<div class="col s12" style="border-bottom:inset">
<h6>Comment:</h6>
</div>
</div>
<div class="row" style="border-bottom:inset">
<div class="col s6 offset-s3">
<p class="flow-text">"#Html.DisplayFor(model => item.ReviewText)"</p>
</div>
</div>
<div class="row">
<div class="col s3">
<p>Date Created : #Html.DisplayFor(modelItem => item.CreatedDate)</p>
</div>
<div class="col s3">
<p>Chosen Display Name: #Html.DisplayFor(modelItem => item.DisplayName)</p>
</div>
</div>
</div>
<hr />
}
Now the controllers:
I have a GetProducts() controller, which is uses to load the partial via ajax and is where I needed to add as IPagedList:
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult> GetProducts(string searchProduct, int? page, string perPage)
{
var product = int.Parse(searchProduct);
var obj = await this.ShowReviewDetails(searchProduct, page, perPage) as IPagedList;
return PartialView("ShowReviewDetails", obj);
}
The index control:
public async Task<ActionResult> Index(Review model, string sortOrder, string searchString, string searchProduct, int? page, string perPage)
{
await ShowReviewDetails(model, sortOrder, searchString, searchProduct, page, perPage);
return View();
}
And finally ShowReviewDetails:
public async Task<ActionResult> ShowReviewDetails(string searchProduct, int? page, string perPage)
{
// get product via id
var prodId = Convert.ToInt32(searchProduct);
if (prodId > 0)
{
var dbProd = await _context.Product.FindAsync(prodId);
var prod = new Product()
{
Id = dbProd.Id,
ProductName = dbProd.ProductName,
Cost = dbProd.Cost,
ProductCategory = dbProd.ProductCategory,
ProductDescription = dbProd.ProductDescription,
};
searchProduct = prod.ProductName;
}
else
{
searchProduct = "All";
}
if (perPage == "0")
{
perPage = _context.Product.Count().ToString();
}
var perPageGet = Convert.ToInt32(perPage);
if (perPageGet <= 0)
{
perPageGet = _context.Product.Count();
}
int pageSize = Convert.ToInt32(perPageGet);
int pageNumber = (page ?? 1);
IEnumerable<Review> reviews = await _context.Review.Where(r => r.ReviewApproved == true).ToListAsync();
if (!string.IsNullOrWhiteSpace(searchProduct) || !string.IsNullOrEmpty(searchProduct))
{
searchProduct = StringExtensions.UppercaseFirst(searchProduct);
}
if (!string.IsNullOrEmpty(searchProduct) || !string.IsNullOrWhiteSpace(searchProduct) || searchProduct == "0")
{
page = 1;
reviews = await _context.Review.Where(r => r.Product == searchProduct && r.ReviewApproved == true).ToListAsync();
}
if (searchProduct == "All" || string.IsNullOrEmpty(searchProduct))
{
reviews = await _context.Review.Where(r => r.ReviewApproved == true).ToListAsync();
}
reviews = reviews.ToPagedList(pageSize, pageNumber);
return PartialView(reviews);
}
I am creating a MVC application and I would like to pass data between views.
Here is my first view:
#model ClassDeclarationsThsesis.Models.AddGroupViewModel
#{
ViewBag.Title = "Add Groups";
}
<h2>Add Groups to subjects</h2>
#foreach (var user in Model.Users)
{
if (user.email.Replace(" ", String.Empty) == HttpContext.Current.User.Identity.Name)
{
if (user.user_type.Replace(" ", String.Empty) == 3.ToString() || user.user_type.Replace(" ", String.Empty) == 2.ToString())
{
using (Html.BeginForm("AddGroup", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create new groups.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#{
List<SelectListItem> listItems1 = new List<SelectListItem>();
}
#foreach (var subject in Model.Subjects)
{
listItems1.Add(new SelectListItem
{
Text = subject.name,
Value = subject.name,
Selected = true
});
}
#Html.LabelFor(m => m.subject_name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.subject_name, listItems1, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.qty, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.qty, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Submit" />
</div>
</div>
}
}
if (user.user_type.Replace(" ", String.Empty) == 1.ToString())
{
<p>You do not have enough permissions to enter this page. Contact the administrator.</p>
}
}
}
And my controller for this:
public ActionResult AddGroup(AddGroupViewModel model)
{
var entities = new ClassDeclarationsDBEntities1();
var model1 = new AddGroupViewModel();
model1.Subjects = entities.Subjects.ToList();
model1.Users = entities.Users.ToList();
// set your other properties too?
if (ModelState.IsValid)
{
return RedirectToAction("AddGroupsQty", "Account");
}
return View(model1);
}
And what I would like to achieve is to pass chosen item from dropdown list and this qty variable to AddGroupsQty View. How do I do this? In my controller of AddGroupsQty i have just a simple return of view so far.
You can pass the values using querystring.
return RedirectToAction("AddGroupsQty", "Account",
new { qty=model.qty,subject=model.subject_name);
Assuming your AddGroupsQty have 2 parameters to accept the quantity and subject
public ActionResult AddGroupsQty(int qty,string subject)
{
// do something with the parameter
// to do : return something
}
This will make browser to issue a new GET request with the values in query string. If you do not prefer to do that, you can use a server side temporary persistence mecahnism like TempData
TempData["qty"]=model.qty;
TempData["subject"]= model.subject_name;
return RedirectToAction("AddGroupsQty", "Account");
And in your AddGroupsQty action,
public ActionResult AddGroupsQty()
{
int qty=0;
string subjectName=string.Empty;
if(TempData["qty"]!=null)
{
qty = Convert.ToInt32(TempData["qty"]);
}
if(TempData["subject"]!=null)
{
subjectName = TempData["subject"];
}
// Use this as needed
return View();
}
If you want to pass these values from the ADdGroupsQty action to it's view, you can use either a view model or ViewBag/ViewData.