MVC4 Nested ViewModel Binding does not work - asp.net-mvc

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;
}

Related

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);
}

Add ID from URL in MVC

Im just starting out with MVC and i'm in the learning phase. I'm currently stuck with this problem. In my soulution i got these two tables.
TopicTable
-TopId
-TopName
ContentTable
-ContId
-TopId
-Content
I want while i'm in the Topic Detailes-view to be able to create new content.
In the detailes.cshtml file i've added:
#Html.ActionLink("Create New Content", "Create", "Content", new { ID = Model.TopicId}, null)` which will display in URL: localhost/Content/Create/1
In my ContentController ive got this, and i get this error when submitting "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details."
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(int Id)
{
ContentModel content = new ContentModel();
if (ModelState.IsValid)
{
content.TopId = Id;
db.ContentModel.Add(content);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(content)
}
The View:
#model WebApplication2.Models.ContentModel
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>JobOfferModel</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Content, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Content)
#Html.ValidationMessageFor(model => model.Content)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Model:
public class Topic
{
[Display(Name = "Id")]
public virtual int TopicId{ get; set; }
[Display(Name = "Name")]
public virtual string TopicName{ get; set; }
}
public class Content
{
[Display(Name = "Id")]
[Display(Name = "Id")]
public virtual int ContentId{ get; set; }
[Display(Name = "TopicId")]
public virtual string TopicId{ get; set; }
[AllowHtml]
[Display(Name = "Innihald")]
public virtual string Content { get; set; }
}
So when i'm creating new content the topId value will automaticly be added to the content table.
Would love some help,, Gracias
#Html.ActionLink() is a call to a GET method. You need a GET to return a view that renders the form, and a POST method to receive and save the form data
public ActionResult Create(int ID)
{
Content model = new Content();
model.TopicId = ID; // note TopicId should be int, not string
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Content model)
{
if(!ModelState.IsValid)
{
return View(model);
}
// save your model and redirect.
}
View (divs etc omitted)
#model WebApplication2.Models.Content
....
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(m => m.TopicId) // for postback
#Html.LabelFor(model => model.Content, new { #class = "control-label col-md-2" })
#Html.TextBoxFor(model => model.Content)
#Html.ValidationMessageFor(model => model.Content)
<input type="submit" value="Create" class="btn btn-default" />
}
....

Model is null when form submitted

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.

Error when send request from partial view

I'm trying to learn the basics of MVC (NerdDinner tutorial). I have defined a model:
public class DinnerFormViewModel
{
// Properties
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
// Constructor
public DinnerFormViewModel(Dinner dinner)
{
Dinner = dinner;
Countries = new SelectList(PhoneValidator.Countries, dinner.Country);
}
}
and I defined a partial view:
#model MyNerddiner.Models.DinnerFormViewModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Dinner</legend>
#Html.HiddenFor(model => model.Dinner.DinnerID)
<div class="editor-label">
#Html.LabelFor(model => model.Dinner.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Dinner.Title)
#Html.ValidationMessageFor(model => model.Dinner.Title)
</div>
</fieldset>
}
which is loaded from usual view:
#model MyNerddiner.Models.DinnerFormViewModel
#{
ViewBag.Title = "Create";
}
<div id="Create" >
<h2>Host a Dinner</h2>
#Html.Partial("_DinnerForm")
</div>
The controller:
public ActionResult Create()
{
Dinner dinner = new Dinner()
{
EventDate = DateTime.Now.AddDays(7)
};
return View(new DinnerFormViewModel(dinner));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(DinnerFormViewModel dinnerViewModel)
{
Dinner dinner = null;
if (ModelState.IsValid)
{
try
{
dinner = dinnerViewModel.Dinner;
UpdateModel(dinner);
dinnerRepository.Add(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
catch
{
ModelState.AddRuleViolations(dinner.GetRuleViolations());
return View(dinner);
}
}
return View(new DinnerFormViewModel(dinner));
}
Now when I'm trying to create (on postback), I'm getting an error:
No parameterless constructor defined for this object.
I can guess that it is because somewhere the program is trying to initiate the DinnerFormViewModel, but where, and why and how should I make it right?
The MVC framework needs your view model to have a constructor that takes no parameters so that it can create an empty instance to populate with data from the request. DinnerFormViewModel does not implement a constructor with no parameters, add one, and this will fix your issue.
Well, found the problem and it have nothing to do with model and constructor.
the problem was that view contained following row:
#Html.DropDownListFor(model => model.Countries, Model.Countries)
#Html.ValidationMessageFor(model => model.Countries)
When i checked from where the exception came- it come because the country value was null.
After i changed
model => model.Countries
to
model => model.Dinner.Country
the exception stoped to be thrown
I'm so glad i solve this on my own!

Resources