I have three cascading dropdowns using Ajax to populate themselves on a view. Also in the view I call a partial view that iterates a container in the model and for each item calls another partial to display for editing the model properties appropriate according to the selected items in the dropdowns. I have built a complex model that contains the dropdown choices as well as the properties to be edited, and pass the portion of the model to the lowest level partial necessary to display the properties to be edited.
I want to update the db when the user clicks the submit button, through a normal Html.BeginForm, not by using Ajax. So I must wrap only the partials that display the properties in the form so that the existing Ajax functionality does not post to the controller. The problem is that although I can build this all, and the submit button connects to the controller as expected, the model returns to the controller null.
Does the model not come back from the partials up through the path that built them? Or more correctly stated, does the model not persist on the page even if it is built using partials?
I am sure someone is going to suggest that I post back using Ajax but that is not a best option for me. Or someone might ask what the html looks like on the page. Oddly, I can only see the complete html using browser developer tools, it does not show in a View Source selection.
Any ideas?
Moving this to where it belongs:
I'm not certain I understand. I get that the returning model needs to match the expected model but I don't get the explanation above. You say "So if your controller looks like this:", and of course it does, then what? That's wrong?
I have to look in dev tools for Chrome to see the actual html output and I see this in one case:
<input class="text-box single-line" id="status_Comments" name="status.Comments" type="text" value="Last Install to be completed this weekend">
So if the 'name' tag needs to look proper, I think it does. Am I wrong?
My date fields look like this:
<input type="text" value="8/19/2014" class="datepicker hasDatepicker" id="dp1391795955619">
So there's no 'name' tag but does have an id. Do I need to add a name?
Here's the code that generates the above:
#foreach (Status status in Model) {
string date = status.Date.HasValue ? status.Date.Value.ToShortDateString() : string.Empty;
<tr>
<td style="width: 175px;">#Html.DisplayFor(model => status.Name)</td>
<td style="width: 75px;">#Html.DisplayDropdownColorFor(model => status.StatusValue)</td>
<td style="width: 80px;"><input type="text" value="#date" class="datepicker" /></td>
<td style="width: 80px;"><input type="text" value="" class="datepicker" /></td>
<td style="width: 375px;">#Html.EditorFor(model => status.Comments)</td>
</tr>
}`
Geez, I sound like a desperate moron.
Thanks for your help.
Let's say you had a couple of classes like this:
public class SomeClass1
{
public string Name { get; set; }
public SomeClass2 class2Value { get; set; }
}
public class SomeClass2
{
public string Description { get; set; }
}
And you have a form in a Razor view:
#model SomeClass1
#using (Html.BeginForm()) {
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.class2Value.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.class2Value.Description)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
The actual HTML code is going to look something like this:
<form id="form1" action="/pageurl" method="POST">
<fieldset>
<div class="editor-label">
Name
</div>
<div class="editor-field">
<input type="text" name="Name" id="Name" value="Name val 1" />
</div>
<div class="editor-label">
Description
</div>
<div class="editor-field">
#Html.EditorFor(model => model.class2Value.Description)
<input type="text" name="class2Value.Description" id="class2Value_Description" value="Desc val 2" />
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</form>
If you notice, the name attribute of the form fields are set by MVC to follow the model. In the case of the nested object, it uses the underscore in the ID and period in the name to keep track of the where in the post model the values should go.
So if your controller looks like this:
[HttpPost]
protected ActionResult Save(SomeClass1 model)
{
//Then
// SomeClass1.Name == "Name val 1"
// and
// SomeClass1.class2Value.Description == "Desc val 2"
}
That's what #David was getting at in the comments... No matter how complex your page, when the <form> posts back to the server, the name attributes of the fields you are returning have to line up with the model that the controller action is expecting... or you will get null values.
You can even create a custom class for the post model, and as long as the name attributes line up with the model, and even though it was not the same as the #model at the top of the Razor view, MVC will use the key/value pairs and populate the data in your post model.
UPDATE
I updated the above to reflect a correction. The underscore is the delimiter in the ID attribute, and the period is used in the NAME attribute.
#leemid: An HTML form only posts fields with a name attribute. Only the name'ed inputs, selects, textareas, etc. will be passed back to the server when the form is submitted. And those names have to be inline with the model that is expected by the controller. For the sake of sounding redundant, that date input field you showed the example for, does not have a name attibute, and if the name were the same as the id, there would have to be a property in your model like
public string dp1391795955619 { get; set; }
for it to show up in your in controller action. You can set the name manually, as long as it's named the way MVC is expecting so that it can pass it into the model when posting.
In my example, the parts I was trying to highlight was the relationship between the class and property names, versus the name attributes MVC writes to the HTML document, and how it keeps all that stuff straight. In simple examples, you don't have to think about it. But in your complex scenario, it looks like you're going to have to manage some of the naming conventions manually so that MVC can understand what you're trying to do.
Related
I want to create an Http post method that will be able to process html forms of different inputs.
[HttpPost]
public async Task<IActionResult> Create(string content)
{
// process content here.
return View("Success");
}
My method should be able to support this form
<form action="url" method="POST">
<input type="text" name="firstName">
<input type="text" name="lastName">
<button type="submit">Submit</button>
</form>
as well as this form
<form action="url" method="POST">
<input type="text" name="name">
<input type="text" name="surname">
<input type="text" name="age">
<button type="submit">Submit</button>
</form>
How can I modify my method so that the content will be populated for both of these cases?
To process forms with unknown fields, you can use the FormCollection type as a parameter in the Create method. The FormCollection type represents a collection of keys and values that are sent as the HTTP request body when the form is submitted. You can use this type to capture all the form data, regardless of the number or names of the fields.
To access the form data, you can use the formData object. For example, you can access a field with the name "firstName" using the following syntax: formData["firstName"].
PdfController.cs:
public class PdfController: Controller
{
[HttpPost]
public IActionResult Create(IFormCollection formData)
{
// process form data here
var surName= formData["surname"].ToString();
return View("Create", (FormCollection)formData);
}
}
Form:
<form action="/pdf/create" method="POST">
<input type="text" name="name">
<input type="text" name="surname">
<input type="text" name="age">
<button type="submit">Submit</button>
</form>
Create.cshtml:
#model FormCollection
<h1>Form Data</h1>
<table>
#foreach (var key in Model.Keys)
{
<tr>
<td>#key</td>
<td>#Model[key]</td>
</tr>
}
</table>
View:
How can I modify my method so that the content will be populated for
both of these cases?
Well, based on your scenario and requirement,we have two option other than [FormBody] pattern,to deal with completely dynamic value, we can use IFormCollection and a Dictionary.
Our goal is to handle any dynamic data which we will finally add into our Dictionary using its Key and Value.
Completely Dynamic Form Request Example:
[HttpPost]
public IActionResult Create(IFormCollection dynamicData)
{
var dynamicFormDataDictionary = new Dictionary<string, object>();
foreach (var item in dynamicData)
{
dynamicFormDataDictionary.Add(item.Key, item.Value);
dynamicFormDataDictionary.Remove("__RequestVerificationToken");
}
return View("DynamicOutput", dynamicFormDataDictionary);
}
Note: We cannot use FormCollection because it will encounter runtime exception as it has Interface of IFormCollection type. .
Bind Dynamic Data And Display in View:
Controller:
public IActionResult DynamicOutput()
{
return View();
}
View:
#model Dictionary<string, object>
<h4>Dynamic Data</h4>
<table class="table">
#foreach (var key in Model.Keys)
{
<tbody>
<tr>
<th>
#key
</th>
<td>
#Model[key]
</td>
</tr>
</tbody>
}
</table>
Output:
Alternative Way:
We have another way, which is using Request.Form but here we need to define our data fields. In following way we can get the data:
var input1 = Request.Form["firstName"].GetValue();
var input2 = Request.Form["name"].GetValue();
Note: Point to remember, in this way, let's say, you have two request from we would define all the property together and allow null-value. Therefore, we would get our desired value from one request form while other we would remain empty.
I'm trying to update only part of my page from a partial View.
It works perfectly fine if i use this
Click heeeeeeeere
But this is a simple get and i'd like to actually post some data and do something with it. I wrote a form, set its method to post like this.
<form method="post" data-ajax="true" data-ajax-method="post" data-ajax-complete="completed" data-ajax-update="#panel" >
<div class="row">
id : #Html.TextBoxFor(x => x.customer.ID)
</div>
<div class="row">
Name : #Html.TextBoxFor(x => x.customer.Name)
</div>
<div class="row">
<input type="submit" value="send data" />
</div>
</form>
BUT this updates my entire page so my entire page is just the little partial view thats supposed to be updated.
a first observation, it seems you are missing the data-ajax-url from the second form .
Saying that, then in your Razor view you should include on the top of the page
#page "{handler?}"
This will allow you to pass additional information to your handler, then in your form you can simply include something like
<input type="hidden" name="id" value="send value"/>
where value is the value you want to pass and name is how the handler will identify what property to bind this to, then in your .cshtml.cs page your handler should look something like this
public IActionResult OnPostPartial(string id) {...//do something here id == "send value"}
hope this helps
I'm trying to create a partial view to save on the amount of boilerplate code I need when creating forms. A simple version of what I have is as follows...
#model FormRowViewModel
<div class="form-group row">
<label for="#Model.PropertyName" class="col-lg-2 col-form-label">#Model.Label</label>
<div class="col-lg-10 input-group">
<input type="text" asp-for="#Model.PropertyName" class="form-control">
</div>
</div>
...where FormRowViewModel looks like this...
public class FormRowViewModel {
public FormRowViewModel(string propertyName, string label) {
PropertyName = propertyName;
Label = label;
}
public string PropertyName { get; set; }
public string Label { get; set; }
}
The idea is to use it in a view like this...
#model ContactViewModel
<form method="post" asp-controller="Home" asp-action="Index">
<div asp-validation-summary="All" class="text-danger"></div>
#await Html.PartialAsync("_FormRow", new FormRowViewModel("UserName", "Your name"))
#await Html.PartialAsync("_FormRow", new FormRowViewModel("Email", "Email"))
#await Html.PartialAsync("_FormRow", new FormRowViewModel("Telephone", "Telephone"))
<div class="form-group row">
<div class="offset-sm-2 col-lg-10">
<button type="submit" class="btn btn-primarySubmit</button>
</div>
</div>
</form>
This works, in that it creates the HTML (almost) as expected, but has two problems...
1) The generated HTML includes value attributes that set the content of the textboxes to the property names...
<input type="text" class="form-control" id="PropertyName"
name="PropertyName" value="UserName">
2) Whatever I put in the textboxes, when the form is posted back to the server, the view model properties are all empty strings. Even the property names that were added don't come through.
In case it helps, here is the controller action that handles the view...
public IActionResult Index() =>
View();
[HttpPost]
public IActionResult Index(ContactViewModel vm) {
if (!ModelState.IsValid) {
return View(vm);
}
// Next line added so I can see when it worked
return RedirectToAction(nameof(Privacy));
}
Anyone know what I'm doing wrong? Thanks
Your whole approach here is incorrect. It seems what you're looking for is editor templates. Essentially, you need to create partial views in Views\Shared\EditorTemplates that correspond with types or members of the DataType enum, and add your custom HTML there. For example, you can create a String.cshtml view:
#model string
<div class="form-group row">
<label asp-for="#Model" class="col-lg-2 col-form-label"></label>
<div class="col-lg-10 input-group">
<input asp-for="#Model" class="form-control">
</div>
</div>
Then, for any string property:
#Html.EditorFor(x => x.MyStringProp)
And your custom template will be used, with the proper name binding.
Alternatively, you can create custom taghelpers, but the methodology for that is a bit more complicated, since you'll need to handle the HTML generation in code. If you're interested in that approach, though, look at the source for the built-in tag helpers and create your own based on that.
I have a strongly typed partial view that consists of one form field. I would like to add this partial view into all of the existing forms on a website(around 25 forms). I am having trouble figuring out how to reference the model information from the existing form controllers. I am adding the partial view like so. #{Html.RenderPartial("_HoneyPotFormField");} what I am looking to do is something to the effect of if HoneyPotFormFieldModel.Comments is null do something.
Here is my View code.
#model Web.Models.HoneyPotFormFieldModel
<div class="form-comments">
<div class="title">
<span>Comments</span>
</div>
<div class="col-md-10">
#Html.EditorFor(model => model.Comments)
</div>
</div>
here is my Model code
public class HoneyPotFormFieldModel
{
public string Comments { get; set; }
}
I have problem in updating object with nhibernate in ASP.Net MVC4
Im doing the update in this scenario:
the application loads an object in the first session
the object is passed up to the UI tier
some modifications are made to the object
the object is passed back down to the business logic tier
the application persists these modifications by calling SaveOrUpdate()
all this happen only in one session. I have static a class name NHibernateSessionPerRequest
and its constructor is static (singeleton)
[HttpPost]
public ActionResult Edit(Menu menu)
{
if (ModelState.IsValid)
{
repository.SaveOrUpdate(menu);
TempData["message"] = string.Format("{0} has been saved", menu.Name);
return RedirectToAction("Index");
}
else
{
// there is something wrong with the data values
return View(menu);
}
}
but menu ID is zero. and doesnt have its original ID (id is type of GUID). and SaveOrUpdate() alway treat it as a new object and save it not update it.
here is Edit.cshtml:
#model MyApp.Domain.Entities.MenuComponent
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<h2>Edit #Model.Name
</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>MenuComponent</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
how can I update the object?
From your comments, I see two problems :
it seems you have removed #Html.HiddenFor(model => model.ID) from your markup. You should put it back, or your ID won't be stored in the page to be posted back to your Controller.
Your ID code is public virtual Guid ID { get; private set; } You should remove the private modifier on the setter. I guess it prevents the ModelBinder to set the property when receiving the posted data
from what you have posted it seems that you are returning the entity to the view and there isn't any concept of view model being used.
Firstly
usually entities are defined with private setters which would prevent the id being posted back to the Edit action if you use the entity itself.
Secondly (i am not certain about this)
since you are getting the object in the post back and using a session per request (assumption since it is quite common) nhibernate might treat it as a new entity. I am highly doubtful for the second point but will try re creating this and update the answer