MVC viewmodel is null on post (editing multiple rows) - c#

I'm new to ASP.NET MVC and have been searching for a solution to this problem for hours. I'm trying to create a site where admins can go in and approve registration user requests and also assign them to a user group. I'm getting data from multiple tables so I created a viewmodel.
I finally have the GET Edit controller working to display the data, but can't figure out how the POST Edit should work. When I was debugging, I realized that the viewmodel I was trying to return had only null values.
I'm not sure if there are many things wrong with this code or just one. On postback, I need to update some values in the Access table. If you need more information from me, just let me know. Thanks!
ViewModel:
using System.Collections.Generic;
using AccessRequests.Models;
using System.ComponentModel.DataAnnotations;
namespace AccessRequests.ViewModels
{
public class UserAccessData
{
public IEnumerable<User> Users { get; set; }
public IEnumerable<Access> Accesses { get; set; }
public IEnumerable<UserGroup> UserGroups { get; set; }
}
}
Controller:
// GET: Accesses/Edit/5
public ActionResult Edit(string brand, string group)
{
var viewModel = new UserAccessData();
viewModel.Users = db.Users
.Include(i => i.Accesses)
.OrderBy(i => i.UserName);
viewModel.UserGroups = db.UserGroups
.Where(i => i.Groups.Contains(group));
if (brand != null)
{
viewModel.Accesses = db.Accesses
.Include(x => x.User)
.Where(x => x.Brand.ToUpper() == brand);
}
return View(viewModel);
}
// POST: Accesses/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Access access, UserAccessData editaccess)
{
//code here
}
View:
#model AccessRequests.ViewModels.UserAccessData
#{
ViewBag.Title = "Edit";
}
<h2>Edit Access</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<table class="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Company</th>
<th>Role</th>
<th>Region</th>
<th>User Group</th>
<th>Approve</th>
<th>Deny Reason</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Accesses)
{
#Html.HiddenFor(modelItem => item.UserName)
<tr>
<td>
#Html.DisplayFor(modelItem => item.User.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Company)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Role)
</td>
<td>
#Html.DisplayFor(modelItem => item.User.Region)
</td>
<td>
#Html.DropDownListFor(m => m.UserGroups, new SelectList(Model.UserGroups, "Groups", "GroupDescription"), "Please select a User Group")
</td>
<td>
#Html.DropDownListFor(modelItem => item.Approved, new SelectList(
new List<Object>{
new { value = 0 , text = "" },
new { value = "YES" , text = "YES" },
new { value = "NO" , text = "NO"}
},"value","text", 2))
</td>
<td>
#Html.EditorFor(modelItem => item.DenyReason, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(modelItem => item.DenyReason, "", new { #class = "text-danger" })
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

In order to post to a collection property, your field names need to be in the form of something like: Accesses[0].Approved, Accesses[1].Approved, etc. In order to achieve that, you need to use a for loop rather than foreach. You'll also need to change your property's type from IEnumerable<Access> to List<Access>.
#for (var i = 0; i < Model.Accesses.Count(); i++)
{
...
#Html.DropDownListFor(m => Model.Accesses[i].Approved, ...)
}
Also, bear in mind that only those properties which have actual HTML fields that participate in the the post will have values in your post action. Everything else will either be null or whatever default value the property may have. If you save an entity with properties that have been nulled out because they weren't posted, you will overwrite those properties in your database. You need to take care to either make sure all the necessary data comes through in the post or that you repopulate said data from the the database before attempting to save anything.

Related

How to pass value of htmlhelper from table (view) to controller?

I want to update only a single value from a row on click of the update button against a specific ID, the id is available at controller but the updated value field is not available so when I press the update button controller updated the database but store a null value in DB.
//View Code
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(m => item.Id)
</td>
<td>
#Html.DisplayFor(m => item.TagName)
</td>
<td>
#Html.DisplayFor(m => item.TagCategory)
</td>
<td>
#Html.TextBoxFor(m => item.TagValue, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
#Html.ActionLink("Update", "Update", new { id = item.Id}, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</tbody>
//Controller Code
public ActionResult Update([Bind(Include = "Id,TagValue") ]Tag tags)
{
var data = db.Tags.Where(x => x.Id == tags.Id).FirstOrDefault();
if (data!=null)
{
data.TagValue = tags.TagValue;
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
So right now your update button makes a GET request. The database is storing null because when you create the ActionLink you are not passing in the TagValue as a parameter because with GET requests the variables need to be passed in the url. You could add the TagValue as a parameter like this
#Html.ActionLink("Update", "Update", new { id = item.Id, TagValue = item.TagValue }, new { htmlAttributes = new { #class = "form-control" } })
I don't think this will work the way you want though because I am assuming you want the user to be able to change the value of TagValue and then hit update to save the value. What this solution does is it just grabs the original value of whatever TagValue is.
If you want to be able to edit the TagValue and then submit you could change your html to look like this
<tbody>
#foreach (var item in Model)
{
<tr>
<form action="#Url.Action("Update")" method="post">
<td>
#Html.HiddenFor(m => item.Id, new { #Name = "Id" })
#Html.DisplayFor(m => item.Id)
</td>
<td>
#Html.DisplayFor(m => item.TagName)
</td>
<td>
#Html.DisplayFor(m => item.TagCategory)
</td>
<td>
#Html.TextBoxFor(m => item.TagValue, new { #class = "form-control", #Name="TagValue" })
</td>
<td>
<input type="submit" value="Update" />
</td>
</form>
</tr>
}
This will create a form for every Tag you have and then generate a POST request when you hit submit.
vikscool's comment is also another good solution you could use.
You just need to Bind your View with The table from which you are sending and receiving data and you can do it with Html Begin Forms

Failing to pass data from view to the Action by Html.BeginForm()

I am very new at asp.net mvc, so the reason behind my failure might be something basic as well, but I can't seem to find it after nearly a days work now.
What I am trying to do is to get the edited Model from the Index view and pass it to a second action which does not have view and returns return RedirectToAction("Index") in the related controller. In OrdersItemsController my Action is as the following:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MarkedShipped(IEnumerable<orders_items> orderItems)
{
if (ModelState.IsValid)
{
foreach (var item in orderItems)
{
unitOfWork.GenericRepository<orders_items>().Update(item);
}
}
return RedirectToAction("Index");
}
And in the Index.cshtml which is in OrdersItems folder in the Views, what I did is as following:
#model IEnumerable<Project.DataAccess.orders_items>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("MarkedShipped", "OrdersItems", new { orderItems = Model }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.quantity)
</th>
<th>
#Html.DisplayNameFor(model => model.itemprice)
</th>
<th>
#Html.DisplayNameFor(model => model.trackingnumber)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.quantity)
</td>
<td>
#Html.DisplayFor(modelItem => item.itemprice)
</td>
<td>
#Html.EditorFor(modelItem => item.trackingnumber)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.itemid })
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="MarkShipped" class="btn btn-default" />
</div>
</div>
}
My problem is, I am not able to get the Model from the view with orderItems parameter, I am not sure if this is the right "syntax" for what I am trying to accomplish; but what I get for orderItems when the action is called is a List of orders_items with Count = 0 not a null value.
I have also checked if there is an application level exception, but got no exception from Application_Error in Global.asax
I am stuck for a day now so If anyone can point me a direction on how to pass the Model (or "the edited data") to the action for me to update the db, I would be very grateful. Thanks.
Firstly you cannot add a model which is a collection (or a model which contains a property which is a complex object or collection) to a forms route parameters. Internally the helper calls the .ToString() method and if you inspect the html generated for your form tag you will see something like
<form action=OrdersItems/MarkedShipped?orderItems=System.Collection.Generic.......
and binding will fail since you collection cannot be bound to a string. Even if it did work, it would be rather pointless because it would just post back the original values of the model.
Next, you cannot use a foreach loop to generate form controls. Again if you inspect the html your generating you will see that the EditorFor() method is generating inputs with duplicate id attributes (invalid html) and duplicate name attributes which do not have the necessary indexers to bind to a collection when you post. You need to use either a for loop of a custom EditorTemplate for typeof orders_items
Using a for loop means that your model must implement IList<T> and the view needs to be
#model IList<Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items>
#using (Html.BeginForm()) // only necessary to add the controller and action if they differ from the GET method
{
....
#for(int i = 0; i < Model.Count; i++)
{
#Html.DisplayFor(m => m[i].quantity)
....
#Html.EditorFor(m => m[i].trackingnumber)
}
....
}
Using an EditorTemplate, create a partial in /Views/Shared/EditorTemplates/orders_items.cshtml (note the name of the file must match the name of the class)
#model Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items
<tr>
<td>#Html.DisplayFor(m => m.quantity)</td>
....
<td>#Html.EditorFor(m => m.trackingnumber)</td>
....
</tr>
and then in the main view (you can use IEnumerable<T>)
#model IEnumerable<Hochanda.CraftsHobbiesAndArts.DataAccess.orders_items>
#using (Html.BeginForm())
{
<table class="table">
<thead>
....
<thead>
<tbody>
#Html.EditorFor(m => m)
</tbody>
</table>
....
}
The EditorFor() method accepts IEnumerable<T> and will generate one row for each item in you collection based on the html in the EditorTemplate
In both cases, it you inspect the html you will now see the correct name attributes necessary for you model to bind when you post
<input type="text" name="[0].trackingnumber" ... />
<input type="text" name="[1].trackingnumber" ... />
<input type="text" name="[3].trackingnumber" ... />
Try For instead of foreach here.
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.quantity)
</td>
<td>
#Html.DisplayFor(modelItem => item.itemprice)
</td>
<td>
#Html.EditorFor(modelItem => item.trackingnumber)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.itemid })
</td>
</tr>
}
Check this :- http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
You can do the following
for (int i = 0; i < #ViewBag.Persons; i++)
{
<li>
<table>
<tr>
<td>
#Html.LabelFor(m => m.Fullname,new { #id = "FullnameLabel" + #i })
</td>
<td>
#Html.TextBoxFor(m => m.Fullname, new { #id = "Fullname" + #i })
</td>
</tr>
In this way each html element gets its own unique id and its possible to retrieve the information back from them individually. You can use for or Foreach. Just make sure that your razor iterator is functioning. You can view the element ids in the source to see if it is working.

Passing the values of the DisplayFor to the model binder

I have the following view , which mainly view a list of items as a displayfor:-
#foreach (var item in Model.Resources) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.SystemInfo.MODEL)
</td>
<td>
#Html.DisplayFor(modelItem => item.RESOURCENAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.ResourceLocation.SiteDefinition.AccountDefinition.SDOrganization.NAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.ResourceLocation.SiteDefinition.SDOrganization.NAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.ComponentDefinition.COMPONENTNAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.ResourceState.STATEDESC)
</td>
<td id = "#item.RESOURCEID">
#using (Ajax.BeginForm("CreateOn","VirtualMachine", new AjaxOptions {
InsertionMode = InsertionMode.Replace,
UpdateTargetId = item.RESOURCEID.ToString() ,
LoadingElementId = "progress",
HttpMethod = "POST"})){
<span class="f">Hypervisor Server</span>
#Html.DropDownListFor(model =>model.VirtualMachine.ServerID, ((IEnumerable<t.Models.Server>)ViewBag.Servers).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.Technology.Tag),
Value = option.ServerID.ToString(),
Selected = (Model != null) && (Model.VirtualMachine != null) && (option.ServerID == Model.VirtualMachine.ServerID)
}), "Choose...")
#Html.ValidationMessageFor(model =>model.VirtualMachine.ServerID)
#Html.Hidden("IT360id", item.RESOURCEID)
#Html.Hidden("CustomerName",item.ResourceLocation.SiteDefinition.AccountDefinition.SDOrganization.NAME)
#Html.Hidden("SiteName",item.ResourceLocation.SiteDefinition.SDOrganization.NAME)
#Html.Hidden("ResourceName",item.RESOURCENAME)
<input type="submit" value="Add To" class="btn btn-primary"/>
}
But the user can read the data and chose to create the item on our database, using the ajax.beginform. But to do so I need to pass some values of the DisplayFor to the model binder. Currently I have added all the needed data as a hiddenfields, and then I will pass these values to my action method as follow:-
[HttpPost]
public ActionResult CreateOn(VirtualMachineOnIT360Only vm, long IT360id, string CustomerName, string SiteName, string ResourceName)
{
I found that my approach is not very reliable and I am trying to figure out a more reliable solution. So can anyone advice please?
Thanks
To post DisplayFor back, I prefer to use hiddenfor instead of hidden like this:
#Html.HiddenFor(item => item.RESOURCENAME);

Cannot create an instance of an interface (PagedList)

I have a view model object that contains a List of another model object type. When the user does a query on the page, if the List returned contains more than 300 records we want to use paging to keep the loading time down (some search results can return more than 14k records). The paging plugin we're using can be found here.
Once the results have been displayed on the page, the user has the ability to click a check box next to specific results, type in some information in an input text box, hit submit, and have the selected records edited with the information from the text box.
Since we needed to use an IPagedList<> in order to enable paging, however, when you hit submit (and before the page even hits the controller) we get the following error:
Cannot create an instance of an interface.
View Model
These are the two list objects that we use for paging. The zipCodeTerritory object holds the results of the query. The pagedTerritoryList is used to display only the results on the specific page the user is on.
//Paging List objects
public IPagedList<ZipCodeTerritory> pagedTerritoryList { get; set; }
public List<ZipCodeTerritory> zipCodeTerritory { get; set; }
public IPagedList PagingMetaData { get; set; }
Controller
This is our basic search. The .ToPagedList method is used to specify what range of results we want to display and place them in the pagedTerritoryList object.
//set Paged List counter variables
int pageNumber = page ?? 1;
int pageSize = 300;
//Determine if Territory present?
if (string.IsNullOrWhiteSpace(search.searchTerritory))
{
//State Code ONLY search
search.zipCodeTerritory = (from z in db.ZipCodeTerritory
where z.StateCode.Equals(search.searchState)
select z).ToList();
}
else if(string.IsNullOrWhiteSpace(search.searchState))
{
//Territory ONLY search
search.zipCodeTerritory = (from z in db.ZipCodeTerritory
where z.IndDistrnId.Equals(search.searchTerritory)
select z).ToList();
}
else
{
//Territory AND state search
search.zipCodeTerritory = (from z in db.ZipCodeTerritory
where z.IndDistrnId.Equals(search.searchTerritory) &&
z.StateCode.Equals(search.searchState)
select z).ToList();
}
//Convert list to IPagedList for pagining on Index
search.pagedTerritoryList = search.zipCodeTerritory.ToPagedList(pageNumber, pageSize);
//Set Paged List objects
search.PagingMetaData = new StaticPagedList<ZipCodeTerritory>(search.zipCodeTerritory, pageNumber, pageSize,
search.zipCodeTerritory.Count).GetMetaData();
return View(search);
View
This is the form that displays the search results. If a user checks the check box, then hits either clone or delete buttons, the results are supposed to be posted back to the controller's Update method and appropriate edits or deletes performed. The information the user wants to overlay in an edit are input into the newTerritory/Description/etc fields in the form (above the table).
Regarding the #Html.PagedListPager I found I had to pass back to the index method the same search criteria from the page, thus the excessive amount of parameters in the RouteValueDictionary.
#if (Model.zipCodeTerritory.Count > 0)
{
using (Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post))
{
#Html.HiddenFor(model => model.searchZip)
#Html.HiddenFor(model => model.searchDate)
#Html.HiddenFor(model => model.searchState)
<div id="cloneBox">
<div id="rw1">
#Html.LabelFor(model => model.newTerritory)
#Html.TextBoxFor(model => model.newTerritory, new { style = "width: 30px;padding-left:10px;", maxLength = 3 })
#Html.LabelFor(model => model.newDescription)
#Html.TextBoxFor(model => model.newDescription, new { style = "width: 250px;padding-left:10px;", maxLength = 30 })
#Html.LabelFor(model => model.newEffectiveDate)
#Html.TextBoxFor(model => model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" })
<div id="rw2" style="padding-top: 10px;">
#Html.LabelFor(model => model.newChannelCode)
#Html.DropDownListFor(model => model.newChannelCode, Model.ChannelCodes, " ")
#Html.LabelFor(model => model.newStateCode)
#Html.DropDownListFor(model => model.newStateCode, Model.StateCodes, " ")
</div>
</div>
</div>
<br/>
<div id="buttonDiv">
<button type="submit" id="CloneButton" name="button" value="clone">Apply New Data</button>
<button type="submit" id="deleteButton" name="button" value="delete">Delete Selected Items</button>
</div>
#*Display paging only if necessary*#
if (Model.pagedTerritoryList.Count >= 300)
{
<div id="pagingDiv">
#Html.PagedListPager(new StaticPagedList<Monet.Models.ZipCodeTerritory>(Model.zipCodeTerritory, Model.PagingMetaData) ,
Page => Url.Action("Index", new RouteValueDictionary()
{
{ "Page", Page},
{ "searchZip", Model.searchZip },
{ "searchActiveOnly", Model.searchActiveOnly },
{ "searchDate", Model.searchDate },
{ "searchState", Model.searchState },
{ "searchTerritory", Model.searchTerritory },
{ "searchChannel" , Model.searchChannelCode }
}), PagedListRenderOptions.DefaultPlusFirstAndLast)
</div>
}
<table id="thetable" class="tablesorter" >
<thead>
<th>#Html.CheckBox("SelectAll")</th>
<th>State</th>
<th>Territory</th>
<th>Zip</th>
<th>Description</th>
<th>Effective</th>
<th>End Date</th>
<th>Last Update Date</th>
<th>Channel</th>
<th></th>
</thead>
<tbody id="tableBody">
#for (int i = 0; i < Model.pagedTerritoryList.Count; i++)
{
<tr id="#(Model.lastEditedId == Model.pagedTerritoryList[i].Id ? "lastEdit" : "")">
<td>
#Html.CheckBoxFor(model => model.pagedTerritoryList[i].Update)
#Html.HiddenFor(model => model.pagedTerritoryList[i].Update)
</td>
<td>
#Html.DisplayFor(model => model.pagedTerritoryList[i].StateCode)
#Html.HiddenFor(model => model.pagedTerritoryList[i].StateCode)
</td>
<td>
#Html.DisplayFor(model => model.pagedTerritoryList[i].IndDistrnId)
#Html.HiddenFor(model => model.pagedTerritoryList[i].IndDistrnId)
</td>
<td>
#Html.DisplayFor(model => model.pagedTerritoryList[i].ZipCode)
#Html.HiddenFor(model => model.zipCodeTerritory[i].ZipCode)
</td>
<td>
#Html.DisplayFor(model => model.pagedTerritoryList[i].DrmTerrDesc)
#Html.HiddenFor(model => model.pagedTerritoryList[i].DrmTerrDesc)
</td>
<td>
#Html.DisplayFor(model => model.pagedTerritoryList[i].EffectiveDate)
#Html.HiddenFor(model => model.pagedTerritoryList[i].EffectiveDate)
</td>
<td>
#if (Model.pagedTerritoryList[i].EndDate.Date != DateTime.MaxValue.Date)
{
#Html.DisplayFor(model => model.pagedTerritoryList[i].EndDate)
#Html.HiddenFor(model => model.pagedTerritoryList[i].EndDate)
}
</td>
<td>
#Html.DisplayFor(model => model.pagedTerritoryList[i].LastUpdateDate)
#Html.HiddenFor(model => model.pagedTerritoryList[i].LastUpdateDate)
</td>
<td>
#Html.DisplayFor(model => model.pagedTerritoryList[i].ChannelCode)
#Html.HiddenFor(model => model.pagedTerritoryList[i].ChannelCode)
</td>
#if (ViewBag.SecurityLevel >= 4)
{
<td>
#Html.ActionLink("Edit", "Edit", new
{
id = Model.zipCodeTerritory[i].Id,
searchZip = Model.searchZip,
searchActiveOnly = Model.searchActiveOnly,
searchDate = Model.searchDate,
searchState = Model.searchState,
searchTerritory = Model.searchTerritory,
searchChannelCode = Model.searchChannelCode
})
#Html.HiddenFor(model => model.zipCodeTerritory[i].Id)
</td>
}
</tr>
}
</tbody>
</table>
}
}
EDIT
Per the comment below, here is the signature for the method the form is posting to. It contains an instance of the ZipCodeIndex that gets loaded on the page originally, plus the text from the button to determine whether we're doing a clone or delete
[HttpPost]
public ActionResult Update(ZipCodeIndex updateZip, string button)
{
Second Edit
Tried the method from this question but still receiving the original error message ("cannot create instance of an interface").
I was able to completely hack my way out of this, however I don't think it's the best solution. Would love it if someone could provide a better answer however I'll post this up here in the meantime.
Since the IPagedList object was built to hold a specific range of the List<> I just made a display property on my view model and used this on my view. This list, not the IPagedList gets posted back to the controller for the updates, so no interface weirdness occurs.
View Model
//Paging List objects
public IPagedList<ZipCodeTerritory> pagedTerritoryList { get; set; }
public List<ZipCodeTerritory> zipCodeTerritory { get; set; }
public List<ZipCodeTerritory> displayForPaging { get; set; }
Controller
//Convert list to IPagedList for pagining on Index
search.pagedTerritoryList = search.zipCodeTerritory.ToPagedList(pageNumber, pageSize);
search.displayForPaging = search.pagedTerritoryList.ToList();
View
<td>
#Html.CheckBoxFor(model => model.displayForPaging[i].Update)
#Html.HiddenFor(model => model.displayForPaging[i].Update)
</td>
<td>
.
.
I've found that models with interfaces as properties will need to have a ModelBinder created to determine which implementation to use.
ASP.Net MVC Custom Model Binding explanation
Basically, your binder will parse your model for you, determining which type of IPagedList<> to implement.

how to Call controller when click the button?

I want to call a controller when click the button in view.How can I do it in MVC?.
This is my first controller.
public ActionResult DetailForm()
{
graduandModel model = new graduandModel();
var ceremonyList = _ceremonyService.GetAllCeremonyByDate(DateTime.Now);
if (ceremonyList.Count == 0)
{
return Content("No ceremony can be loaded");
}
else
{
foreach (var c in ceremonyList)
{
model.AvailableCeremony.Add(new SelectListItem() {
Text = "Ceremony at " + c.ceremony_date.ToString(),
Value = c.ceremony_id.ToString() });
}
return View(model);
}
}
This is my view.
#{
Layout = "~/Views/Shared/_ColumnsThree.cshtml";
}
#model graduandModel
#using Nop.Web.Models.Hire;
#using Nop.Web.Framework;
#using Telerik.Web.Mvc.UI;
#using System.Linq;
<table>
<tr>
<td>
#Html.LabelFor(model => model.ceremony_id)
</td>
<td>
#Html.DropDownListFor(model => model.ceremony_id, Model.AvailableCeremony)
</td>
</tr>
<tr>
<td>
#Html.LabelFor(model => model.first_name):
</td>
<td>
#Html.EditorFor(model => model.first_name)
</td>
</tr>
<tr>
<td>
#Html.LabelFor(model => model.middle_name):
</td>
<td>
#Html.EditorFor(model => model.middle_name)
</td>
</tr>
<tr>
<td>
#Html.LabelFor(model => model.last_name):
</td>
<td >
#Html.EditorFor(model => model.last_name)
</td>
</tr>
<tr>
<td>
#Html.LabelFor(model => model.student_id):
</td>
<td>
#Html.EditorFor(model => model.student_id)
</td>
</tr>
<tr>
<td colspan="2" class="buttons">
<input type="submit" id="btnsearchgraduand" name="btnsearch"
class="searchbutton" value="#T("Search")" />
</td>
</tr>
</table>
Then When I click the search button I want to check the input data.
Should I write new controller like this
public ActionResult CheckDegreeDetails()
{
graduandModel model = new graduandModel();
var degreeList = _graduandService.GetGraduandByStudent(
model.ceremony_id, model.first_name,
model.middle_name, model.last_name);
return View(model);
}
Or...
I don't know how to call controller when click the button...
You want to wrap your user input fields and the submit button in a form. You can use an html helper that will also let you specify the controller action to call.
So modify your view:
...
#model graduandModel
#using Nop.Web.Models.Hire;
#using Nop.Web.Framework;
#using Telerik.Web.Mvc.UI;
#using System.Linq;
#using(Html.BeginForm("DetailForm", "ControllerName", FormMethod.Post, new {enctype="multipart/form-data"})
{
<table >
<tr>
<td >
#Html.LabelFor(model => model.ceremony_id)
</td>
...
//code omitted for brevity
...
<input type="submit" id="btnsearchgraduand" name="btnsearch" class="searchbutton" value="#T("Search")" />
})
Then in your controller you need to add the method to 'catch' this form. You're already using a strongly typed view so capturing the data is easy
[HttpPost]
public ActionResult DetailForm (graduandModel model)
{
//do what you need to do with the data here
//the model passed into this method's parameter should
//contain all the data from the editor templates in the view
}
The way you have your button, it won't make the request.
You can use jQuery/javascript.
HTML:
Javascript:
function callController()
{
$.get("YourController/Action",data, callBackMethod);
}
Or you can wrap your input, buttons etc... using #using(Html.BeginForm(..))

Categories