NullReferenceException after POST - c#

I've got a form that has a dropDownlist using the Model to fill the list, the view is rendered. The issue is that when i press the submit button, a null pointer exception for Model is thrown. I want to receive the value selected in the Post Action.
Here is my code:
Model:
public class BillViewModel
{
public List<SelectListItem> ClientList { get; set; }
public int SelectedClient { get; set; }
}
Controller Action:
public ActionResult Index()
{
var billRepo = new BillRepo();
var bill = new BillViewModel {ListProducts = billRepo.GetAllProducts()};
bill.ClientList = new List<SelectListItem>();
List<Client> allClientList = billRepo.GetAllClients();
foreach (Client client in allClientList)
{
var item = new SelectListItem() { Value = client.ClientId.ToString(), Text = client.Name };
bill.ClientList.Add(item);
}
ViewBag.ClientSelect = new SelectList(billRepo.GetAllClients(), "value", "text", bill.SelectedClient);
bill.SelectedClient = 1;
return View(bill);
}
[HttpPost]
public ActionResult Index(BillViewModel billViewModel)
{
return View();
}
View: this is where I get the null pointer exception in Model.ClientList
#using (Html.BeginForm())
{
#Html.DropDownListFor(item => item.SelectedClient, Model.ClientList, "Select Client")
<input type="submit" value="Aceptar"/>
}

In the [HttpPost] action method, you are invoking the View() method without any viewmodel. Therefore the Model property inside the view is null. The solution is simply to invoke View and passing in the BillViewModel.
Ex:
[HttpPost]
public ActionResult Index(BillViewModel billViewModel)
{
return View(billViewModel);
}

As the error is trying to tell you, Model.ClientList is null.
You need to initialize the model, just like you did in the GET action. (for example, by calling the same function)

Related

Pass in data parameters to new MVC ActionResult call without showing in URL

I am working with adding a new MVC page and have the method and calls up and running. My issue is that I am not wanting to pass in URL parameters to show in my page but need to pass in the parameters for the method when I do a redirect to my new page. Currently I have it set up like this:
Page.cs
void ToNewPage()
{
Response.RedirectToRoute(new { controller = "ControllerName", action = "ActionName", ID1 = 1, ID2 = 2 });
}
ControllerName.cs
public ActionResult ActionName(int ID1, int ID2)
{
...
return View(model);
}
Currently with my code I get the URL ~/ControllerName/ActionName?ID1=1&ID2=2. I am just wanting the URL to just be ~/ControllerName/ActionName. I know this would be easier on a frontend or maybe through javascript but needing to do this from the ToNewPage method if possible.
There are working codes:
PageController.cs
public class PageController : Controller
{
// GET: Page
public ActionResult Index()
{
return View();
}
public ActionResult ToNewPage()
{
var ids = Newtonsoft.Json.JsonConvert.SerializeObject(new { ID1 = 1, ID2= 2 });
TempData["ids"] = ids;
return RedirectToAction("Index", "NewPage");
}
}
NewPageController.cs
public class NewPageController : Controller
{
// GET: NewPage
public ActionResult Index()
{
if (TempData["ids"] != null)
{
dynamic ids = JsonConvert.DeserializeObject(TempData["ids"] as string);
ViewBag.ID1 = ids.ID1;
ViewBag.ID2 = ids.ID2;
}
return View();
}
}
NewPage\Index.cshtml
#{
ViewBag.Title = "Index";
}
<h2>NewPage</h2>
<ul>
<li>ID1: #ViewBag.ID1</li>
<li>ID2: #ViewBag.ID2</li>
</ul>
You should use TempData:
void ToNewPage()
{
TempData["ID1"]="ID1 Value"
TempData["ID2"]="ID2 Value"
Response.RedirectToRoute(new { controller = "ControllerName", action = "ActionName"
});
}
public ActionResult ActionName()
{
int ID1=int.parse(TempData["ID1"].ToString());
int ID2=int.parse(TempData["ID2"].ToString());
return View();
}
You can fill many TempDatas in many controllers and use them in many Views and controllers

How to call a asp.net mvc action without changing the url

I have a simple controller with 2 custom actions. From the index view there is a link to the action "MyActionA" with id = 3. Then the matching view is returned.
On the view "MyActionA" there is a link (on a submit form button) to the action "HiddenAction".
The action "HiddenAction" only updates a property on given model. I do not want to change the URL on client browser. But I want to send the updated model to the client.
I tried it with Redirect but it never works.
Has anyone an idea?
Here is the controller code:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult MyActionA(int id)
{
if (id <= 0) throw new ArgumentException();
HomeModel model = GetModel();
return View(model);
}
[HttpPost]
public IActionResult HiddenAction(HomeModel model)
{
if (model == null) throw new ArgumentNullException(nameof(model));
model.MyPropB = 999;
//HttpContext.Response.Redirect()
return View(model);
//return Rewrite("MyAtionA/3");
//return RedirectToAction(nameof(MyActionA), 3);
//return View();
}
// Helper
private HomeModel GetModel()
{
return new HomeModel();
}
public class HomeModel
{
public int MyPropA { get; set; }
public int MyPropB { get; set; }
}
}
You would have to use AJAX to post the data back to the server (i.e. make the request client-side via JavaScript). Any other method (such as an HTML form) or a link will cause the URL in the browser to change.

I have got trouble to send id from view to controler

#model IEnumerable<Evidencija.Models.Vozilo>
#{
ViewBag.Title = "PokreniIzvjestaj";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>PokreniIzvjestaj</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Vozilo</legend>
<p>
#Html.DropDownList("Vozila", Model.Select(p => new SelectListItem { Text = p.VoziloID.ToString(), Value = p.VoziloID.ToString() }), "Izaberi vozilo")
</p>
<input type="submit" value="Dodaj stavku" />
</fieldset>
}
I want to send id of table vozilo to controler with dropdownlist.
Controler accepts vozilo as a parameter but it is ollways zero.
How can I solve this without using viewmodel.
[HttpPost]
public ActionResult PokreniIzvjestaj(Vozilo v)
{
ReportClass rpt = new ReportClass();
rpt.FileName = Server.MapPath("~/Reports/Vozilo.rpt");
rpt.Load();
//ReportMethods.SetDBLogonForReport(rpt);
//ReportMethods.SetDBLogonForSubreports(rpt);
// rpt.VerifyDatabase();
rpt.SetParameterValue("#VoziloId",v.VoziloID);
Stream stream = null;
stream = rpt.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat);
return File(stream, "application/pdf", "Vozilo.pdf");
//PortableDocFormat--pdf format
//application/pdf -- vezan za pdf format, ako je drugi tip mjenja se u zavisnosti od izabranog
//naziv.pdf -- naziv dokumenta i izabrana ekstenzija
}
[HttpGet]
public ActionResult PokreniIzvjestaj()
{
var vozila = db.Voziloes.ToList();
return View(vozila);
}
There are two method from controler.
You currently binding your drop down to a property named Vozilo. A <select> post back single value (in your case the VoziloID or the selected option. Your POST method then tries to bind a complex object Vozilo to an int (assuming VoziloID is typeofint) which of course fails and the model isnull`. You could solve this changing the method to
[HttpPost]
public ActionResult PokreniIzvjestaj(int Vozilo)
The parameter Vozilo will now contain the value of the selected VoziloID.
However it not clear why you want to "solve this without using viewmodel" when using a view model is the correct approach
View model
public class VoziloVM
{
[Display(Name = "Vozilo")]
[Required(ErrorMessage = "Please select a Vozilo")]
public int? SelectedVozilo { get; set; }
public SelectList VoziloList { get; set; }
}
Controller
public ActionResult PokreniIzvjestaj()
{
var viziloList = db.Voziloes.Select(v => v.VoziloID);
VoziloVM model = new VoziloVM();
model.VoziloList = new SelectList(viziloList)
model.SelectedVozilo = // set a value here if you want a specific option selected
return View(model);
}
[HttpPost]
public ActionResult PokreniIzvjestaj(VoziloVM model)
{
// model.SelectedVozilo contains the value of the selected option
....
}
View
#model YourAssembly.VoziloVM>
....
#Html.LabelFor(m => m.SelectedVozilo)
#Html.DropDownListFor(m => m.SelectedVozilo, Model.VoziloList, "-Please select-")
#Html.ValidationMessageFor(m => m.SelectedVozilo)
....

not able to see the view after postback

Hi I have got a drop downlist that I am binding that one in controller I have got one button in view with that I am doing some validations that's working fine,
when I submit the button for validation check i am not able to get the view with error message. Instead of this I am getting error like this " The view 'PostValues' or its master was not found or no view engine supports the searched locations".
would any one help on why I am not able to get the view
here the view is strongly Typed view
and this is my code in controller.
public class CrossFieldsTxtboxesController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
var model = NewMethod();
return View(model);
}
private static CrossFieldValidation NewMethod()
{
var model = new CrossFieldValidation
{
SelectedValue = "Amount",
Items = new[]
{
new SelectListItem { Value = "Amount", Text = "Amount" },
new SelectListItem { Value = "Pound", Text = "Pound" },
new SelectListItem { Value = "Percent", Text = "Percent" },
}
};
return model;
}
[HttpPost]
public ActionResult PostValues(CrossFieldValidation model1)
{
model1 = NewMethod();
if (!ModelState.IsValid)
{
return View(model1);
}
else
{
return RedirectToAction("Index");
}
}
}
and this is my view
#model MvcSampleApplication.Models.CrossFieldValidation
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("PostValues", "CrossFieldsTxtboxes"))
{
#Html.ValidationSummary(true)
<div class ="editor-field">
#Html.TextBoxFor(m => m.TxtCrossField)
#Html.ValidationMessageFor(m=>m.TxtCrossField)
</div>
#Html.DropDownListFor(m=> m.SelectedValue , Model.Items)
<input id="PostValues" type="Submit" value="PostValues" />
}
would any one pls help on this...
This line
return View(model1);
looks for the view named exactly like the action in which it was called. Calling this line from PostValues action assumes there is a view PostValues.cshtml (which apparently does not exist). If you still want to use view Index - you should specify this explicitly:
if (!ModelState.IsValid)
{
return View("Index", model1);
}
As Andrei said. Alternatively, you can give your PostValues method an additional tag:
[HttpPost, ActionName("Index")]
public ActionResult PostValues(CrossFieldValidation model1)
{
if (!ModelState.IsValid)
{
return View(model1);
}
}

How to use a separate model class for validation in MVC

I am not sure on how to implement this, I have a MovieController.cs in the Controllers folder and a MovieCreateViewModel.cs in the Models folder. I need to add validation for the create, edit and delete views.
MovieDetailsViewModel.cs
public class MovieDetailsViewModel
{
public int Id { get; set; }
}
then I have MovieController.cs
public class MovieController : Controller
{
Connect connection;
MovieCreateViewModel movie;
MovieDetailsViewModel id;
public MovieController()
{
this.connection = new Connect();
this.movie = new MovieCreateViewModel();
this.id = new MovieDetailsViewMode();
}
public ActionResult Edit(MovieDetailsViewModel id)
{
movie = this.connection.MovieContext.Where(m => m.ID == id).SingleOrDefault(); **//I get an error here**
return View(movie);
}
//
// POST: /Movie/Edit/5
[HttpPost]
public ActionResult Edit(MovieCreateViewModel movieedit)
{
try
{
if (ModelState.IsValid)
{
this.connection.MovieContext.AddObject(movieedit);
this.connection.MovieContext.Context.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
return View(movieedit);
}
}
for the httpPost I made the type MovieDetailsViewModel id in the parameter list
Where do I go from here please?
Thanks
Validation will now be performed on your model instance, to check it, you do:
ModelState.IsValid
In the controller method prior to the save operation. As long as the input names on the view correspond with your model class's property names, binding and validation will be performed implicitly prior to your action method being executed. To show your validation messages in your view, add a Html.ValidationMessage() to the top of the view. Hope this helps. By the way its well worth checking out Foolproof Validation which provides conditional validation attributes and some other good stuff.
Better practice would be use seperate model and viewmodel. Convert your model to viewmodel and then pass it to view
public ActionResult Edit(int id)
{
var movie = this.connection.MovieContext.SingleOrDefault(m => m.ID == id);
var vm = new MovieCreateViewModel{ Id = movie.Id};
return View(vm);
}
//
// POST: /Movie/Edit/5
[HttpPost]
public ActionResult Edit(MovieCreateViewModel vm)
{
try
{
if (ModelState.IsValid)
{
var movie = new Movie{Id = vm.Id};
this.connection.MovieContext.Attach(movie);
this.connection.MovieContext.Context.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
return View(movieedit);
}
}

Categories