Compound primary key edit view MVC - asp.net-mvc

Hi I'm trying to edit a Students grade in a linker table that contains the Student Id and the Course Id. I've looked around a bit, and have seen that I need to take in two id parameters into the view rather than just the one id, and then change the action link to take two parameters. But when i click on the edit button in the index, I get a 404 saying the page can't be found. Any ideas where I'm going wrong?
Thanks in advance.
Here's my index view
#model IEnumerable<S00132671CA2.Models.StudentCourse>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Grade)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Grade)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id= item.StudentId, CourseId = item.CourseId }) |
#Html.ActionLink("Details", "Details", new { id = item.StudentId, Course = item.CourseId }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
Here's my Edit action
public ActionResult Edit(int? StudentId, int? CourseId)
{
StudentCourse courseList = db.StudentCourse.Find(StudentId, CourseId);
if (courseList == null)
{
return HttpNotFound();
}
ViewBag.StudentId = new SelectList(db.Students, "StudentId", "StudentName", courseList.StudentId);
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "CourseName", courseList.CourseId);
return View(courseList);
}
Here's my model if it's any help
public class StudentCourse
{
[Key, Column(Order = 0)]
public int StudentId { get; set; }
public virtual Student Student { get; set; }
[Key, Column(Order = 1)]
public int CourseId { get; set; }
public virtual Course Course { get; set; }
public double Grade { get; set; }
}

Try
#Html.ActionLink("Edit", "Edit", new { StudentId = item.StudentId, CourseId = item.CourseId })

Related

Pass multiple parameters to partial view

I have a model structured like so:
public int JobID { get; set; }
public int SiteID { get; set; }
public List<AModel> ListAModel { get; set; }
In my main view, I am iterating through the List using a for loop with i as an index. I want to call a partial view from within this main page to avoid repeat code across the system but this partial view needs to be aware of the index number as well as the job id and site id.
I cannot just pass in in Model.ListAModel[i] as this will not be aware of job or site id, and likewise with the other way round.
Any help would be appreciated.
You can use the ExpandoObject Class that represents an object whose members can be dynamically added and removed at run time. Code below demonstrates how it can be used:
Data model:
public class AModel
{
public string Name { get; set; }
}
public class ViewModel
{
public int JobID { get; set; }
public int SiteID { get; set; }
public List<AModel> ListAModel { get; set; }
}
.cshtml:
#model Models.ViewModel
#using System.Dynamic
<div>
#for (int i=0; i < Model.ListAModel.Count; i++)
{
dynamic item = new ExpandoObject();
item.job = Model.JobID;
item.site = Model.SiteID;
item.amodel = Model.ListAModel[i];
Html.RenderPartial(#"PartialView", (object) item);
}
</div>
In the partial view:
#using System.Dynamic
#model dynamic
#{
Models.AModel am = #Model.amodel;
<div>job id = #Model.job, site id = #Model.site, amodel = #am.Name </div>
}
There are several posts about how to pass parameters to a view dynamically For example:
Dynamic Anonymous type in Razor causes RuntimeBinderException
In my main view, I am iterating through the List using a for loop with
i as an index. I want to call a partial view from within this main
page to avoid repeat code across the system but this partial view
needs to be aware of the index number as well as the job id and site
id.
I cannot just pass in in Model.ListAModel[i] as this will not be aware
of job or site id, and likewise with the other way round.
From your description, I assume you want to filter data based on the Job id and site id, then display the AModel via the partial view.
To pass parameters from main page to the partial view, you can use the view-data attribute. Please refer the following sample:
Model:
public class JobViewModel
{
public int JobID { get; set; }
public int SiteID { get; set; }
public List<AModel> ListAModel { get; set; }
}
public class AModel
{
public int ID { get; set; }
public string Name { get; set; }
public int JobID { get; set; }
public int SiteID { get; set; }
}
Controller:
public IActionResult Index4()
{
var initialdata = new List<JobViewModel>()
{
new JobViewModel(){
JobID = 1001,
SiteID = 102,
ListAModel = new List<AModel>()
{
new AModel(){ ID=1, Name="Joe", JobID=1001, SiteID=101},
new AModel(){ ID=2, Name="Merry", JobID=1001, SiteID=102},
new AModel(){ ID=3, Name="Henry", JobID=1001, SiteID=103},
new AModel(){ ID=4, Name="Cody", JobID=1001, SiteID=101},
new AModel(){ ID=5, Name="Simon", JobID=1001, SiteID=102},
new AModel(){ ID=6, Name="Leena", JobID=1001, SiteID=103},
new AModel(){ ID=7, Name="Ode", JobID=1001, SiteID=101},
new AModel(){ ID=8, Name="Nicky", JobID=1001, SiteID=102},
}
}
};
return View(initialdata.FirstOrDefault());
}
Main page: using ViewData and view-data attribute to pass parameters.
#model MVCWebApplication.Models.JobViewModel
#{
ViewData["JobID"] = Model.JobID.ToString();
ViewData["SiteID"] = Model.SiteID.ToString();
}
<div>
<h4>JobViewModel</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
#Html.DisplayNameFor(model => model.JobID)
</dt>
<dd class="col-sm-10">
#Html.DisplayFor(model => model.JobID)
</dd>
<dt class="col-sm-2">
#Html.DisplayNameFor(model => model.SiteID)
</dt>
<dd class="col-sm-10">
#Html.DisplayFor(model => model.SiteID)
</dd>
</dl>
<div class="form-group">
<partial name="_AModelPV.cshtml" model="#Model.ListAModel" view-data="ViewData"/>
</div>
</div>
Partial View (_AModelPV.cshtml): In the partial view, you could also check whether the ViewData exists and contains the value.
#model IEnumerable<MVCWebApplication.Models.AModel>
#{
var jobid = Convert.ToInt32(ViewData["JobID"]);
var siteid = Convert.ToInt32(ViewData["SiteID"]);
}
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.ID)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.JobID)
</th>
<th>
#Html.DisplayNameFor(model => model.SiteID)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Where(c=>c.JobID == jobid && c.SiteID == siteid).ToList()) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.JobID)
</td>
<td>
#Html.DisplayFor(modelItem => item.SiteID)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</tbody>
</table>
The result like this:
Besides, you could also consider filtering the data before sending data to the main page. For example, in the above action method, filter the data based on the JobID and SiteID, then return the result to the main page. In this scenario, the ListAmodel contains the filtered data, there is no need to do the filter action in the partial view.
var result = initialdata.Select(c =>
new JobViewModel {
JobID = c.JobID, SiteID = c.SiteID,
ListAModel = c.ListAModel.Where(d => d.JobID == c.JobID && d.SiteID == c.SiteID).ToList() })
.FirstOrDefault();
return View(result);

Using multiple paginated lists on one HTML page

I'm MVC / Razor pages newbie so bear with me. I would like to display several lists of items on one page. These lists have 100+ items each so they need to be paginated. In Microsofts Entity Framework .net core tutorial I found only 1 pagination per page, so that cannot be used.
I feel like I should be using View Component to achieve this, but I'm quite not sure how to do it. Can anyone help me please?
I'm using Razor pages .net Core, but I'm not against using controller to achieve this
Thanks in advance
My view is:
#page
#model DOOR.Core.Web.Pages.Models.IndexModel
<h2 style="margin-top:20px "><span class="glyphicon glyphicon-compressed"></span> Model #Html.DisplayFor(model => Model.
<h4 class="h4-bold">Tables</h4>
#if (Model.PdModel.FirstOrDefault().PdTables.Count == 0)
{
#:Model doesn't contain any tables
}
else
{
<table class="table table-striped">
<thead>
<th>
Name
</th>
<th>
Description
</th>
<th>
Type
</th>
</thead>
#foreach (var tableItem in Model.PdModel.FirstOrDefault().PdTables.OrderBy(x => x.TableName))
{
<tr>
<td>
<a asp-page="/Tables/Index" asp-route-id="#tableItem.Id" asp-page-handler="LoadTable"><span class="glyphicon glyphicon-list-alt"></span> #tableItem.TableName</a>
</td>
<td>
#tableItem.TableComment
</td>
<td>
#tableItem.TableStereotype
</td>
</tr>
}
</table>
}
<hr />
<h4 class="h4-bold">View</h4>
#if (Model.PdModel.FirstOrDefault().pdViews.Count == 0)
{
#:Model doesn't contain any view
}
else
{
<table class="table table-striped">
<thead>
<th>Name</th>
<th>Description</th>
</thead>
#foreach (var viewitem in Model.PdModel.FirstOrDefault().pdViews.OrderBy(x => x.ViewName))
{
<tr>
<td><a asp-page="/Views/index" asp-page-handler="LoadView" asp-route-id="#viewitem.ID"><i class="glyphicon glyphicon-search"></i> #viewitem.ViewCode</a></td>
<td>#viewitem.ViewComment</td>
</tr>
}
</table>
}
My domain models are:
public class PdModel
{
public int Id { get; set; }
public string ModelCode { get; set; }
...
public ICollection<PdTable> PdTables { get; set; }
public ICollection<PdView> pdViews { get; set; }
}
public class PdTable
{
public int Id { get; set; }
public int ModelId { get; set; }
public string ModelCode { get; set; }
public string TableCode { get; set; }
...
[ForeignKey("ModelId")]
public virtual PdModel PdModels { get; set; }
}
public class PdView
{
public int ID { get; set; }
public string ModelCode { get; set; }
public int ModelID { get; set; }
public string ViewCode { get; set; }
...
[ForeignKey("ModelID")]
public virtual PdModel PdModel { get; set; }
}
My method is:
public PaginatedList<PdModel> PdModel { get; set; }
public async Task OnGetLoadModelAsync(int id, int? pageIndex)
{
IQueryable<PdModel> PdModelsQuer = _context.PdModel.Where(x => x.Id == id)
.Include(x => x.PdTables)
.Include(x => x.pdViews)
PdModel = await PaginatedList<PdModel>.CreateAsync(PdModelsQuer, pageIndex ?? 1, 3);
}
Not the best solution, but try this approach instead: you can create separate tables for each item and then link pagination to each table
Not sure if you have found a solution yet, but I like using this library
https://www.nuget.org/packages/X.PagedList.Mvc.Core/
To paginate different models, just create a viewmodel containing your IPagedList objects.
Here's an example of how to set up the pagination control:
#Html.PagedListPager(
(IPagedList)Model.MyPagedListObject,
page => Url.Action("MyControllerAction", "MyController",
new
{
page,
size = Model.MyPagedListObject.PageSize
}),
PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(
new PagedListRenderOptions
{
MaximumPageNumbersToDisplay = 5,
UlElementClasses = new[] { "pagination" },
ContainerDivClasses = new[] { "pagination-container" },
LiElementClasses = new string[] { "page-item" },
PageClasses = new string[] { "page-link" }
},
new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
UpdateTargetId = "MyDivWrapperID"
}))
If the documentation and/or this explanation is unclear I can provide a more in depth example.

Display Active Directory Group in a MVC view

I Am trying to display active directory group member in the view. When i run the Code I am having the error "The model item passed into the dictionary is of type 'System.String[]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[SSSFT.LogIT.Models.ActiveDirectory]'". Debbuging the code show all the group member i am looking for
Model Class
public class ActiveDirectory
{
public int id { get; set; }
//public string Username { get; set; }
//public string Email { get; set; }
public string SamAccountName { get; set; }
}
Controller
public ActionResult Index(string username)
{
username = "sssftappdev";
string[]output = null;
using (var ctx = new PrincipalContext(ContextType.Domain))
using (var user = UserPrincipal.FindByIdentity(ctx, username))
{
if (user != null)
{
output = user.GetGroups() //this returns a collection of principal objects
.Select(x => x.SamAccountName) // select the name. you may change this to choose the display name or whatever you want
.ToArray(); // convert to string array
}
}
return View(output);
}
View
#model IEnumerable<SSSFT.LogIT.Models.ActiveDirectory>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.SamAccountName)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.SamAccountName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.id }) |
#Html.ActionLink("Details", "Details", new { id=item.id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.id })
</td>
</tr>
}
</table>
You can fix this error by simply updating your #model with:
#model String[]
You are currently passing a String[] to your view while expecting an IEnumerable of SSSFT.LogIT.Models.ActiveDirectory. Either update your code to return the right type of data, or adapt your strongly typed view with the actual result you return.

Proper way to bind the model to view in MVC?

Im newbie to mvc.I have a menusettings page where user can add menu according to his role.For that I want to create a view.How can I bind the model to view properly so that i can save the settings.My concern is inorder to access ienumerable item in the viewmodel,viewmodel should also be ienumerable.Here i cannot convert the viewmodel to ienumerable.
Any help will be greatly appreciated
ModelClass is
public partial class Role
{
public int Id { get; set; }
public string RoleName { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<MenuRole> MenuRoles { get; set; }
}
public partial class Menu
{
public int Id { get; set; }
public string MenuName { get; set; }
public string NavigateUrl { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<MenuRole> MenuRoles { get; set; }
}
ViewModel Is
public class MenuRoleVM
{
public int? RoleId { get; set; }
public SelectList RoleList { get; set; }
public IEnumerable<Menu> MenuList { get; set; }
}
My controller is
public class MenuSettingsController : Controller
{
public ActionResult Add()
{
var _menuRoleVM = new MenuRoleVM
{
RoleList = new SelectList(_db.Roles.ToList(), "Id", "RoleName"),
MenuList = _db.Menus
.Where(m => m.NavigateUrl != "#").ToList()
};
return View(_menuRoleVM);
}
}
Below example will help. Here, I have the model ContactModel with properties - FirstName, LastName and Email.
#model IEnumerable<CrazyContacts.Models.ContactModel>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
Thanks for your input #Stephen.
I got the code running by the adding the view as below
#foreach (var item in Model.MenuList.Select((x, i) => new {Data=x,Index=i }))
{
<tr>
<td>#(item.Index+1)</td>
<td>#item.Data.MenuName</td>
</tr>
}

Avoid duplicating the results on viewpage in mvc

I'm creating banking application in MVC 5.
Load table by bank and Load table rows by Bank,
But I cannot figure out do this exactly like above image,
This what I'm getting , it has 10 tables, instead just 2 tables
this is cshtml code for above view
#model IEnumerable<albaraka.Models.ProductApprovals>
#{
ViewBag.Title = "Product_Approvals";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>sadfasf</h2>
<h2>Product Approvals</h2>
#using (Html.BeginForm("Index", "Student", FormMethod.Get))
{
<div> Filter by : Date #Html.TextBox("SearchString", ViewBag.CurrentFilter as string) Filter by : Country #Html.TextBox("SearchString", ViewBag.CurrentFilter as string) <input type="submit" value="Search" /> </div>
}
#foreach (var item in Model)
{
#Html.DisplayNameFor(model => model.SubsidaryName); <p &nbsp /> #Html.DisplayFor(modelItem => item.SubsidaryName);
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ProductNameEn)
</th>
<th>
#Html.DisplayNameFor(model => model.ProdcutNameAr)
</th>
<th>
#Html.DisplayNameFor(model => model.CurrentStatus)
</th>
<th>
Actions
</th>
</tr>
#foreach (var inside in Model)
{
if (inside.Subsidary_ID == item.Subsidary_ID)
{
<tr>
<td>
#Html.DisplayFor(modelItem => inside.ProductNameEn)
</td>
<td>
#Html.DisplayFor(modelItem => inside.ProdcutNameAr)
</td>
<td>
#Html.DisplayFor(modelItem => inside.CurrentStatus)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
}
</table>
}
This is Model class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace albaraka.Models
{
public class ProductApprovals
{
public DateTime SearchDate { get; set; }
public string Country { get; set; }
public string Product_ID { get; set; }
public string Subsidary_ID { get; set; }
public string SubsidaryName { get; set; }
public string ProductNameEn { get; set; }
public string ProdcutNameAr { get; set; }
public string CurrentStatus { get; set; }
}
}
This is Controller class
public ActionResult Product_Approvals()
{
var productApproveResult =(from p in db.AB_Product
join s in db.AB_Subsidary on p.Subsidary_ID equals s.SubsidaryID
select new ProductApprovals
{
Product_ID = p.ProductID,
Subsidary_ID = s.SubsidaryID,
SubsidaryName = s.SubsidaryNameEn,
ProductNameEn = p.ProductTitleEn,
ProdcutNameAr = p.ProductTitleAr,
CurrentStatus = p.Status
}).ToList();
return View(productApproveResult);
}
How to stop this repeating ,appricate if can show a way to this in view page
You need a view model(s) to represent what you want to display in the view. It should be something like
public class BankVM
{
public string SubsidaryName { get; set; }
public IEnumerable<ProductVM> Products { get; set; }
}
public class ProductVM
{
public string ProductNameEn { get; set; }
public string ProdcutNameAr { get; set; }
public string CurrentStatus { get; set; }
}
and pass a collection of BankVM to the view
#model IEnumerable<yourAssembly.BankVM>
#foreach(var bank in Model)
{
<h3>#Html.DisplayFor(m => bank.SubsidaryName)</h3>
<table>
foreach(var product in bank.Products)
{
<tr>
<td>#Html.DisplayFor(m => product.ProductNameEn)</td>
<td>#Html.DisplayFor(m => product.ProductNameAr)</td>
....
</tr>
}
</table>
}
In the controller, you can use a Linq .GroupBy() method to populate your collection of BankVM

Resources