MVC5 Parent Child Single Edit Form [duplicate] - c#

I am trying to return the results of a table back to the controller for further manipulation. Once returned to the controller the value shows as null. In the past I have been able to use #Html.HiddenFor to return the values but it doesn't seem to be working in this instance. Not sure what I am doing wrong here. Any help is greatly appreciated.
#model IEnumerable<Project.Models.Item>
#{
ViewBag.Title = "Welcome to The Project";
}
#using (Html.BeginForm("UpdateQuality", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<div class="row">
<div class="form-group">
<table class="table table-bordered">
<tr>
<th>#Html.DisplayNameFor(m => m.Name)</th>
<th>#Html.DisplayNameFor(m => m.SellIn)</th>
<th>#Html.DisplayNameFor(m => m.Quality)</th>
</tr>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>#Html.DisplayFor(m => m.ElementAt(i).Name)</td>
<td>#Html.DisplayFor(m => m.ElementAt(i).SellIn)</td>
<td>#Html.DisplayFor(m => m.ElementAt(i).Quality)</td>
#Html.HiddenFor(m => m.ElementAt(i).Name)
#Html.HiddenFor(m => m.ElementAt(i).SellIn)
#Html.HiddenFor(m => m.ElementAt(i).Quality)
</tr>
}
</table>
<div class="form-group">
<div style="margin-top: 50px">
<input type="submit" class="btn btn-primary" value="Advance Day"/>
</div>
</div>
</div>
</div>
}
And here is the controller which returns null.
public ActionResult UpdateQuality(List<Item> Items )
{
return View("Index", (object)Items);
}

You cannot use ElementAt() in a HtmlHelper method that generates form controls (look at the name attribute your generating - it does not match your model).
Either change the model to be IList<T>
#model List<Project.Models.Item>
and use a for loop
#for (int i = 0; i < Model.Count; i++)
{
....
#Html.HiddenFor(m => m.[i].Name)
....
or change use a custom EditorTemplate for typeof Item, and in the main view, use #Html.EditorFor(m => m) to generate the correct html for each item in the collection.

Related

Multiple forms in one view same action

i have problem creating view with multiple forms with in same view where i have get action for data view and post action for data collection.
Here is my main view:
#model Models.BuildingNewBuildingViewModel
#{
ViewBag.Title = "Index";
}
#if (TempData["Added"] != null)
{
<div class="alert alert-success alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
#TempData["Added"]
</div>
}
#if (TempData["AddError"] != null)
{
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
#TempData["AddError"]
</div>
}
<h1>Svěřené budovy pro: #ViewBag.Name</h1>
<table class="table-bordered table-responsive table table-condensed">
<thead>
<tr>
<th>Id budovy</th>
<th>Podlaží</th>
<th>Použití</th>
<th>Výměra</th>
<th>Datum</th>
<th>Administrace</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.NewBuildingViewModels.Count; i++)
{
using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #class = "form-horizontal", id = "Form" }))
{
Html.RenderPartial("IndexPartial", Model.NewBuildingViewModels[i]);
}
}
</tbody>
</table>
Here is form:
#model Models.NewBuildingViewModel
#Html.HiddenFor(x => x.Building, Model.Building)
<tr>
<td>#Model.Building</td>
<td>
#if (Model.Floor > 0)
{
#Model.Floor
#Html.HiddenFor(x => x.Floor)
}
else
{
#Html.TextBoxFor(x => x.Floor, new {#Value = ""})
#Html.ValidationMessageFor(x => x.Floor)
}
</td>
<td>
#if (Model.Usage != null)
{
#Model.Usage
#Html.HiddenFor(x => x.Usage)
}
else
{
#Html.TextBoxFor(x => x.Usage)
#Html.ValidationMessageFor(x => x.Usage)
}
</td>
<td>
#if (Model.Size > 0)
{
#Model.Size
#Html.HiddenFor(x => x.Size)
}
else
{
#Html.TextBoxFor(x => x.Size, new {#Value = ""})
#Html.ValidationMessageFor(x => x.Size)
}
</td>
<td>
#if (Model.Date != DateTime.MinValue)
{
#Model.Date.Date
#Html.HiddenFor(x => x.Date)
}
else
{
#Html.TextBoxFor(x => x.Date)
#Html.ValidationMessageFor(x => x.Date)
}
</td>
<td>
#if (!Model.NewBuildingDataInDatabase)
{
<button type="submit">Odeslat</button>
}
else
{
if (Model.MarksInDatabase)
{
<i class="fa fa-check checkIcon" aria-hidden="true"></i>
}
else
{
Zadat Hodnocení
}
}
</td>
</tr>
}
And my problem is when i send non valid data to controller. Validation message shows for all forms. I want to see all forms but validation messages only for form which sended data.
Here is my action:
[HttpPost]
public ActionResult Index(NewBuildingViewModel buildingNewBuildingViewModel)
{
BuildingForIcoCommand buildingForIcoCommand = new BuildingForIcoCommand();
var buildingsForIco = buildingForIcoCommand.GetBuildingListForIco(AppContext);
if (ModelState.IsValid)
{
ViewBag.Name = User.Identity.Name;
ViewModelBuilder viewModelBuilder = new ViewModelBuilder();
DaoBase<NewBuilding, int> daoBase = new DaoBase<NewBuilding, int>(AppContext.NhSession);
daoBase.Save(viewModelBuilder.CreateNewBuilding(buildingNewBuildingViewModel));
return View(buildingForIcoCommand.GetBuildingListForIco(AppContext));
}
return View("Index",buildingsForIco);
}
All of your form fields will have the name and id attributes because you are rendering them in a partial. This is why they are all flagged as invalid.
Any good solution will probably involve ajax.
One approach to revolving this would be to use a single edit form. Each rendered item would have an edit button, clicking this button would populate the form with the item's data via javascript. This could be a modal or similar. This approach works well with jQuery / unobtrusive validation.
Another approach would be to return a custom response from your controller eg status 400 and the invalid fields derived from the model state. You can then show a suitable error message in the right field.

Child Actions are not allowed to perform redirect actions Partial Views

On my Main View I have 4 partial views.. two are tables.. the others are create forms.
Partial View Table 1
#model IEnumerable<ProjectName.Models.code_AutoMake>
<h3>Auto Make List</h3>
<table id="Auto-Make-Table" class="table table-bordered table-striped">
<thead>
<tr>
<th class="col-md-5">
#Html.DisplayNameFor(model => model.AutoMake)
</th>
<th class="col-md-5">
#Html.DisplayNameFor(model => model.Active)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.AutoMake)
</td>
<td>
#Html.DisplayFor(modelItem => item.Active)
</td>
#if (!item.Active)
{
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.MakeID }) |
Activate
</td>
}
else
{
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.MakeID }) |
Deactivate
</td>
}
</tr>
}
</tbody>
</table>
Partial View Table 2
#model IEnumerable<ProjectName.Models.code_Funding>
<h3>Funding List</h3>
<table class="table table table-bordered table-striped">
<tr>
<th>
#Html.DisplayNameFor(model => model.Funding)
</th>
<th>
#Html.DisplayNameFor(model => model.Active)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Funding)
</td>
<td>
#Html.DisplayFor(modelItem => item.Active)
</td>
<td>
#Html.ActionLink("Edit", "Edit", "code_Funding",new { id=item.FundID }, null) |
</td>
</tr>
}
</table>
Partial View 1 Create
#model ProjectName.Models.code_AutoMake
#using (Html.BeginForm("Create", "code_AutoMake", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h3>Add Auto Make</h3>
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-sm-12 col-md-3">
#Html.Label("Auto Make")
#Html.EditorFor(model => model.AutoMake, new {htmlAttributes = new {#class = "form-control"}})
</div>
<div class="col-sm-12 col-md-3">
#Html.Label("Active")
<div class="checkbox">
#Html.EditorFor(model => model.Active)
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-12 col-md-3">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Partial View 2 Create
#model ProjectName.Models.code_Funding
#using (Html.BeginForm("Create", "code_Funding", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<h3>Add Funding</h3>
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-sm-12 col-md-3">
#Html.Label("Funding")
#Html.EditorFor(model => model.Funding, new {htmlAttributes = new {#class = "form-control"}})
</div>
<div class="col-sm-12 col-md-3">
#Html.Label("Active")
<div class="checkbox">
#Html.EditorFor(model => model.Active)
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-12 col-md-3">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Main View
<div id="AutoMake" class="tab-pane fade active in">
<div id="AutoMake-Index">#{Html.RenderAction("Index", "code_AutoMake");}</div>
<hr/>
#{Html.RenderAction("Create", "code_AutoMake");}
</div>
#*Funding*#
<div id="Funding" class="tab-pane fade">
#{Html.RenderAction("Index", "code_Funding");}
<hr/>
#{Html.RenderAction("Create", "code_Funding");}
</div>
Now here is the scenario.. When I want to create a new autoMake.. I fill out the form and hit submit.. this goes through fine.. until I get back to the Main View.. specifically this line:
#{Html.RenderAction("Create", "code_Funding");}
and I get a runtime error saying:
Child Actions are not allowed to perform redirect actions
I have debugged.. and for some reason.. the HttpPost Create action for code_Funding is being hit.. even when I'm not filling out the create form for code_funding.. How is that possible?
Here are my Create Methods for code_autoMake and code_funding:
code_Funding
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "FundID,Funding,Active")] code_Funding code_Funding)
{
try
{
if (ModelState.IsValid)
{
db.code_Funding.Add(code_Funding);
db.SaveChanges();
return RedirectToAction("EditDDL", "tblNewAutos");
}
}
catch (DbEntityValidationException ex)
{
foreach (var entityValidationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in entityValidationErrors.ValidationErrors)
{
Response.Write("Property: " + validationError.PropertyName + " Error: " + validationError.ErrorMessage);
}
}
}
return RedirectToAction("EditDDL", "tblNewAutos");
}
code_autoMake
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "MakeID,AutoMake,Active")] code_AutoMake code_AutoMake)
{
if (ModelState.IsValid)
{
db.code_AutoMake.Add(code_AutoMake);
db.SaveChanges();
return PartialView("~/Views/PartialViews/_AutoMakeCreate.cshtml");
}
return RedirectToAction("EditDDL", "tblNewAutos");
}
Why when I try and create a new automake.. both HttpPost Create methods are hit?
Any help is appreciated.
Well, the problem is following. In your main view you have got this code:
...
#{Html.RenderAction("Create", "code_AutoMake");}
...
Which triggers the Create action which finishes with the following line of code if ModelState.IsValid == false:
return RedirectToAction("EditDDL", "tblNewAutos");
That is obviously a bad idea. Why? You are already in a process of rendering a parent view. Child actions might be a bit confusing at first because they are not real actions - no client/server communication. You are still on the server side. Therefore no redirect is allowed in the child action.
Solutions
First of all, I am not quite sure what you want to achieve so my solution recommendation might be a bit off, but let's see.
Option 1
You may want to use two different actions. One that is called on submit of the form and another one that is called from your main view. The latter one should not make a redirect - instead it should wisely choose which view to render based on the ModelState.IsValid if this is really what you need.
Option 2
There is a hack way which allows you to make redirect from a child action. Instead of making a redirect, only store information about required redirect for instance in HttpContext.Items collection. Then, implement an ActionFilter and in its OnResultExecuted event, check if the redirect request was set to the HttpContext.Items. If so, make a redirect. The ActionFilter should be applied on the parent action, not on the child action.
#{Html.RenderAction("Create", "code_Funding");}
in this RenderAction Method is call the GET request but, your controller you written only post method. and you write the
[ChildActionOnly]
public ActionResult Create(string parm)
{
reurn view()
}
and use [ChildActionOnly] this attribute is allowing restricted access via code in View.
Check and reply me..

Binding to a view model with a nested list of type class

I'm trying to bind to a view model for a POST request into my controller action, and it appears the correct parameters are being sent, but my viewmodels data seems to be null despite trying multiple things. I have the following view model that I'm binding to from within my view:
public class OrderDetailViewModel {
public List < OrderDetail > OrderDetails;
}
The data loads into my views properly, here's what the main view looks like:
<form class="form-horizontal" id="update-orderdetail" action="/orderdetail/update" method="post">
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "alert alert-danger" })
<table class="table table-striped table-bordered table-hover results">
<thead>
... tr tags ...
</thead>
<tbody>
#Html.EditorFor(x => x.OrderDetails)
</tbody>
</table>
<div class="form-group">
<div class="pull-right">
<button type="reset" class="btn btn-default">Reset</button>
<button type="submit" class="btn btn-success">Save</button>
</div>
</div>
</form>
Here's what my EditorTemplates/OrderDetail.cshtml looks like:
#model CorinthVendorPortal.Models.OrderDetail
<tr>
<td>#Html.DisplayFor(x => x.PurchaseOrder)</td>
<td class="editable">
#Html.HiddenFor(x => x.ID)
#Html.HiddenFor(x => x.PurchaseOrder)
#Html.TextBoxFor(x => x.Price, new { #type = "number", #class = "form-control" })
</td>
<td>#Html.DisplayFor(x => x.StockCode)</td>
<td>#Html.DisplayFor(x => x.MStockDes)</td>
<td>#Html.DisplayFor(x => x.OrderQtyOrig)</td>
<td class="editable">
#Html.TextBoxFor(x => x.OrderQtyCur, new { #type = "number", #class = "form-control" })
</td>
<td>#Html.DisplayFor(x => x.DueDateOrig)</td>
<td class="editable">
#Html.TextBoxFor(x => x.DueDateCur, new { #type = "date", #class = "form-control" })
</td>
<td>#Html.DisplayFor(x => x.ShipDateOrig)</td>
<td class="editable">
#Html.TextBoxFor(x => x.ShipDateCur, new { #type = "date", #class = "form-control" })
</td>
</tr>
Here's what the OrderDetail controller's Update method looks like (in which my model's OrderDetails property is null):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Update(OrderDetailViewModel model) {
var l = model; // model.OrderDetails is always null
....
return null;
}
Here are the parameters that are being POSTed:
OrderDetails[0].ID:1
OrderDetails[0].PurchaseOrder:0246298
OrderDetails[0].Price:8.85000
OrderDetails[0].OrderQtyCur:200.000000
OrderDetails[0].DueDateCur:
OrderDetails[0].ShipDateCur:
OrderDetails[1].ID:2
OrderDetails[1].PurchaseOrder:0246298
OrderDetails[1].Price:1.40000
OrderDetails[1].OrderQtyCur:750.000000
OrderDetails[1].DueDateCur:
OrderDetails[1].ShipDateCur:
Any help or advice would be appreciated.
You are using field in your view model, but MVC binding works only with properties.
Change
public class OrderDetailViewModel {
public List<OrderDetail> OrderDetails;
}
to
public class OrderDetailViewModel {
public List <OrderDetail> OrderDetails { get; set; }
}
and your code should start working.

MVC RadioButtonFor How & What to POST back to Controller

New new to C# and MVC, so apologies in advance for posting something which is probably obvious.I have looked at similar answers but still can't see how and what value in the RadioButtonFor should be used so that it can be POSTed back to the controller.
Controller
[HttpPost]
public ActionResult Score(ExamViewModel exam)
{
const int AddCorrect = 1;
var correct = from c in db.Answers
where c.ID == 1
select c.CorrectAnswer;
if (ModelState.IsValid)
{
if (correct == exam.CorrectAnswer)
{
ViewData["message"] = "Correct Answer!";
return View("Results");
}
else
{
var feedback = from g in db.Questions
where g.ID == 1
select g.GrammarPoint;
ViewData["message"] = "That's not the right answer.";
ViewData["feedback"] = feedback;
return View("Results");
}
}
return View("Results");
And the View
#model AccessEsol.Models.ExamViewModel
#{
ViewBag.Title = "TakeTest";
}
<h2>TakeTest</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<div class="display-label">
<h3>Click the correct answer:</h3>
</div>
<div class="display-field">
<strong>#Html.DisplayFor(model => model.Text.Text )</strong>
</div>
#Html.DisplayFor(model => model.Foil1.Foil1)
#Html.RadioButtonFor(model =>model.Foil1, "Incorrect" )
#Html.DisplayFor(model => model.Foil2.Foil2)
#Html.RadioButtonFor(model => model.Foil2, "Incorrect" )
#Html.DisplayFor(model => model.Foil3.Foil3)
#Html.RadioButtonFor(model => model.Foil3, "Incorrect")
#Html.DisplayFor(model => model.CorrectAnswer.CorrectAnswer)
#Html.RadioButtonFor(model => model.CorrectAnswer, "Correct")
<p>
<input type="submit" value="Submit Answers" />
</p>
</fieldset>
}
I also tried to pass a string from the CorrectAnswer into the Score Controller without success.Much appreciated if you can point to how can checked RadioButton value can be passed back to the Controller?
You should not have 3 different properties but instead a single one that will contain the answer. This will allow you to group the radio buttons and be able to select only one of them:
#model AccessEsol.Models.ExamViewModel
#{
ViewBag.Title = "TakeTest";
}
<h2>TakeTest</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<div class="display-label">
<h3>Click the correct answer:</h3>
</div>
<div class="display-field">
<strong>#Html.DisplayFor(model => model.Text.Text)</strong>
</div>
#Html.DisplayFor(model => model.Foil1.Foil1)
#Html.RadioButtonFor(model => model.Answer, "1")
#Html.DisplayFor(model => model.Foil2.Foil2)
#Html.RadioButtonFor(model => model.Answer, "2")
#Html.DisplayFor(model => model.Foil3.Foil3)
#Html.RadioButtonFor(model => model.Answer, "3")
#Html.DisplayFor(model => model.CorrectAnswer.CorrectAnswer)
#Html.RadioButtonFor(model => model.Answer, "4")
<p>
<input type="submit" value="Submit Answers" />
</p>
</fieldset>
}
and then in your controller action check if the Answer property value that is sent is the correct one for this question. As you can see from the view we have multiple answer for the question and the value is what will get sent to the server. Usually you will use the ID of the answer as value and on the server you can compare whether this is the correct answer for the question.

inline models editor MVC3

Ola.. I have a problem with editing of few models in table. I have a List of models on page (with foreach), something like
<table id="grid-table" >
#foreach (var image in ViewBag.Images)
{
<tr>
<td >
<a href="#Url.Action("ShowFullImage", new { id = #image.ID })" rel="lightbox[roadtrip]" title="#image.Description" >
<img src="#Url.Action("ShowImageThumbneil", new { id = #image.ID })" alt="#image.AlternateText" />
</a>
</td>
<td >
#using (Html.BeginForm("SaveImageInfo", "Admin", FormMethod.Post))
{
#Html.TextAreaFor(m => m.Description) <br />
#Html.TextBoxFor(m => m.AlternateText) <br />
#Html.LabelFor(m => m.ID)
<div id="item-post" >
<input title="Подтвердить" type="submit" value="Подтвердить" />
</div>
}
</td>
</tr>
}
and I want to have a way to edit ONE model item. In controller I have something like this:
[HttpPost]
public ActionResult SaveImageInfo(ImageModel imageModel)
{
Image img = _core.GetImageByID(_client, imageModel.ID);
img.AlternateText = imageModel.AlternateText;
img.Description = imageModel.Description;
_core.SaveImageInfo(_client, img);
return View();
}
but, of course, it's not works..
Can somebody help me?
Change #Html.LabelFor(m => m.ID) with #Html.HiddenFor(m => m.ID). Contents of labels are not sent with POST, but contents of hidden fields are..
I am guessing a bit, as I don't know exactly what problem you are seeing, but if it is to do with it trying to redirect, following the save, then try changing from:
Html.BeginForm
to
Ajax.BeginForm

Categories