Add database model (for listing records) to a View associated already with a model (for textboxes) - asp.net-mvc

I'm quite new to MVC and still making myself familiar to how MVC works. So basically, I have a User model that has a Create view. I'm using Razor syntax to get the variables from User model:
Create.cshtml
#model CDS.Models.UserModels
#{
ViewBag.Title = "Create User";
}
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.firstname)
#Html.TextBoxFor(m => m.firstname)
<input type="submit" id="btnSave" value="Save" class="btn btn-default" />
}
UserModels.cs
namespace CDS.Models
{
public class UserModels
{
public string userid { get; set; }
[Display(Name = "First Name")]
public string firstname{ get; set; }
public IEnumerable<SelectListItem> filteroptions { get; set; }
}
}
I tried auto-generating an Index view from the controller's Index method to list the database records, but found out that the generated Index view is using the Database model (first line of code). I just want to move the code from the Index.cshtml to my Create.cshtml to have the latter View also display the database records. So how will I do that? I've heard that I need to use Javascript for that?
UserController.cs
namespace CDS.Controllers
{
public class UserController : Controller
{
CDSEntities _odb = new CDSEntities(); //My Database
// GET: User
public ActionResult Index()
{
return View(_odb.USR_MSTR.ToList());
}
// GET: User/Create
public ActionResult Create()
{
var filters = GetAllFilters();
var model = new UserModels();
model.filteroptions = GetSelectListItems(filters);
return View(model);
}
}
}
Index.cshtml
#model IEnumerable<CDS.USR_MSTR>
#{
ViewBag.Title = "Index";
}
<p>#Html.ActionLink("Create New", "Create")</p>
<table class="table">
<tr>
<th>#Html.DisplayNameFor(model => model.FIRST_NM)</th>
<th>#Html.DisplayNameFor(model => model.LAST_NM)</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.FIRST_NM)</td>
<td>#Html.DisplayFor(modelItem => item.LAST_NM)</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>
Please note that I removed the codes that I think unnecessary to post here. These are all just summaries of my classes and HTMLs

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);

Html.BeginForm call the right Action in Controller

There are a lot of topics related to this question but I still did't figured out what I'm doing wrong.
I have a database where I manage access of different users to folders. On my View the User can select Employees which should have access to certain folder. Then I want to pass the selected Employees to Controller, where the database will be updated.
My Problem is: The right Action in the Controller class didn't get invoked.(I have a breakpoint inside)
Here is the View
#model DataAccessManager.Models.EmployeeSelectionViewModel
#{
ViewBag.Title = "GiveAccessTo";
}
#using (Html.BeginForm("SubmitSelected", "FolderAccessController", FormMethod.Post, new { encType = "multipart/form-data"}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.fr_folder_uid_fk)
<div class="form-horizontal">
<input type="submit" value="Save" id="submit" class="btn btn-default" />
<table id="tableP">
<thead>
<tr>
<th>Selection</th>
<th>Second Name</th>
<th>First Name</th>
<th>Department</th>
</tr>
</thead>
<tbody id="people">
#Html.EditorFor(model => model.People)
</tbody>
</table>
</div>
</div>
</div>
}
Here is the Controller reduced to the minimum
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SubmitSelected(EmployeeSelectionViewModel model)
{
return View();
}
More Details: I am not sure what is causing the problem, so here some more details.
The view is strongly typed to EmployeeSelectionViewModel, it represets the table with all Employees as a List here is the the code:
public class EmployeeSelectionViewModel
{
public List<SelectEmployeeEditorViewModel> People { get; set; }
public EmployeeSelectionViewModel()
{
this.People = new List<SelectEmployeeEditorViewModel>();
}
public Int64 fr_folder_uid_fk { get; set; }
public IEnumerable<string> getSelectedIds()
{
// Return an Enumerable containing the Id's of the selected people:
return (from p in this.People where p.Selected select p.fr_mavnr_fk).ToList();
}
}
The SelectEmployeeEditorViewModel represents one row of the table with all Employees.
public class SelectEmployeeEditorViewModel
{
public bool Selected { get; set; }
public string fr_mavnr_fk { get; set; }
public string firstName { get; set; }
public string secondName { get; set; }
public string dpt { get; set; }
}
And it has a View which create the checkboxes for each Employee
#model DataAccessManager.Models.SelectEmployeeEditorViewModel
<tr>
<td style="text-align:center">
#Html.CheckBoxFor(model => model.Selected)
#Html.HiddenFor(model => model.fr_mavnr_fk)
</td>
<td>
#Html.DisplayFor(model => model.secondName)
</td>
<td>
#Html.DisplayFor(model => model.firstName)
</td>
<td>
#Html.DisplayFor(model => model.dpt)
</td>
</tr>
The /FolderAccessController/SubmitSelected URL is called in the browser when I press the Submit button, but as mentioned the Action isn't invoked.
EDIT: I get the HTTP 404 not found error after pressing the button
Try removing the "Controller" word from your Html.BeginForm() second parameter, it's not needed.
#using (Html.BeginForm("SubmitSelected", "FolderAccess", FormMethod.Post, new { encType = "multipart/form-data"}))
Thiago Ferreira and haim770 Thanks a lot! The solution is to use the combination of your comments. So:
#using (Html.BeginForm("SubmitSelected", "FolderAccess", FormMethod.Post))
at the Controller

Post Multiple Data from View to Controller MVC

I want to post quantity property to Controller (It's an edit action). I'm editing OrderedProductSet which is connected with ProductSet in my SQL Database (I get the name and price from there). How to pass multiple data from the view to controller? How to write method in controller class to receive the data (I'm asking about method arguments in this specific case).
My view:
#model Shop.Models.ProductViewModel#{
ViewBag.Title = "Edycja zamówienia";
}
<h2>Edycja zamówienie</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<table class="table">
<tr>
<th>
<b>Nazwa produktu</b>
</th>
<th>
<b>Cena</b>
</th>
<th>
<b>Ilość</b>
</th>
<th></th>
</tr>
#foreach (var item in Model.orderedProductSet)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ProduktSet.name)
</td>
<td>
#Html.DisplayFor(modelItem => item.ProduktSet.price)
</td>
<td>
#Html.EditorFor(model => item.quantity, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Potwierdź zmiany" class="btn btn-default" />
</div>
</div>
}
<div>
#Html.ActionLink("Powrót", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
My model (in separated classes of course):
public class ProductViewModel
{
public OrderSet orderSet { get; set; }
public IEnumerable<OrderedProductSet> orderedProduktSet { get; set; }
}
public partial class OrderedProduktSet
{
public int orderNumber{ get; set; }
public int productNumber { get; set; }
public int ilosc { get; set; }
public virtual ProduktSet ProduktSet { get; set; }
public virtual OrderSet OrderSet { get; set; }
}
You need to construct controls for you collection in a for loop or use a custum EditorTemplate for OrderedProduktSet so that the controls are correctly named with indexers and can be bound on post back. Note the for loop approach required that the collection be IList.
#model Shop.Models.ProductViewModel
#using(Html.BeginForm())
{
....
for(int i = 0; i < Model.orderedProductSet.Count; i++)
{
#Html.DisplayFor(m => m.orderedProductSet[i].ProduktSet.name)
....
#Html.EditorFor(m => m.orderedProductSet[i].quantity, new { htmlAttributes = new { #class = "form-control" } })
}
<input type="submit" />
}
Controller (the model will be bound, including the collection of OrderedProductSet)
public ActionResult Edit(ProductViewModel model)
{
....
}
Alternatively, you can create an EditorTemplate
/Views/Shared/EditorTemplates/OrderedProduktSet.cshtml
#model OrderedProduktSet
#Html.DisplayFor(m => m.ProduktSet.name)
#Html.EditorFor(m => m.quantity, new { htmlAttributes = new { #class = "form-control" } })
and in the main view
#model Shop.Models.ProductViewModel
#using(Html.BeginForm())
{
....
#Html.EditorFor(m => m.orderedProductSet)
<input type="submit" />
}
Viewbag is your friend here. You normally pass data from View to Controller in MVC. You can access data set in a Viewbag in the controller in your View.
The simplest way to let your controller handle your view is to create an actionresult method in your controller with the same name as your view.
For example, your view is called Index, thus you would have the following method in your controller to handle the view data:
public ActionResult Index()
{
return View();
}
Accessing a list:
Use a Viewbag.
Controller
Viewbag.MyList = myList
View
#foreach (var item in Viewbag.MyList)
Here is good link for more info:
http://www.asp.net/mvc/overview/older-versions/getting-started-with-aspnet-mvc4/adding-a-view

Posting Ienumerable Values and Saving to M-2-M Relationship

VS'12 KendoUI InternetApplication Template C# asp.net EF Code First
My Question is how to pass both the Regular ( are passing now ) values and the Ienumerable(passing null) into my controller and saving them to the Database using EF Code First in a Many-2-Many Relationship manor.
The Following is what i have tried
Main View
#model OG.Models.UserProfiles
#using (Html.BeginForm())
{
<div class="editor-field">
<div class="Containter">
<div>
#Html.DisplayFor(model => model.UserName)
</div>
<div class="contentContainer">
#foreach (var item in Model.Prospects)
{
<table>
<tr>
<td>
#Html.Label("Current Prospects")
</td>
</tr>
<tr>
<td>
#Html.DisplayNameFor(x=>item.ProspectName)
</td>
</tr>
</table>
}
</div>
</div>
<div class="contentContainer2">
#Html.Partial("_UsersInProspectsDDL", new OG.ModelView.ViewModelUserInProspects() { Users = Model.UserName })
</div>
</div>
}
Partial View
#model OG.ModelView.ViewModelUserInProspects
<label for="prospects">Prospect:</label>
#(Html.Kendo().DropDownListFor(m=>m.Prospects)
.Name("Prospects")
.HtmlAttributes(new { style = "width:300px"}) //, id = "countys"})
.OptionLabel("Select Prospect...")
.DataTextField("ProspectName")
.DataValueField("ProspectID")
.DataSource(source => {
source.Read(read =>
{
read.Action("GetCascadeProspects", "ChangeUsersInfo")
.Data("filterProspects");
})
.ServerFiltering(true);
})
.Enable(false)
.AutoBind(false)
.CascadeFrom("Clients")
</div>
Model for PartialView
public class ViewModelUserInProspects
{
public string Clients { get; set; }
public IEnumerable<dbClient> AvailableClients { get; set; }
public string Prospects { get; set; }
public IEnumerable<dbProspect> AvailableProspects { get; set; }
public string Users { get; set; }
public IEnumerable<UserProfiles> AvailableUsers {get;set;}
}
}
Main Model
Standart SimpleMemberShipUserTable
Post Method
[HttpPost]
public ActionResult UsersInProspect(
[Bind(Include= "ProspectName, ProspectID")]
UserProfiles userprofiles, ViewModelUserInProspects values, FormCollection form)
//<- Trying different things sofar
{
if (ModelState.IsValid)
{
//string something = form["Prospects"];
int prosID = Convert.ToInt16(values.Prospects);
int UserID = userprofiles.UserID; // <- THIS VALUE is null atm.
This is where i need to save both ID's to the EF Generated / Mapped Table. Unsure how.
db.Entry(userprofiles).CurrentValues.SetValues(userprofiles);
db.Entry(userprofiles).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userprofiles);
}
Please take a look Here
Goes over ViewModels
What EditorTemplate are and how to use them
What the GET Method would look like
What the Edit View would look like
Give you a View Example
What the Post Method would look like

MVC 4 Model is empty when I try to submit a form

I have two forms on one View executing two separate Action methods in one Controller.
The first form (frmRefresh) is responsible for getting data and displaying it on the form so the user can pick certain checkboxes. Once submitted, the data is returned just fine in the ViewModel and is properly displayed on the form. 11 records for the Templates and 3 records for the Guarantors are displyaed as checkboxes on the form.
The second form (frmProcess), is responsible for taking the data on the form (that came back from the first post above). The user makes selections on the screen and processes it against some logic in the Controller. I have List objects in the model and don't suppose I can use the FormCollection to process the data because of the complex objects. Basically, they are a collection of checkboxes. I really need to use the data that should be submitted in the Model because of processing in the Controller for that data.
When submitting the second form, I realize that the loanid & ddl will not be available unless I put them in a hidden field (because they are in a separate form) --- that's fine. What I'm having a great deal of difficulty in understanding is when I submit the second form (frmProcess), why doesn't the model view binder take the data from the form, put it in the model and submit it to my GeneratePDF action method.?
Number one, I really need some help in understanding why this is happening and number two, I really need a soltution which takes my model data from the form to the action method and processes it. As you can see in the Controller, at the end of the code, I'm enumerating the Templates in the ViewModel to process the data.
Please help, as I am totally stuck on this at work and they are depending on me for this. I just don't get why the model binder doesn't take the values on the form and submit it to the action method for processing. It appears I'm missing something to allow the data to get back into the Model upon submission.
Below is my pertinent code:
ViedwModel
public partial class ViewModelTemplate_Guarantors
{
public int SelectedTemplateId { get; set; }
public IEnumerable<PDFTemplate> Templates { get; set; }
public int SelectedGuarantorId { get; set; }
public IEnumerable<tGuarantor> Guarantors { get; set; }
public string LoanId { get; set; }
public string SelectedDeptText { get; set; }
public string SelectedDeptValue { get; set; }
public string LoanType { get; set; }
public bool ShowTemps { get; set; }
public string Error { get; set; }
public string ErrorT { get; set; }
public string ErrorG { get; set; }
public bool ShowGeneratePDFBtn { get; set; }
}
View
#model PDFConverterModel.ViewModels.ViewModelTemplate_Guarantors
#{
ViewBag.Title = "BHG :: PDF Generator";
}
<h2>#ViewBag.Message</h2>
<div>
<table style="width: 1000px">
<tr>
<td colspan="5">
<img alt="BHG Logo" src="~/Images/logo.gif" />
</td>
</tr>
#using (Html.BeginForm("Refresh", "Home", FormMethod.Post, new { id = "frmRefresh" })) { <tr>
<td>
#*#(Html.Kendo().NumericTextBox<int>()
.Name("txtLoanID")
.Placeholder("Enter numeric value")
)*#
#Html.LabelFor(model => model.LoanId)
#Html.TextBoxFor(model => model.LoanId)
#Html.ValidationMessageFor(model => model.LoanId)
</tr>
<tr>
<td>#Html.LabelFor(model => model.LoanType)
#Html.TextBox("SBA", "SBA")
#Html.ValidationMessageFor(model => model.LoanType)
#*#Html.TextBoxFor(model => model.LoanType)*#
</td>
<td>
<label for="ddlDept">Department:</label>
#(Html.Kendo().DropDownListFor(model => model.SelectedDeptText)
.Name("ddlDept")
.DataTextField("DepartmentName")
.DataValueField("DepartmentID")
.Events(e => e.Change("Refresh"))
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetDepartments", "Home");
});
})
)
#Html.ValidationMessageFor(model => model.SelectedDeptText)
</td>
</tr>
<tr>
<td colspan="3">
<input type="submit" id="btnRefresh" value='Refresh' />
</td>
</tr>
}
#using (Html.BeginForm("GeneratePDF", "Home", FormMethod.Post, new { id = "frmProcess" })) { if (Model.ShowGeneratePDFBtn == true)
{
if (Model.ErrorT != string.Empty)
{
<tr>
<td colspan="5">
<u><b>#Html.Label("Templates:")</b></u>
</td>
</tr>
<tr>
#foreach (var item in Model.Templates)
{
<td>
#Html.CheckBoxFor(model => item.IsChecked)
#Html.DisplayFor(model => item.TemplateName)
</td>
}
</tr>
}
else
{
Model.Error = Model.ErrorT;
}
if (Model.ErrorG != string.Empty)
{
<tr>
<td colspan="5">
<u><b>#Html.Label("Guarantors:")</b></u>
</td>
</tr>
<tr>
#foreach (var item in Model.Guarantors)
{
<td>
#Html.CheckBoxFor(model => item.isChecked)
#Html.DisplayFor(model => item.GuarantorFirstName) #Html.DisplayFor(model => item.GuarantorLastName)
</td>
}
</tr>
}
else
{
Model.Error = Model.ErrorG;
}
<tr>
<td>
<input type="submit" id="btnGeneratePDF" value='Generate PDF' />
</td>
</tr>
<tr>
<td colspan="5">
#Model.Error
</td>
</tr>
}
} </table>
</div>
<script type="text/javascript">
$('btnRefresh').on('click', '#btnRefresh', function () {
Refresh();
});
function Refresh() {
var LoanID = $("#LoanID").val();
if (LoanID != "") {
document.forms["frmTemps"].submit();
}
}
</script>
Controller
public ActionResult Index(ViewModelTemplate_Guarantors model)
{
ViewBag.Error = "";
model.ShowGeneratePDFBtn = false;
return View("Index", model);
}
// used for the first form "frmRefresh" [HttpPost] public ActionResult Refresh(ViewModelTemplate_Guarantors model) {
try
{
model.Error = string.Empty;
bool dbHasRows = db.ChkLoanFields(Convert.ToInt32(model.LoanId));
if (!dbHasRows)
{
model.ShowGeneratePDFBtn = false;
model.Error = "Details not available for this LoanId.";
return View("Index",model);
}
else
{
int TemplateCnt = 0;
int GuarantorCnt = 0;
//todo - modify 2nd & 3rd parms instead of hardcoding
ViewModelTemplate_Guarantors tg = db.SelectViewModelTemplate_Guarantors(Convert.ToInt32(model.LoanId), "All", "All", out TemplateCnt, out GuarantorCnt);
if (TemplateCnt > 0)
model.Templates = tg.Templates;
else
model.ErrorT = "Templates not available for this LoanType.";
if (GuarantorCnt > 0)
model.Guarantors = tg.Guarantors;
else
model.ErrorG = "Guarantors not available for this LoanId.";
model.ShowGeneratePDFBtn = true;
// right before the return here, the model is full of data. return View("Index", model); }
}
catch (Exception ex)
{
throw ex;
}
} [HttpPost] // when I check the data here (via submission from the "frmProcess" form, the model is completely empty, null, etc... WHY???? // i NEED the model data here to perform processing in this action method. public ActionResult GeneratePDF(ViewModelTemplate_Guarantors model) {
try
{
int FolderNo, GuarantorNum = 0;
string Folder, LoanFolder = String.Empty;
string FormId, FormName, GuarantorName = String.Empty;
int LoanId = Convert.ToInt32(model.LoanId);
LoanFolder = LoanId.ToString().PadLeft(8, '0');
//To calculate FolderId based on LoanId
if ((LoanId > 0) && (LoanId < 99000))
{
FolderNo = ((int)(LoanId / 10000) * 10000);
}
else
{
FolderNo = ((int)(LoanId / 1000) * 1000);
}
Folder = ((int)FolderNo).ToString();
Folder = Folder.PadLeft(8, '0');
//todo - 2nd parm SelectedValue of dept
List<sSRPTFundexDocCodes1_Test_Result> sSRPTFundexDocCodes1 = db.GetFormValues(Convert.ToInt32(model.LoanId), (model.SelectedDeptValue));
if (sSRPTFundexDocCodes1 != null)
{
foreach (PDFTemplate template in model.Templates) {
if (template.IsChecked == true) {
TemplateName not showing up in model after post.
This works fine... The values (the checkboxes and the corresponding names are displyaed on the form.
However, when posting the GeneratePDF button, all I see in the model is if the Checkbox is checked (which is great). After playing around with many of the following statements: (ValueFor, DisplayFor, LabelFor, EditorFor, etc), the value coming back for the Template name is blank. I need the name of the template that was checked in correspondance with the checkbox.
#Html.ValueFor(model => Model.Templates[i].TemplateName)
How can I accomplish this? Thanks ahead of time... Below is my updated code.
ViewModel public partial class ViewModelTemplate_Guarantors
{
public ViewModelTemplate_Guarantors()
{
Templates = new List<PDFTemplate>();
Guarantors = new List<tGuarantor>();
}
public int SelectedTemplateId { get; set; }
public List<PDFTemplate> Templates { get; set; }
public int SelectedGuarantorId { get; set; }
public List<tGuarantor> Guarantors { get; set; }
public string LoanId { get; set; }
public string SelectedDeptText { get; set; }
public string SelectedDeptValue { get; set; }
public string LoanType { get; set; }
public string Error { get; set; }
public string ErrorT { get; set; }
public string ErrorG { get; set; }
public bool ShowGeneratePDFBtn { get; set; }
}
Pertinet part of View:
if (Model.ShowGeneratePDFBtn == true)
{
if (Model.ErrorT == string.Empty)
{
<tr>
<td colspan="5">
<u><b>#Html.Label("Templates:")</b></u>
</td>
</tr>
<tr>
#for (int i = 0; i < Model.Templates.Count; i++)
{
<td>
#Html.CheckBoxFor(model => Model.Templates[i].IsChecked)
#Html.ValueFor(model => Model.Templates[i].TemplateName) </td>
}
</tr>
}
else
{
<tr>
<td>
<b>#Html.DisplayFor(model => Model.ErrorT)</b>
</td>
</tr>
}
if (Model.ErrorG == string.Empty)
{
<tr>
<td colspan="5">
<u><b>#Html.Label("Guarantors:")</b></u>
</td>
</tr>
<tr>
#for (int i = 0; i < Model.Guarantors.Count; i++)
{
<td>
#Html.CheckBoxFor(model => Model.Guarantors[i].isChecked)
#Html.ValueFor(model => Model.Guarantors[i].GuarantorFirstName) #Html.ValueFor(model => Model.Guarantors[i].GuarantorLastName) </td>
}
</tr>
}
else
{
<tr>
<td>
<b>#Html.DisplayFor(model => Model.ErrorG)</b>
</td>
</tr>
}
}
<tr>
<td colspan="3">
<input type="submit" name="submitbutton" id="btnRefresh" value='Refresh' />
</td>
#if (Model.ShowGeneratePDFBtn == true)
{
<td>
<input type="submit" name="submitbutton" id="btnGeneratePDF" value='Generate PDF' />
</td>
}
</tr>
<tr>
<td colspan="5">
#Model.Error
</td>
</tr>
Controller:
public ActionResult ProcessForm(string submitbutton, ViewModelTemplate_Guarantors model, FormCollection collection)
Basically, again it's working fine. When the form posts using the Generate PDF button, I get the checked value of each checkbox, but not the name of the template in the Model.
Am I missing something here???
The form before I submit is basically like below. It's the name of the checkbox (Form4) that I'm missing as a TemplateID in my Model once I get into the ActionResult.
public ActionResult ProcessForm(string submitbutton, ViewModelTemplate_Guarantors model, FormCollection collection)
checkbox (checked) Form4
#for (int i = 0; i < Model.Templates.Count; i++)
{
<td>
#Html.CheckBoxFor(model => Model.Templates[i].IsChecked)
#Html.DisplayFor(model => Model.Templates[i].TemplateName)
</td>
}
As I mentioned in my comment. The model binder cannot bind to an IEnumerable.
Your Model should look like this:
public partial class ViewModelTemplate_Guarantors
{
public ViewModelTemplate_Guarantors() {
Templates = new List<PDFTemplate>(); // These are important, the model binder
Guarantors = new List<tGuarantor>(); // will not instantiate nested classes
}
public int SelectedTemplateId { get; set; }
public List<PDFTemplate> Templates { get; set; }
public int SelectedGuarantorId { get; set; }
public List<tGuarantor> Guarantors { get; set; }
...
}
Further, your view should look like this:
...
#for(int i = 0; i < Model.Templates.Count; i++) // should really use label, not display
{
<td>
#Html.CheckBoxFor(model => Model.Templates[i].IsChecked)
#Html.DisplayFor(model => Model.Templates[i].TemplateName)
</td>
}
...
#for(int i = 0; i < Model.Guarantors.Count; i++)
{
<td>
#Html.CheckBoxFor(model => Model.Guarantors[i].isChecked)
#Html.DisplayFor(model => Model.Gurantors[i].GuarantorFirstName) #Html.DisplayFor(model => Model.Guarantors[i].GuarantorLastName)
</td>
}
...
Although a better choice would be to use an EditorTemplate and instead do this:
...
#Html.EditorFor(m => m.Templates)
...
#Html.EditorFor(m => m.Guarantors)
...
Then create a folder in ~/Views/Shared called EditorTemplates, and then create two files called Templates.cshtml and Guarantors.cshtml.
In those files you would do this:
#model PDFTemplate
<td>
#Html.CheckBoxFor(model => model.IsChecked)
#Html.DisplayFor(model => model.TemplateName)
</td>
and
#model Guarantors
<td>
#Html.CheckBoxFor(model => model.isChecked)
#Html.DisplayFor(model => model.GuarantorFirstName) #Html.DisplayFor(model => model.GuarantorLastName)
</td>
The editor templates will automatically iterate over the collection and will account for the correct naming format to make the model binder understand it's a collection.

Resources