I have a main view which contains a partial view. The model of partial view has 1 property called "HttpPostedFileBase file", together with other properties
However when the main-view get posted, all the other properties in that model get correct value, but the "HttpPostedFileBase file" is null. I already set the name of to be the same as parameter. Also even Request.Files give me 0 number of files.
What have I done wrong?
P.S. My main-view actually has 2 partial views. One PV has the same model as main-view. The 2nd one is what I mentioned above. The model contains a list of objects and HttpPostedFileBase file. Code like this:
public class MyPartialViewModel
{
public List<MyObject> objInfos { get; set; }
public ICollection<HttpPostedFileBase> file { get; set; }
}
And in the PV I looply use #Html.EditFor(model=>model.objInfos[i]) to bind it to a template.
So in main-view post method, I could get "objInfos" list & all ojects' value correct. But just NULL for "file".
Try adding enctype = "multipart/form-data" as one of the htmlAttributes in the #Html.BeginForm() helper.
EDIT: To not make a visual full postback:
AJAX FILE Upload in MVC3 using iFrame
In this right moment I'm using this approach, actually implementing this as we write.
I've just come across the same problem, and have worked around it by placing an extra parameter in the ActionResult to take the file upload parameter.
[Authorize]
[HttpPost]
public ActionResult MyActionResult(MyViewModel viewModel, HttpPostedFileBase myFileToUpload)
{
myViewModel.PartialViewModel.MyFileToUpload = myFileToUpload;
//manually bind MyFileToUpload;
//for some reason, because it's in the Partial View and it's of type HttpPostedFileBase, it won't post.
return View(viewModel);
}
Just for context:
My PartialViewModel contains the code:
public HttpPostedFileBase MyFileToUpload{ get; set; }
and PartialView the code:
<div class="editor-item">
<div class="editor-label">
#Html.LabelFor(model => model.MyFileToUpload)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MyFileToUpload)
</div>
</div>
Finally, I have an EditorTemplate Views/Shared/EditorTemplates/HttpPostedFileBase.cshtml:
#model HttpPostedFileBase
<input type="file" name="#ViewData.ModelMetadata.PropertyName" />
It's a workaround, not a solution, but does enable you to get the file uploaded while leaving the HttpPostedFileBase in the PartialViewModel.
Related
I am late in on a project where possibly the most important requirements have been ignored until the 11th hour.
The application allows suppliers to apply to become contractors for a company via a fairly in depth online application form.
The application takes the form of question sections that make up a left sidebar nav and each section has a number of questions.
The requirements that have been ignored up until now are that each applicant who fills out a form online has an application type, e.g. construction, catering etc. and the requirements state that the application is configurable so that only certain sections appear to a certain application type and certain questions within a section should be configurable to appear to a particular application type.
Configuring the sections is easy but configuring the questions is not so easy. At the moment everything is hardcoded into razor views and EditorFor templates. Basically there is one EditorFor per ViewModel which makes up a Section e.g.
#model Models.Enforcement
<div >
<form class="form-horizontal" method="post" action="#Url.Action(nameof(ApplicationController.Enforcements))"
#Html.EditorFor(x => x)
</div>
And the editor for will look something like this:
#model GSCM.Logic.Web.Models.Enforcement
#using System.Web.Mvc.Html
<div>
#Html.HiddenFor(x => x.Id)
<div class="form-group">
#Html.LabelFor(model => model.Name)
<div>
#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
My solution to this problem is to first of all be able to add custom attributes to a ViewModel class that tag the property as a configurable Question, e.g.
public class Enforcement
{
[ApplicationQuestion DisplayOrder=1, Partial="DifferentNamedPartial"]
public string Name{get;set;}
}
I then have my own EditorFor that loops through all these tagged Properties:
using System.Web.Mvc.Html;
public static class HtmlHelperExtensions
{
public static MvcHtmlString QuestionSetEditorFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, VMBaseClass model)
{
//PSEUDO CODE
var sectionHtml;
ForEach(var taggedProperty in model)
{
var basedOnProperty = GetFilePathFromProperty(taggedProperty);
sectionHtml += System.Web.Mvc.Html.GetPartial(basedOnProperty)
}
return MvcHtmlString.Create(sectionHtml);
}
}
So basically, I would need to chop up the existing EditorFor ViewModel templates and have a partial for each section, property.
My questions are:
Is this a good idea or is there a better way. Time is a constraint (as always)?
Can I create the html string in the way I have outlined above with the pseduo code that loops rounds the properties and calls out to the partials?
Here you can proceed like
Suppose your partial pages which you want to call in main pages using loops are:
_form-one-partial.cshtml
_form-two-partial.cshtml
_form-three-partial.cshtml
_form-four-partial.cshtml
_form-five-partial.cshtml
One of those partial page content as:
#model PartialFieldViewModel
//---other fileds on the page
<input type="text"/>
//---other fileds on the page
To call the partial on the basis of the loop around the "partial" varible in the main page where partial will be called as:
#model PartialViewModel
foreach (var partial in Model.Parital)
{
#Html.Partial("_form-{0}-partial".FormatWith(partial.PartialType.ToString().ToLower()), partial)
}
Partial view modal class as
public class PartialViewModel
{
//Other properties
public List<PartialFieldViewModel> Partial { get; set; }
}
public class PartialFieldViewModel
{
//Other properties
public Constants.PartialType PartialType{ get; set; }
}
I'm working on an ASP.NET MVC 5 application and the project owner is concerned about "under-posting" issues caused by validating non-nullable types (as mentioned in http://bradwilson.typepad.com/blog/2010/01/input-validation-vs-model-validation-in-aspnet-mvc.html and http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api).
I created a test case to replicate this issue in ASP.NET MVC 5 but without luck.
Model:
public class ContactModel
{
[Required]
public Int32 data1 { get; set; }
public Int32 data2 { get; set; }
}
View:
<div class="form-group">
#Html.LabelFor(model => model.data1)
<div>
#Html.EditorFor(model => model.data1)
</div>
</div>
<div>
#Html.LabelFor(model => model.data2)
<div>
#Html.EditorFor(model => model.data2)
</div>
</div>
Controller:
public ActionResult Index(Models.ContactModel contact)
{
if (ModelState.IsValid)
{
Response.Write("modelstate is valid<br>");
return View();
}
else
{
Response.Write("modelstate is invalid<br>");
return View();
}
}
It seems that when data1 and data2 are null in the post, their values in the model (contact) will be 0. However, ModelState.IsValid will also be false (instead of true as shown in the two articles).
What I have:
What the second article showed:
I couldn't find any information regarding changes on how model validation works in ASP.NET MVC, so I'm guessing I did something wrong with my test case. Any thought and suggestion are appreciated.
The reason your ModelState is false is because the post is providing form values from each property in your model. Essentially the Model binding system is checking the validity of both data1 and data2 fields as you have #Html.EditorFor helpers explicitly written for both properties in your view (so no underposting is actually going on).
I did successfully replicate the under-posting concerns from the articles. Simply remove one of the EditorFor helpers in your view, so you're actually underposting. With both helpers present, there's no underposting going on. So the view looks like this now (note I added the validation helper for both properties to get feedback in the view on what's going on):
View:
<div class="form-group">
#Html.LabelFor(model => model.data1)
<div>
#Html.EditorFor(model => model.data1)
#Html.ValidationMessageFor(model => model.data1)
#Html.ValidationMessageFor(model => model.data2)
</div>
</div>
Make sure to leave the #Html.EditorFor helper completely off for the data2 property. Now fill in zero in the form field (you'll only one form field in the view of course now), and post to your action.
ModelState will come back as true in this scenario, even though only one form field is being posted. Not a good result if someone does underpost! So here's the (slightly modified) original model class where underposting issues will occur in the case a form field is left off of your form (note the Required attributes don't make any difference in this situation as both properties are value types):
//You could add the Required attribute or not, doesn't matter at this point.
//The concern here is that the Modelstate will still come back as Valid
//in the case of a form field being left off of your form (or someone underposts).
//So to replicate underposting issues, make sure to comment or delete
//at least one Html.EditorFor helper in the view.
//[Required] Underposting will occur regardless if this is marked required or not,
//so be careful if someone does underpost your form.
public Int32 data1 { get; set; }
//[Required]
public Int32 data2 { get; set; }
Now the solution if you want to solve the underposting issue:
Simply mark both properties as required and make them nullable as mentioned in the articles you provided, like so:
[Required]
public Int32? data1 { get; set; }
[Required]
public Int32? data2 { get; set; }
Now when the view is posted with a missing #Html.EditorFor helper or a missing form field, the ModelState Validation will come back as false, and you're protected from underposting issues.
I understand that I can use #Html.HiddenFor(m => m.parameter) and when the form is submitted, that parameter will be passed to the controller. My model has many properties.
Is there a shorter way of passing the entire model at once to the controller or must I do it one by one each time?
The model will be passed to the controller in its entirety, but the values of properties that are not bound by input or hidden fields will be lost.
You have to either bind the properties in the form on the client-side, or re-fetch the entity on the server-side.
You seem to be asking for something like #Html.HiddenFor(m => m.Model), and that is not possible. Sorry
One thing to keep in mind, if you have tons of hidden fields, you may be sending more data to the view than you really need. Consider employing view models
For anyone else who looks at this you can do a #Html.EditorForModel() in a hidden div. You'd also have to use #Html.EditorFor(model => model.ObjectProperty) for each object property of the model.
<div hidden="hidden">
#Html.EditorForModel()
#Html.EditorFor(model => model.ObjectProperty)
#Html.EditorFor(model => model.ListOfObjectsProperty)
</div>
The entire model will be posted if you are using a FORM element. Your elements using the Model obviously need to be inside the form element
You can also POST the form yourself say by using JQuery
See this other stack issue for that : jQuery AJAX submit form
Have a close look at the anwser by "Alfrekjv"
This is already built in. Consider this model:
public class MyModel
{
public string PropertyA { get; set; }
public string parameter { get; set; }
}
and now consider this action:
[HttpPost]
public ActionResult PostSomeData(MyModel model)
{
}
MVC will leverage the FormCollection and fill in the MyModel class where it can. If you don't have the PropertyA in the form then it will be null. But since you have an input for the parameter property it will be filled in.
You can check only the properties you want:
if (this.ModelState.IsValidField("Name"))
{
// .....
}
instead of:
if (this.ModelState.IsValid)
{
// .....
}
#using (Ajax.BeginForm("my_function", "my_controller", new AjaxOptions { InsertionMode = InsertionMode.Replace }, mymodel))
#model MyMVC.Models.MyMVC.MyModel
#{
ViewBag.Title = "Index";
}
The reason why I ask this question is in MVC we can have more than 1 form, so I would like to have a model for each form. But when I was trying to add another model at the top of the view, it displays an error.
I know we can use ViewModel which has model1 and model2 inside, but it's not the question I am asking.
I just simply want to know is there any way that we can put in 2 models into 1 view.
Update:
For example, I want to have 2 forms in my view, so for each form I'd like to have a separated model.
Update 2
Thank you all for your replies. I now know that MVC only supports one single model on one view. Do you guys consider this a disadvantage of MVC? Why or Why not?(No one has answered it yet.... why?)
Your answers are similar, so I don't even know which one should be set as an answer.
You should use some prtialviews for other models. You have your main view with main model. Inside of that view, you could have some partialviews with their models and their validations.
--------------------------------------Edit:
As others said it is better to use View Models and combine the models with each others. But as you wanted I created a sample project for you on my Github account:
https://github.com/bahman616/ASPNET_MVC_multiple_models_in_a_view_with_partialview.git
Here is the simplified code of that project:
Here are two models:
public partial class Person
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
}
public partial class Company
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
I want to have both create views in one view, so this is the parent view:
#model ASP_NET_MVC_Multiple_Models_In_A_View.Models.Person
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm("Create","Person")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Person</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>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#{Html.RenderAction("_CompanyCreate", "Company");}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
And this is the partial view:
#model ASP_NET_MVC_Multiple_Models_In_A_View.Models.Company
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
#using (Html.BeginForm("_CompanyCreate","Company")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Company</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>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Simple answer:
No, you can't have two models.
Details
There is only one way to declare a Model to be used by a view:
#model [namespace].[path].[ViewModelName]
There is no way to specify multiple of these above.
In addition to that, you can only send one object to a view from the Controller, which would be the model:
public ActionResult WhateverAction()
{
var model = new MyViewModel();
return View(model);
}
Theoretically you cant do it because it binds one model to one view. But you can create somethink like a model helper which combines two other models to one
public class model1
{
string var1 {get; set;}
string var2 {get; set;}
}
public class model2
{
string var3 {get; set;}
string var4 {get; set;}
}
public class modelofBoth
{
model1 mod1 {get; set;}
model2 mod2 {get; set;}
}
If you cannot/don't want to modify the model passed to the view then you could pass the additional object in via the ViewBag object. It feels like a hack to me but it works:
Controller:
ViewBag["Model2"] = yourObject
View:
#{var Model2 = ViewBag["Model2"] as yourObjectType;}
Technically, you could write your own view engine (derived from the RazorViewEngine?) using a custom WebViewPage and have it do whatever you want. The view derives from WebViewPage<T> where T is the type of the model for the page. To support multiple models, the view base class would need to have multiple generic types. Using the baked in RazorViewEngine, however, you're limited to a single model type per view and much of the controller framework presumes a single model per view so you'd have to rewrite that, too. There doesn't seem to be much point in that, however, as you could simply have your models be properties of the (single) view model, then use partial views for each of the forms providing the property as the model for the partial.
For more information see http://haacked.com/archive/2011/02/21/changing-base-type-of-a-razor-view.aspx
Answer is NO. Thats why ViewModel pattern Exists. because the razor engine is totally relied on the model you passed, the model is used for ModelBinding for the basic CRUD Operations.
You can easily have 1 model per form. For each form, you just have to do something like this:
var model = new MyModel();
#Html.TextBoxFor(m => model.Title)
#Html.ValidationMessageFor(m => model.Title)
The validation works like a charm.
This way, you can keep your model to display information as usual and have 1 model per form to submit data (for example newsletter subscription)
if your models are similar,
Make a new model contains references to all models you want, and use it.
if models are not similar, you can fill new model with similar fields you need from all your models.
but if you wants use totally different models, you should use Partial views.
I have created view page in MVC like
<%using (Html.BeginForm())
{ %>
<%=LabelHelpers.Label("firstname", "FirstName:")%>
<br/>
<%=Html.TextBox("firstname")%>
<br/><br/>
<%=LabelHelpers.Label("lastname", "Lastname:")%>
<br/>
<%=Html.TextBox("lastname")%>
<br/><br/>
<input type="Button" value="Register"/>
<%} %>
Here I want to write Buttonclick Event ...How and Where should i write?
Your input is of type button - these don't do anything without additional client side code.
If you want to handle the 'event' on the server in a similar way that you would have in ASP.NET, you should convert it to a submit button. Assuming your controller is called 'Account' and your action is called 'Register' your current code would look something like this:
public ViewResult Register()
{
return View();
}
You want to start by passing a model to the view:
public ViewResult Register()
{
var registerModel = new RegisterModel();
return View(registerModel);
}
Your current view is using loosely typed inputs. Since you're passing it a model you can use strongly typed views. Your model should look something like this:
public class RegisterMode
{
public string Firstname { get; set; }
public string Surname { get; set; }
}
To use strongly typed views, change your view to look like this:
<%using (Html.BeginForm())
{ %>
<%=Html.LabelFor(x => x.Firstname)%>
<br/>
<%=Html.TextBoxFor(x => x.Firstname)%>
<br/><br/>
<%=Html.LabelFor(x => x.Surname)%>
<br/>
<%=Html.TextBoxFor(x => x.Surname)%>
<br/><br/>
<input type="submit" value="Register"/>
<%} %>
What we've done is told the view to build labels and text boxes for your RegisterModel type. This will allow the model values to be automatically mapped when you POST the form to the controller.
Do accept the post, we need to add a new Action to the controller, with the same name, but accepting a parameter of type RegisterModel:
public ActionResult Register(RegisterModel model)
{
// do something with the model, such as inserting it into the database.
// model.Firstname will contain the value of the firstname textbox
// model.Surname will contain the value of the surnaem textbox
return RedirectToAction("Success");
}
One last thing to do, to be safe, is to add the [HttpGet] and [HttpPost] attributes to your controller actions to control the methods they accept:
[HttpGet]
public ViewResult Register()
and
[HttpPost]
public ActionResult Register(RegisterModel model)
I suggest you read up on MVC at http://www.asp.net/mvc and read the NerdDinner tutorial chapter in Professional MVC (available for free online in PDF format).
joining to the question, want to make it more concrete
i have a form, the form already has a submit button, but i need to bind an additional action to another button.
yes, i do know that MVC does not support events 'cause HTML forms doesn't support them.
so the solution i've came to is to create to hidden inputs inside the form and bind an 'onclick' event (jquery 'live' method) to every... oh, what the hell? here is the code:
html:
<input type="hidden" id="SenderControlID" name="SenderControlID" value="-1" />
<input type="hidden" id="SenderControlValue" name="SenderControlValue" value="-1" />
js:
if ($('#SenderControlID')[0]) {
$('input[type="submit"], input[type="button"], input[type="checkbox"], input[type="radio"]').live('click', function () {
$('#SenderControlID').val($(this).attr('name'));
$('#SenderControlValue').val($(this).val());
});
}
but maybe there is more elegant solution?