Model is null when form submitted - asp.net-mvc

When I hit submit, the file parameter is null.
public ActionResult Create()
{
return View(new FileViewModel());
}
[HttpPost]
[InitializeBlobHelper]
public ActionResult Create(FileViewModel file)
{
if (ModelState.IsValid)
{
//upload file
}
else
return View(file);
}
public class FileViewModel
{
internal const string UploadingUserNameKey = "UserName";
internal const string FileNameKey = "FileName";
internal const string Folder = "files";
private readonly Guid guid = Guid.NewGuid();
public string FileName
{
get
{
if (File == null)
return null;
var folder = Folder;
return string.Format("{0}/{1}{2}", folder, guid, Path.GetExtension(File.FileName)).ToLowerInvariant();
}
}
[RequiredValue]
public HttpPostedFileBase File { get; set; }
}
Here is the cshtml:
#model MyProject.Controllers.Admin.FileViewModel
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_BackOfficeLayout.cshtml";
}
#using (Html.BeginForm("Create", "Files", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<fieldset>
<legend>Create</legend>
<div class="editor-label">
#Html.LabelFor(model => model.File)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.File, new { type = "file" })
#Html.ValidationMessageFor(model => model.File)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>

It's naming conflict and binder trying to bind your File property to FileViewModel object with file name, that's why you get null. POST names are case-insensitive.
Change:
public ActionResult Create(FileViewModel file)
To:
public ActionResult Create(FileViewModel model)
or to any other name

This solved my issue as well. It was a name that I was using that was similar to the model, which was similar to the variable I assigned the posted model too. once I sorted out the field name all worked as expected.
Of course the error was not helpful in pointing this out.

Related

Problem uploading member image in Umbraco

I am working with member API in Umbraco 8 and i am trying to upload the image when registering. After i register member and open member profile in backoffice i see this:
This is my Coontroller
public class RegisterController : SurfaceController
{
// GET: Register
public ActionResult Register(Models.RegisterModel model)
{
if (!ModelState.IsValid)
return CurrentUmbracoPage();
var memberService = Services.MemberService;
if (memberService.GetByEmail(model.Email) != null)
{
ModelState.AddModelError("", "A member with that email alredy exists");
return CurrentUmbracoPage();
}
var member = memberService.CreateMemberWithIdentity(model.Email, model.Email, model.Name, "bMEMembers");
member.SetValue("companyName", model.CompanyName );
member.SetValue("avatar", model.Avatar);
memberService.SavePassword(member, model.Password);
Members.Login(model.Email, model.Password);
memberService.Save(member);
return Redirect("/");
}
}
Model:
public HttpPostedFileBase Avatar { get; set; }
View:
#Html.LabelFor(model => model.Avatar)
<input type="file" name="Avatar" />
#Html.ValidationMessageFor(model => model.Avatar)
<input class="btn" type="submit" value="Create" />
Anybody could help me with this?
It's normal to appear because Avatar is an object (exactly: HttpPostedFileBase), and LabelFor will display object namespace.
Take a look: https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.html.labelextensions.labelfor?view=aspnet-mvc-5.2
and see that there is an overload for that method:
public static System.Web.Mvc.MvcHtmlString LabelFor<TModel,TValue> (this System.Web.Mvc.HtmlHelper<TModel> html, System.Linq.Expressions.Expression<Func<TModel,TValue>> expression, string labelText, System.Collections.Generic.IDictionary<string,object> htmlAttributes);
you can use like:
#Html.LabelFor(model => model.Avatar, "Avatar", null);
or more beauty way:
[DisplayName("Avatar")]
public HttpPostedFileBase Avatar { get; set; }
and leave your CSHTML code:
#Html.LabelFor(model => model.Avatar)

Model binding doesn't work for complex object

Here's the view I'm going to post:
#model WelcomeViewModel
#using (Html.BeginForm("SignUp", "Member", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post))
{
....
<div class="form-group">
#Html.EditorFor(model => model.SignUp.CompanyName, new {htmlAttributes = new {#class = "form-control" }})
</div>
<div class="form-group">
#Html.EditorFor(model => model.SignUp.RegisteredNo, new {htmlAttributes = new {#class = "form-control" } })
</div>
....
<button type="submit" name="signup" class="btn">Register</button>
}
ViewModel:
public class WelcomeViewModel
{
public SignInViewModel LogOn { get; set; }
public SignUpViewModel SignUp { get; set; }
}
Action method:
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public virtual async Task<ActionResult> SignUp(SignUpViewModel model)
{
if (!ModelState.IsValid)
return View("SignIn", new WelcomeViewModel { SignUp = model });
// other code
return View();
}
When I post the data, the model gets null. I know the inputs will be generated like:
<input id="SignUp_CompanyName" name="SignUp.CompanyName">
But the model binder accepts this:
<input id="SignUp_CompanyName" name="CompanyName">
Now I want to know how can I remove that prefix? I know I can explicitly add name for each input:
#Html.TextBoxFor(model => model.SignUp.CompanyName, new { Name = "CompanyName" })
but I want to do it in a strongly type way.
Perhaps the easiest way would be to apply the [Bind] attribute with its Prefix set to "SignUp":
public async Task<ActionResult> SignUp([Bind(Prefix="SignUp")] SignUpViewModel model)
See MSDN

Add to list mvc

I'm trying to add string data to list, but have null reference exception and don't have any idea how to fix this.
Here home controller:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ChatUser model)
{
if (ModelState.IsValid)
{
**model.chatGroup.Add(model.inputGroups);** - here excepton
}
return View(model);
}
And Index.cshtml:
#model test.Models.ChatUser
#{
ViewBag.Title = "Test";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<div class="form-group">
#Html.LabelFor(m => m.inputGroups, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.inputGroups, new { #class = "form-control" })
<input type="submit" value="GO" class="btn btn-default" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Your form does not include any controls for properties of chatGroup so its null when you post back. You need to either initialize the property in a parameterless constructor
public class ChatUser
{
public ChatUser()
{
chatGroup = new List<string>(); // initialize collection
}
....
public List<string> chatGroup { get; set; }
}
or initialize it in the POST method
[HttpPost]
public ActionResult Index(ChatUser model)
{
if (ModelState.IsValid)
{
model.chatGroup = new List<string>(); // initialize collection
model.chatGroup.Add(model.inputGroups);
}
return View(model);
}

MVC4 Nested ViewModel Binding does not work

i have an outerViewModel and inside of it two ViewModels,
when i try to bind innermodel i get null for all the properties...
here is the code:
**Models.cs**
public class OuterModel
{
public FirstInnerModel firstInnerModel;
public SecondInnerModel secondInnerModel;
}
public class FirstInnerModel
{
public string Title;
}
public class SecondInnerModel
{
public string Title;
}
Index.cshtml
#using (Html.BeginForm("ActivateFirst", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.firstInnerModel.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.firstInnerModel.Title)
#Html.ValidationMessageFor(model => model.firstInnerModel.Title)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
HomeController.cs
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
var model = new OuterModel()
{
firstInnerModel = new FirstInnerModel(),
secondInnerModel = new SecondInnerModel()
};
return View(model);
}
[HttpPost]
public void ActivateFirst(FirstInnerModel ggg)
{
}
ggg.Title returns null...
Anyone? help!
When you submit the form it will be posting the OuterModel to the controller so you would need to do something like:
[HttpPost]
public void ActivateFirst(OuterModel ggg)
{
var whatever = ggg.FirstInnerModel.Title;
}

UpdateModel not updating my model

I must have something incorrectly setup as I can't get the UpdateModel function to correctly update my model based on information passed in via a FormCollection.
My View looks like:
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
<p>ID: #Html.TextBox("ID", Model.ID)</p>
<p>Forename: #Html.TextBox("Forename", Model.Forename)</p>
<p>Surname: #Html.TextBox("Surname", Model.Surname)</p>
<input type="submit" value="Submit" />
}
</fieldset>
My model is:
namespace NSLM.Models
{
public class Person
{
public int ID;
public string Forename;
public string Surname;
}
}
and my controller is:
[HttpPost]
public ActionResult Details(FormCollection collection)
{
try
{
// TODO: Add update logic here
Models.Person m = new Models.Person();
// This doesn't work i.e. the model is not updated with the form values
TryUpdateModel(m);
// This does work
int.TryParse(Request.Form["ID"], out m.ID);
m.Forename = Request.Form["Forename"];
m.Surname = Request.Form["Surname"];
return View(m);
}
catch
{
return View();
}
}
as you can see if I manually assign each property it works fine, so what have I not set that would get the model to be updated with the form values?
Thanks,
Mark
Replace fields with properties in your model, i.e.:
namespace NSLM.Models
{
public class Person
{
public int ID {get; set;}
public string Forename {get; set;}
public string Surname {get; set;}
}
}
By the time the call gets to the action method any automatic model binding has already been performed. Try changing the input parameter of your action method to accept a Person instance. In that case the model binder will try to create the instance and populate it from the values passed by your form.
try this :
view :
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
#Html.HiddenFor(model => model.ID)
<p>Forename: #Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</p>
<p>Surname: #Html.EditorFor(model => model.Surname)
#Html.ValidationMessageFor(model => model.Surname)
</p>
<input type="submit" value="Submit" />
}
</fieldset>
Controller :
[HttpPost]
public ActionResult Details(Person p)
{
if (ModelState.IsValid)
{
db.Entry(p).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(p);
}

Resources