NullReferenceException when trying to pass a nested ViewModel to a Partial - c#

My login and signup forms are in the same page, and they are separated by a tab element.
Each one of the forms have a ViewModel:
public class RegisterViewModel
{
public string FullName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
public class LoginViewModel
{
public string Email { get; set; }
public string Password { get; set; }
}
And since both forms are on the same page, I have a parent ViewModel:
public class LoginAndRegisterViewModel
{
public LoginViewModel LoginViewModel{ get; set; }
public RegisterViewModel RegisterViewModel{ get; set; }
}
// Actions...
public ActionResult Register()
{
return View("Login");
}
public async Task<ActionResult> Register(LoginAndRegisterViewModel model)
{
// blah.. do stuff here...
return View("Login", model);
}
But in the main view when I try to render the partials with the forms I get a System.NullReferenceException (Additional information: Object reference not set to an instance of an object.) in Model.LoginViewModel and Model.RegisterViewModel. This is the view:
Login.cshtml:
#model Models.LoginAndRegisterViewModel
...
#Html.Partial("_LoginPartial", Model.LoginViewModel)
...
#Html.Partial("_RegisterPartial", Model.RegisterViewModel)
I have seen that it is common practice to follow this kind of convention, as detailed here in this SO question. I even tried using this PartialOrNull helper but I still get the exception. I also tried the solution based on this "Forms for Deep View Model graphs" article.
Also this didn't work:
#Html.Partial("_LoginPartial", Model.LoginViewModel, new ViewDataDictionary(ViewData){Model = null});
How can I have a register/login form like this? As I said this is EXACTLY the same situation in that other SO question, and everyone reported that the solution worked, but that was for MVC3.
EDIT 1: added the _LoginPartial as reference.
#model Models.LoginViewModel
#{
using (Html.BeginForm("Login", "Account", new {ReturnUrl = ViewBag.ReturnUrl}, FormMethod.Post))
{
#Html.TextBoxFor(m => m.Email)
#Html.PasswordFor(m => m.Password)
}
}
EDIT 2: tried the ?? operator and still getting the Exception.
#Html.Partial("_LoginPartial", Model.LoginViewModel ?? new LoginViewModel())
UPDATE: With the accepted answer I made some tests and now it is working without an initialization ViewModel constructor and without sending an empty ViewModel in the Get action.
The only needed requirement is to declare the partial view to have the main model as the #model:
#model Models.LoginAndRegisterViewModel
Then to access the nested ViewModel in the partial:
#model Models.LoginAndRegisterViewModel
#{
using (Html.BeginForm("Login", "Account", new {ReturnUrl = ViewBag.ReturnUrl}, FormMethod.Post))
{
#Html.TextBoxFor(m => m.LoginViewModel.Email)
#Html.PasswordFor(m => m.LoginViewModel.Password)
}
}

You should initialize properties LoginViewModel and RegisterViewModel in the view models constructor
public class LoginAndRegisterViewModel
{
public LoginAndRegisterViewModel()
{
LoginViewModel = new LoginViewModel();
RegisterViewModel = new RegisterViewModel ();
}
public LoginViewModel LoginViewModel{ get; set; }
public RegisterViewModel RegisterViewModel{ get; set; }
}
and in the GET method, pass a new instance of the view model
public ActionResult Register()
{
var model = new LoginAndRegisterViewModel();
return View("Login", model);
}
and to ensure you model properties post back, pass the model to the partials
#Html.Partial("_LoginPartial", Model)
and change the partial to use LoginAndRegisterViewModel
#model Models.LoginAndRegisterViewModel
#using (Html.BeginForm("Login", "Account", new {ReturnUrl = ViewBag.ReturnUrl}, FormMethod.Post))
{
#Html.TextBoxFor(m => m.LoginViewModel.Email)
#Html.PasswordFor(m => m.LoginViewModel.Password)
}
This ensures the that the controls are bound to the view model and generate the correct name attributes
<input type="text: name="LoginViewModel.Email"..>
whereas passing on a property of the model would have generated
<input type="text: name="Email"..>
which would not bind to LoginAndRegisterViewModel on post back and all properties would have been null

Related

BeginForm dont sent model to Controller method POST why?

I have problem i dont now how to fix it.
I have ViewModel
public class CartOrderViewModel
{
public Cart Carts;
public Order Orders;
public string name;
}
in CartController method
public async Task<IActionResult> Index()
{
CartOrderViewModel vm = new CartOrderViewModel
{
Carts = _cart,
Orders = new Order() { User = applicationUser },
};
return View(vm);
}
View Index from CartController
#using (Html.BeginForm("Test", "Cart", FormMethod.Post))
{
#Html.TextBoxFor(m => m.name)
<input type="submit" value="Submit" />
}
and in my method Test in CartController i dont see ViewModel CartOrderViewModel
[HttpPost]
public IActionResult Test(CartOrderViewModel test)
{
string komentarz = test.name;
return View();
}
Model: test in null.
Please help me. Thx a lot
How to send form from Index View from CartController my ViewModel CartOrderViewModel
example Model.Order.name ??
Why my name from CartOrderViewModel dont sent to method Test from index View ?
printscreen
enter image description here
Why my name from CartOrderViewModel dont sent to method Test from index View ?
ASP.NET binds to properties, not fields. Change all of the public fields in class CartOrderViewModel to be mutable properties with { get; set; }.
Like so:
public class CartOrderViewModel
{
public Cart? Carts { get; set; }
public Order? Orders { get; set; }
public String? Name { get; set; }
}
Don't forget to add validation attributes, such as [Required], as per your project's needs.

How can I send more data than there is in form - using helper BeginForm

I send model to my view. My model is:
#model OnlineShop.Models.ViewModels.ManageProfileViewModel
public class ManageProfileViewModel
{
public ManageProfile ManageProfile { get; set; }
public ManagePassword ManagePassword { get; set; }
}
In my view I have form which change only my password.
#using (#Html.BeginForm("UpdatePassword", "Manage", FormMethod.Post)) { //some code }
Then I have action UpdatePassword in controller Manage which have as argument ManageProfileViewModel.
public async Task<ActionResult> UpdatePassword(ManageProfileViewModel model)
In my action, model contains only ManagePassword but ManageProfile is null.
Is it possible to pass whole view model including ManageProfile using form ?

CheckBoxFor resetting the model to null in Razor

I'm iterating a list to set the values for multiple Html.CheckBoxFor controls, but after submitting the form, I get a null value for the model itself in the controller parameter. Should I replace Html.CheckBoxFor with Html.HiddenFor for instance, the whole model binding works and FunctionViewModel is passed to the controller properly.
Model
public class FunctionViewModel
{
//... (this class is huge)
public MeasuringViewModel MeasuringViewModel { get; set; }
}
Classes used by the model
public class MeasuringViewModel
{
//... (this class is also huge)
public List<BatchItem> BatchToCancel { get; set; }
}
public class BatchItem
{
public bool IsCancel { get; set; }
public VMeasuringService VMeasuringService { get; set; }
public BatchItem(VMeasuringService vMeasuringService)
{
IsCancel = false;
VMeasuringService = vMeasuringService;
}
}
Controller
[HttpPost]
public ActionResult CancelBatch(FunctionViewModel viewModel)
{
// viewModel is null should I use CheckBoxFor
return View();
}
Form
#using (Html.BeginForm("CancelBatch", "Services", FormMethod.Post, new { Area = "Functions", #id = "cancelForm" }))
{
for(int i = 0; i < Model.MeasuringViewModel.BatchToCancel.Count; i++)
{
#Html.CheckBoxFor(m => m.MeasuringViewModel.BatchToCancel[i].IsCancel)
}
<input type="submit" id="btSubmit" title="Post" alt="Post" value="Post" />
}
So, what's wrong with it?
You get null because none of the control ids matching with your model properties, Check by Inspecting your html in browser that, is control ids generated by Razor are matching with your model properties?
secondly in this situation where you cannot give specific ids to your controls then you can get your control value in your controller action using FormCollection like given bellow:
[HttpPost]
public ActionResult CancelBatch(FormCollection fc)
{
// viewModel is null should I use CheckBoxFor
return View();
}

Html.BeginForm posted Model is null

Hello I have following problem:
I tried to post a Model through a Form to an other controller-action.
But the received Model is only filled with null elements.
ToolController.cs
public class ToolController : Controller
{
public ActionResult Index()
{
var Model = new EditToolModel{ /* Some data */ };
return View(Model);
}
[HttpPost]
public ActionResult EditToolOverview(EditToolModel myModel)
{
return RedirectToAction("Index", "Tool", new { show = "overview" });
}
}
EditToolModel.cs
public class EditToolModel
{
public Tools tool;
public IEnumerable<Tools> tools { get; set; }
public ToolsExtention tool_extention;
public string latest_version { get; set; }
public string latest_version_type { get; set; }
public string latest_devStep { get; set; }
public IEnumerable<ToolVersionsView> versions { get; set; }
public IEnumerable<DevelopmentStep> developmentSteps { get; set; }
}
Index.cshtml
#model EditToolModel
#{
ViewBag.Title = "Index";
Layout = "~/Layout/_Layout.cshtml";
}
#Html.Partial("ToolOverview", this.Model)
ToolOverview.cshtml
#model EditToolModel
#using (Html.BeginForm("EditToolOverview", "Tool", FormMethod.Post))
{
<div class="contend">
#Html.TextBoxFor(Model => Model.tool_extention.a)
#Html.TextBoxFor(Model => Model.tool_extention.b)
<input type="submit" name="tool_submit" value="Submit"/>
</div>
}
You need to have a getter/setter on the tool_extention property in order for the DefaultModelBinder to work
public ToolsExtention tool_extention { get; set; }
Ditto for the tool property (but your only rendering controls for the tool_extention property in your view)
ToolsExtention Try changing the EditToolOverview property from EditToolModel to ToolsExtention in the form post method.
[HttpPost]
public ActionResult EditToolOverview(ToolsExtention myModel)
{
//_devProv.changeToolExtention(myModel);
return RedirectToAction("Index", "Tool", new { show = "overview" });
}

PartialView model is empty returns null reference

I'm rendering a PartialView via an Action on my controller.
This is sending a model to the partial which then populates a sub-list for each parent that the partial goes into.
Some of the parent objects don't have children.
I need to capture an Id from the model in the partial to link the sub-list into an Accordion control.
How do I prevent a null reference exception when the child model is empty?
Is there any way to send the ID direct from the Action?
Current attempt...
#using BootstrapSupport
#model IEnumerable<WhatWorks.ViewModels.FamilyListViewModel>
#{ if (string.IsNullOrEmpty(Model.FirstOrDefault().familyId.ToString()))
{
do something...
}
else
{
int modelIndex = Model.FirstOrDefault().familyId;
Controller Action
public ActionResult Index(int Id)
{
var model = GetDisplay(Id).OrderBy(i => i.dob).AsEnumerable();
return PartialView("_family", model);
}
Main View
var family = model.GetIdValue();
<div class="accordion" id="#Html.Raw("accordion")#family.Values.FirstOrDefault()#Html.Raw("_b")">
#Html.Action("Index", "Family", new { Id = family["Id"] })
</div>
ViewModel
public partial class FamilyListViewModel
{
public int Id { get; set; }
public int familyId { get; set; }
public string name { get; set; }
etc...
}
Then do this :
#{ if (Model.Count() >0 )
{
do something...
}

Categories

Resources