I have a view which contains a dropdown list and on dropdownlist item being selected I load a partial view. And when the form is submitted I want to be able to get both the values from main view and partial view during form submit.
Here is the main view
#model AdminPortal.Areas.Hardware.Models.CreateModule
#{
ViewBag.Title = "Create Module";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
}
#Html.ValidationSummary(true)
<fieldset class="form-horizontal">
<legend>Add a Module <small>Create</small></legend>
#using (Html.BeginForm("CreateModule", "Module", new{id="AddModuleForm"}))
{
#Html.ValidationSummary(true)
<div class ="controls">
<div class="input-block-level">#Html.TextBoxFor(model => model.ModuleId, new {#placeholder = "ModuleID"})</div>
<br/>
<div class ="input-block-level" id="selectedModuleTypeName">#Html.DropDownListFor(model => model.SelectedModuleTypeName, Model.TypeNames,"Select Moduletype", new{id = "ModuleList"})</div>
<br/>
<div id="partialDiv"></div>
</div>
<div class="form-actions" id="buttons">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
</fieldset>
<div>
#Html.ActionLink("Back to List", "ModuleList")
</div>
<script>
$("#buttons").hide();
$("#ModuleList").on("change", function() {
var modId = $(this).val();
$.get('#Url.Action("GetModulePropertyName", "Module")', { moduleTypeValue: modId }, function(result) {
$("#partialDiv").html(result);
});
//uncomment following section to check if the partial view is working properly
/*.done(function() { alert("done"); })
.fail(function() { alert("fail"); })
.always(function() { alert("completed"); });*/
});
$("#buttons").show();
</script>
and here is the partial view
#model IEnumerable<string>
#foreach(var names in Model)
{
<div class="input-block-level">#Html.TextBoxFor(m=>names, new{Value="", placeholder=names})</div>
<br/>
}
Here is my model
public class CreateModule
{
//Empty form to handle form serialization
public CreateModule()
{
}
[Required]
public string ModuleId { get; set; }
[DataType(DataType.DateTime)]
public DateTime DateEntered { get; set; }
[Required]
public string SelectedModuleTypeName { get; set; }
public IEnumerable<SelectListItem> TypeNames { get; set; }
public List<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
Here is the method that script in main view forwards to
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var moduleKindId = _repository.GetModuleKindId(moduleTypeValue);
var modulePropertyNames = _repository.GetModuleKindPropertyNames(moduleTypeValue);
return PartialView("GetModulePropertyName",modulePropertyNames);
}
and finally here is httppost method for the main view
[HttpPost]
public ActionResult CreateModule(CreateModule moduleV)
{
var module = new Module
{
ModuleTypeId = Convert.ToInt64(moduleV.SelectedModuleTypeName),
ModuleId = moduleV.ModuleId,
DateEntered = moduleV.DateEntered,
};
if (ModelState.IsValid)
{
_repository.AddModule(module);
Success("Module added successfully!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Something went wrong!");
return RedirectToAction("CreateModule", "Module", new { area = "Hardware" });
}
Current situation:
When the form is posted, the properties value of the model that is being passed via partial view is null. I get other values, like typename, Module ID.
What I'd want:
I also want to get the value of properties that is being passed via partial view.
You don't have any input field for the Properties property anywhere in your form. So it will always be null. That's normal.
Here's how you could proceed. Start by setting the correct navigational property so that the helper generates correct names of the corresponding input fields.
Also make sure that you are passing an IEnumerable<Property> model to the partial if you want to be able to get them back correctly:
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var moduleKindId = _repository.GetModuleKindId(moduleTypeValue);
IList<Property> model = ...
return PartialView("GetModulePropertyName", model.ToList());
}
and in your partial view use an editor template:
#model IList<Property>
#{
// This indicates the current navigational context to the helpers
ViewData.TemplateInfo.HtmlFieldPrefix = "Properties";
}
#Html.EditorForModel()
and the last step is to define a custom editor template for the Property class: ~/Views/Shared/EditorTemplates/Property.cshtml (note that the name and location of the template is important)
#model Property
<div class="input-block-level">
#Html.HiddenFor(m => m.Name)
#Html.TextBoxFor(m => m.Value, new { placeholder = Model.Name })
</div>
<br />
Try using the
List<Property>
as a model in your partial view and pass the CreateModule.Properties as model from your View
The problem is model binder can not figure out there
#Html.TextBoxFor(m=>names, new{Value="", placeholder=names})
belongs to as the "names" is not a property on your model class. If you need to bind to the CreateModule.Properties you need to change the partial view to emit textboxes with aproprate names, like this one:
#model IEnumerable<string>
#
{
int i=0;
}
#foreach(var names in Model)
{
<div class="input-block-level">#Html.TextBox("Properties[" + i + "].Value")</div>
<br/>
}
Related
I'm building a simple inventory application where the user create an order by selecting the items he wants to be delivered, fill a form with the name of the recipient and then the order get processed.
First of all I have an OrdineScarico class that stores a collection of InventoryItems to be processed (like a cart), a DeliverDetails class that stores the recipient name
public class OrdineScarico
{
private List<SingoloOrdine> ordineCollection = new List<SingoloOrdine>();
// collection methods
}
public class SingoloOrdine
{
public InventoryItem InventoryItem { get; set; }
public int Qty { get; set; }
}
public class DeliverDetails
{
[Required(ErrorMessage = "Inserire il nome del ricevente")]
public string Nome { get; set; }
}
and then a ConfermaScaricoViewModel class -in a different namespace- for wrapping them up
public class ConfermaScaricoViewModel
{
public OrdineScarico OrdineScarico { get; set; }
public DeliverDetails DeliverDetails { get; set; }
}
I have these action methods in the ScaricoController
public ViewResult Conferma()
{
return View(
new ConfermaScaricoViewModel
{
OrdineScarico = GetScarico(),
DeliverDetails = new DeliverDetails()
});
}
[HttpPost]
public ViewResult Conferma(ConfermaScaricoViewModel viewModel)
{
if (ModelState.IsValid)
{
repositoryProcessor.ScaricaItem(viewModel.OrdineScarico, viewModel.DeliverDetails);
viewModel.OrdineScarico.Clear();
return View("Confermato");
}
else
{
return View(
new ConfermaScaricoViewModel
{
OrdineScarico = GetScarico(),
DeliverDetails = new DeliverDetails()
});
}
}
where GetScarico() reads the OrdineScarico instance from the active session
private OrdineScarico GetScarico()
{
OrdineScarico scarico = (OrdineScarico)Session["Scarico"];
if (scarico == null)
{
scarico = new OrdineScarico();
Session["Scarico"] = scarico;
}
return scarico;
}
This is the view code:
#model GestioneMagazzino.WebUI.Models.ConfermaScaricoViewModel
#{
ViewBag.Title = "Conferma";
}
<h2>Conferma scarico</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<div class="form-group col-md-12">
<div class="row">
<label class="text-left">Ricevente:</label>
</div>
<div class="row">
#Html.TextBoxFor(model => model.DeliverDetails.Nome, new { #class="col-md-7" })
</div>
</div>
<div>
<input class="btn btn-primary" type="submit" value="Conferma" />
</div>
}
The problem is that when the POST action method is called, I get a null value for the OrdineScarico argument, and the ModelState is always false. I also tried adding an hidden field
#Html.HiddenFor(model => model.OrdineScarico)
but the OrdineScarico argument is always null when the POST method is called, while it's not when the controller renders the view.
Thanks,
Davide.
you must use #Html.HiddenFor(model => model.OrdineScarico)
also may be DeliverDetails has other field that you must set value for it.
you can use break point on line :
if (ModelState.IsValid)
in method post of Conferma.
run code and when code arrived to break point,
move cursor to the ModelState and wait to see data in it.
on values check all entities and find which property has error and you can see the message of error.
edit: cause you have not send any data for OrdineScarico. so it will be null in post action.
the OrdineScarico has a list member so
you should add this.
<input hidden name="OrdineScarico.ordineCollection[]" value="#value">
I'm having some issue getting my partial view BeginCollectionItem to save to the database. I have a form which has a dynamic number of "sections" that can be added to the page, and within each of these fields there is a text box where the user can enter the section name.
As far as I can tell the BeginCollectionItem within the partial view is working properly, however I cannot post the info to the database. In my other forms I have used a [bind()] to send the data to the database, is it possible to get this into a list and then post that via a bind?
I've included my code below:
The Model:
namespace project.Models.SetupViewModels
{
public class SOPTopTemplateBuilderViewModel
{
public List<Section> Section { get; set; }
}
public class Section {
public int SectionId { get; set; }
public string SectionText { get; set; }
public string TopTempId { get; set; }
}
}
cshtml:
#model IEnumerable<project.Models.SetupViewModels.Section>
#using (Html.BeginForm("SOPTopTemplateBuilder", "Setup", FormMethod.Post))
{
<div class="main-holder" id="theform">
#foreach(var item in Model)
{
#Html.Partial("_SectionCreator", item)
}
</div>
<button id="add" type="button">Add</button>
<div class="form-group submit-row">
<div class="col-12 float-to-right">
<input type="submit" class="btn btn-default" value="continue" />
</div>
</div>
}
#section Scripts {
<script>
$(document).ready(function () {
var url = '#Url.Action("AddSection")';
var form = $('form');
var recipients = $('#theform');
$('#add').click(function() {
$.post(url, function(response) {
recipients.append(response);
// Reparse the validator for client side validation
form.data('validator', null);
$.validator.unobtrusive.parse(form);
});
});
});
</script>
}
Partial View:
#model project.Models.SetupViewModels.Section
#using HtmlHelpers.BeginCollectionItemCore
#using (Html.BeginCollectionItem("Section"))
{
<div class="new-section">
<div>
<p>New Section</p>
#Html.HiddenFor(m => m.SectionId, new { #class="id" })
#Html.EditorFor(m => m.SectionText, new { #class = "form-control limit-form"})
</div>
</div>
}
Controller:
[HttpPost]
public PartialViewResult AddSection()
{
return PartialView("_SectionCreator", new Section());
}
[HttpGet]
public ActionResult SOPTopTemplateBuilder(){
List<Section> model = new List<Section>();
return View(model);
}
[HttpPost]
public ActionResult SOPTopTemplateBuilder(IEnumerable<Section> soptop)
{
if (ModelState.IsValid)
{}
return View(soptop);
}
Your use of Html.BeginCollectionItem("Section") perpends Section[xxxx] to the name attribute (where xxxx is a Guid) so that you generating inputs with
<input name="Section[xxxx].SectionId" .... />
which posts back to a model containing a collection property named Sections.
Since you already have a model with that property, you can change the POST method to
[HttpPost]
public ActionResult SOPTopTemplateBuilder(SOPTopTemplateBuilderViewModel soptop)
other options include
Using your existing POST method and omitting the "Section" prefix
using Html.BeginCollectionItem("") which will generate
name="[xxxx].SectionId"
Changing the POST method signature to public ActionResult
SOPTopTemplateBuilder(IEnumerable<Section> section) (where the
name of the parameter matches the name of the prefix)
Using a BindAttribute to 'strip' the prefix from the form values
public ActionResult SOPTopTemplateBuilder([Bind(Prefix = "Section")]IEnumerable<Section> soptop)
As a side note, your editing data, so you should always use a view model (say public class SectionViewModel) rather than using data models in your view. - What is ViewModel in MVC?
This is my code for my model
public class ManufacturerModel
{
public int ProductTYpeId { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public List<SelectListItem> manf { get; set; }
public Manufacturer manufacturer { get; set; }
}
This is my code in cshtml file
#using (Html.BeginForm("addmanufacturer", "Admin", FormMethod.Post, new { id = "formPageID" }))
{
<div class="row">
<label>Select Existing Manufacturer<span style="color: Red">*</span>:</label>
<div class="formRight">
#Html.DropDownList("Id", Model.manf)
</div>
</div>
<div class="row">
<label>Manufacturer Name<span style="color: Red">*</span>:</label>
<div class="formRight">
#Html.TextBoxFor(m => m.manufacturer.name)
#Html.ValidationMessageFor(m => m.manufacturer.name)
</div>
</div>
}
I am posting this form and when I am trying to fetch the value from the manfacurer ie - ManufacturerModel manufacturer = new ManufacturerModel();
using a model object all the value are coming out null.
in the text box If I replace it with m => m.Name then I am able to get proper value of Name.
can any one suggest what the problem is
I am using the manf to bind a dropdown. If In case I post back the form and the if it is return the value becomes blank, I need to refill the value..
public ActionResult addmanufacturer(string id)
{
if (id == null)
id = "0";
ManufacturerModel manufacturer = new ManufacturerModel();
manufacturer.ProductTYpeId = Convert.ToInt32(id);
manufacturer.manf = GetManf();
manufacturer.Id = -1;
return View(manufacturer);
}
I think problem will be becouse of:
#using (Html.BeginForm("Action", "Controller", FormMethod, HTML ATTRIBUTES )) { }
You probably want this overload:
#using (Html.BeginForm("Action", "Controller", Route Values, FormMethod, html attributes )) { }
Important is say him that you want route values and not a html atrributes, so try this
#using (Html.BeginForm("addmanufacturer", "Admin", new { id = "formPageID" }, FormMethod.Post, null )) { }
Hope, it helps.
M.
I have a very simple partial view and I am using ajaxform. In my partial view I have one sigle textArea and a Submit button.
The problem is whatever I write into the text area, It does not send the data to controller, rather it sends a text which is = "Comment".
If I dont write anything, the validation works perfect.
The Viewmodel:
public class NoteCommentViewModel
{
public Int32 Id { get; set; }
[Required(ErrorMessage="Hey, if you dont wanna write then why pressing the button !!")]
public string Comment { get; set; }
public DateTime CommentDate { get; set; }
public long UserId { get; set; }
public double Rating { get; set; }
public Guid NoteId { get; set; }
}
Controller:
//
//GET:/Notes/Comment/
public ActionResult Comment(string id)
{
ViewBag.NoteId = id;
var m = new NoteCommentViewModel()
{
NoteId = new Guid(id),
UserId = Convert.ToInt64(Session["LoginUserId"].ToString()),
//Comment=""
};
return PartialView(m);
}
//
//POST:/Notes/Comment
[HttpPost]
public ActionResult Comment(NoteCommentViewModel nvm)
{
NoteRatingComments comment = new NoteRatingComments();
comment.Comment = nvm.Comment; // Here I always have "Comment", regardless whatever I write in the page.
comment.EntryDate = DateTime.Now;
comment.NoteId = nvm.NoteId;
comment.UserId = nvm.UserId;
comment.Rating = 3.00;
dc.NoteRatingComments.AddObject(comment);
dc.SaveChanges();
return Content(Boolean.TrueString);
}
The view:
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Ajax.BeginForm("Comment", "Notes", null, new AjaxOptions
{
UpdateTargetId = "Comment-message",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
OnSuccess = "commentSuccess"
}, new { id = "commentForm" }))
{
<div style="margin-top:20px;">
<div id="commentSuccess"></div>
<div class="comentPic">
#{
long UserId = Convert.ToInt64(Session["LoginUserId"].ToString());
string Fullname = Session["LoginUserFullName"].ToString();
}
<img src='https://graph.facebook.com/#UserId/picture' height="100px" width="100px"/>
</div>
<div class="commentText">
#Html.HiddenFor(m => m.UserId)
#Html.HiddenFor(m=>m.NoteId)
#Html.TextAreaFor(m => m.Comment, new { style = "width:600px;height:120px;" })
<br />
#Html.ValidationMessageFor(m => m.Comment)
<div style="text-align:right;">
<input type="submit" value="Comment" name="comment" class="btn"/>
</div>
</div>
<div class="clear"></div>
</div>
}
Here is the screen shot of the error...for better understanding. I am writing "data" in the view but in the controller I am getting "Comment"..Where is it coming from??
WOuld be great if someone can help me to identify the problem...!!
The problem is that your submit button's name attribute is the same as the Comment textarea name attribute.
To resolve this you need to change the submit button's name to something else than "comment" or remove the name attribute from your submit button, so change:
<input type="submit" value="Comment" name="comment" class="btn"/>
To
<input type="submit" value="Comment" class="btn"/>
Because the Ajax.BeginForm uses the the jQuery .serializeArray() method which - because your submit button has a name and this input triggers the submit - also sends the submit button's value "Comment" to the server.
I am not sure what exactly your problem is. But the below code should work.
public class NoteCommentViewModel
{
public Int32 Id { get; set; }
[Required(ErrorMessage=" Your Error message")]
[DataType(DataType.MultilineText)]
public string Comment { get; set; }
//other properties
}
And in your View, Use it like this
#Html.EditorFor(m => m.Comment)
actually your code is very confusing. in your view u didnt use Model and seems you use m as your model and as i know this is completely wrong.i dont know your view is rendering but wherever you use m=>m.sth the second m must be nothin.instead you must define your #model NoteCommentViewModel at first of cshtml file and then use Model instead of second m
I have the following models:
class A
{
// ...some properties
public B InnerField { get; set; }
}
and
class B
{
public int Id { get; set; }
// ..other properties
}
and a page that has a model Class A and inside the page I have a partial view bound to Class B inside a form.
The value of the Id (in the partial view) is set correctly to the model's Id value (different from 0) but when I submit the page the model has the Id value 0. The Id value is not modified in the component or elsewhere.
Page
...other parts of main page
<%using (Html.BeginForm("ModifyHotel", "Hotel",
FormMethod.Post, new { enctype = "multipart/form-data"}))
{%>
<% Html.RenderPartial("~/Views/Shared/ModifyBaseItem.ascx",
new ModifyItemRequestBaseView() { ItemId = Model.Item.Id });%>
<%}%>
...other parts of main page
Partial View
...other parts of partial view
<br/>
Add Photo: <%:Html.FileBoxFor(x => x.PhotoFile, null)%>
<br/>
Add Video: <%:Html.FileBoxFor(x => x.VideoFile, null)%>
<br/>
<input type="submit" value="Submit changes" />
...other parts of partial view
What can I do to keep the value of the inner model when the post is made?
Thanks,
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
A model = new A() { InnerField = new B() { Id = 5 }};
return View(model);
}
[HttpPost]
public ActionResult Index(B model)
{
//on postback the model should have the value 5 here
return View();
}
}
View:
#model MvcApplication11.Models.A
#using (Html.BeginForm())
{
#Html.Partial("_IndexForm", Model.InnerField)
<input type="submit" />
}
Partial:
#model MvcApplication11.Models.B
#Html.EditorFor(m => m.Id)