Object Model Properties Not Assigned After Submitting The Form - c#

I have view with UserSample model with two properties (Username, Password), All I want to achieve is to take these values from the textboxes and to store them into the object model and then in the Action method to take this object and manipulate it.
But here in this code the object properties in the action method Index are always null after submitting the form with filled text box fields.
Controller:
public ActionResult Index(UserSample user) //this is the object which properties I want to be asigned.
{
if (user != null)
{
}
return View();
}
View:
#using (#Html.BeginForm("Index","Login"))
{
<table>
<tr>
<td>Username: </td>
<td>#Html.TextBox("Username")</td>
</tr>
<tr>
<td>Password: </td>
<td>#Html.TextBox("Password")</td>
</tr>
<tr>
<td></td>
<td><input id="btnSubmit" type="submit" value="Submit" /></td>
</tr>
</table>
}

Try adding this line to your view:
#model UserSample
You probable need to include the namespace there. MyApp.Models.UserSample for example.
Then you can use stronly typed html-helpers:
<td>#Html.TextBoxFor(model => model.UserName)</td>
and
<td>#Html.TextBoxFor(model => model.Password)</td>
You should decorate the Index(UserSample user) method with the HttpPost attribute:
public ActionResult Index()
{
// Get method.
UserSample model = new UserSample();
return View(model);
}
[HttpPost]
public ActionResult Index(UserSample user)
{
// POST method.
}
When you post the form now, the UserSample object should be populated with the values from the form.
If you don't understand model binding, I suggest you look into it. This tutorial might get you started.

Related

How to get viewmodel, which was sent to view from controller, back to controller

I send from controller to view a list of objects, viewmodel is the object with some properties and pagedList, that need to be presented on page. And by pressing the button, this list need to be exported as file, that is, it need to go back to the controller and be processed there.
Model:
public class ProductsList : ListViewModel<Product>
{
public ProductsList(string prefix) : base(prefix){ }
public ProductsList(PagedList<Product> products)
{
List = products;
}
public int? ProductTypeFilter {get;set; }
public string ProductTypeFilterName {get; set;}
public string FilterBy { get; set; }
}
ListViewModel just contain PagedList.
My controller
[HttpPost]
public FileResult SaveAsFile(PagedList<Product> viewmodel)
{
...
}
And my view
#model MyProject.ViewModels.ProductsList
if (Model.List.Count > 0)
{
<table id="products_table">
<colgroup>
<col class="productType"/>
</colgroup>
<thead>
<tr>
<th >
Product type
</th>
</tr>
</thead>
<tbody>
#{ var i = 0; }
#foreach (var item in Model.List)
{
<tr>
<td onclick="window.location='#Url.Action("Details", new {id = item.Id})'">
<p>
#item.Type
</p>
</td>
}
</tr>
i++;
}
</tbody>
</table>
}
<form asp-action="SaveAsFile" enctype="multipart/form-data" method="post">
#Html.HiddenFor(m => list);
<input type="submit" value="Save as File"/>
</form>
I already have tried add to controller params tags [FromForm], [FromBody] (actually all available tags).
In view tried with hidden field in form, without it just with submit; put form on partial view; other forms: ajax, Html.ActionLink("Save as File", "SaveAsFile", new {Model}).
On debug mod Model.List has 21 items (but it can has more, like 2000 items), but when I press the button, viewmodel is creating newly.
Problem: viewmodel is creating newly and i cannot get back my full viewmodel to controller
I will be grateful for any help :)
You can set your ViewModel data in a Session variable when you send the data to your View from Controller method:
In order to setup your Session, you can follow this S.O answer
Once your Session is setup, then you can put your ViewModel in it like:
HttpContext.Session.SetObjectAsJson("ProductsList", productslist);
And then retrieve it in your POST method like this:
[HttpPost]
public FileResult SaveAsFile(PagedList<Product> viewmodel)
{
//Get your viewmodel here
var list = HttpContext.Session.GetObjectFromJson<ProductsList>("ProductsList");
}
You can also serialize your ViewModel and then send it your Controller method without using form:
Create an ActionLink:
#Html.ActionLink("Submit", "SaveAsFile", "Home", new { jsonModel= Json.Encode(Model.list) }, null)
And your Controller method:
public FileResult SaveAsFile(string jsonModel)
{
var serializer= new DataContractJsonSerializer(typeof(Model.Product));
var yourmodel= (Product)serializer.ReadObject(GenerateStreamFromString(jsonModel));
}

Can't pass two parameters to action ASP.NET MVC

I want to pass id and quantity to action, but I get this error: parameter dictionary contains a null entry for parameter id.
I've tried to do map routing, but I can't find how to do it properly.
My Action:
[HttpPost]
public ActionResult Index(int id, int quantity)
{
var user = unitOfWork.Users.FindByEmail(HttpContext.User.Identity.Name);
unitOfWork.Carts.AddProductToCartByEmail(user.Email, id, quantity);
unitOfWork.Complete();
return View();
}
From this page I'm trying to pass the parameters:
#using PagedList;
#using PagedList.Mvc;
#model IPagedList<MVC_Task.Models.AllProductsModel>
#{
ViewBag.Title = "Index";
}
<h2>Products</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.First().Name)
</th>
<th>
#Html.DisplayNameFor(model => model.First().Price)
</th>
#if (User.Identity.IsAuthenticated)
{
<th>
Quantity
</th>
}
</tr>
#foreach (var item in Model)
{
using (Html.BeginForm("Index", "Product", FormMethod.Post))
{
#Html.AntiForgeryToken()
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
#if (User.Identity.IsAuthenticated)
{
<td class="form-inline">
#Html.TextBoxFor(modelItem => item.Quantity, new { #type = "number", #class = "form-control" })
<input type="submit" value="Add" class="btn btn-default"
onclick="window.location.href = '#Url.Action("Index", new { item.Id, item.Quantity})';" /> //on this line I send the parameters
</td>
}
</tr>
}
}
</table>
<center>#Html.PagedListPager(Model, page => Url.Action("Index", new { page }))</center>
In post actions there is only one parameter can be taken from body, all other parameters you may pass them as route parametes or query string parameters, so your signature
public ActionResult Index(int id, int quantity)
may become:
public ActionResult Index(int id, [FromBody] int quantity)
you can call the action using the url /index?id=...
and try to call the action using ajax post or submit the form not a hyperlink
for more information see https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#using-frombody
You're not really posting correctly, using the client side window.location.href - it will result in a HttpGet.
You should add a hidden input for the item id, here's the relevant snippet:
<td class="form-inline">
<!-- add the hidden input -->
#Html.HiddenFor(modelItem => item.Id)
<!-- keep this as is -->
#Html.TextBoxFor(modelItem => item.Quantity, new { #type = "number", #class = "form-control" })
<!-- remove the onclick redirect,
this will post to the action defined in BeginForm -->
<input type="submit" value="Add" class="btn btn-default"/>
</td>
Next, a model is often passed as a complex type. It has many benefits over simple types, because you can extend it with, e.g.; validation logic.
So, lets define a model:
public class PostModel
{
public int Id {get;set;}
public int Quantity {get;set;}
}
Now adjust your Index to accept the model:
[HttpPost]
public ActionResult Index(PostModel model)
{
//your logic
}
By default the binders will bind to the appropriate properties in the model. If your TextBox's name is Id, or Quantity, it will be able to do so. You can validate that in the rendered html.

Asp.Net MVC - Viewmodel null values

I have made a webapp where you search by client id, and then adds orders on that client. The index action method assigns the chosen client to the viewmodel(vm.AllClients). The order table of course has information about the client. In the Insert method i want to use the information about the chosen client, but now vm.AllClients is returning null.
During debugging vm.AllClients is filled with one client object, as it should, during the running of the first method. When the second method is running vm.AllClients is empty.
I have tried to save the search string as a variable and find it in the db(Not a good solution), but the variable is also empty during the running of the second method. Also tried to save the chosen client as a Client object in the viewmodel, still no dice.
AddController
using MainWeb.Models;
public class AddController : Controller
{
OrderEntities db = new OrderEntities();// New instance of db.
ViewModel vm = new ViewModel();//New instance of viewmodel
[HttpPost]
public ActionResult Index(string searchTerm)
{
if (string.IsNullOrEmpty(searchTerm))
{
vm.AllClients = new List<Client>();
}
else
{
vm.AllClients = db.Clients.Where(x =>
x.RefNo.ToString().Equals(searchTerm)).ToList();
foreach (Client client in vm.AllClients)
{
vm.ThisClient = client;//Attempt at a different solution
break;
}
}
return View(vm);
}
public ActionResult InsertOrder(FormCollection form)
{
Order order = new Order();
order.ClientID = vm.AllClients[0].ID;//Here is where the error gets thrown
return RedirectToAction("Index");
}
View
#model MainWeb.Models.ViewModel
<div class="card border-primary mb-3 card-client" style="max-width: 40rem;">
<div class="card-header">Legg til</div>
<div class="card-body">
<div class="editor-label">
<table>
#using (Html.BeginForm("Test", "Add", FormMethod.Post))
{
<tr>
<td>#Html.Label("Velg Prosjekt:")</td>
</tr>
<tr>
<td>
#Html.DropDownList("fromDBProjects", (IEnumerable<SelectListItem>)ViewData["DBProjects"], new { #class = "form-control" })
</td>
</tr>
<tr>
<td>#Html.Label("Velg Produkt:")</td>
</tr>
<tr>
<td>
#Html.DropDownList("fromDBProducts", (IEnumerable<SelectListItem>)ViewData["DBProducts"], new { #class = "form-control" })
</td>
</tr>
<tr>
<td>#Html.Label("Pris:")</td>
</tr>
<tr>
<td><input type="submit" value="Submit" class="btn btn-primary" id="btn-search" /></td>
</tr>
}
</table>
</div>
</div>
</div>
</div>
}
ViewModel
namespace MainWeb.Models
{
public class ViewModel
{
public List<Client> AllClients { get; set; }
public Client ThisClient { get; set; }
}
}
Error:
Object reference not set to an instance of an object
In general I asume you are trying to write an new asp.net web application. But you should consider using asp.net core. This framework is the followup of asp.net mvc and you shouldn't start coding asp.net core mvc instead of the full framework.
That is deprecated and will get replaced by asp.net core mvc
I guess you should do the mvc tutorial from MS first. To get a better understanding how everything works.
https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/?view=aspnetcore-2.2
But now to your question:
There are a couple of issues in your example:
ViewModel shouldn't be a central class. Create it in your Controller Action, because every webrequest get's a new instance of your controller!
When you hit "InsertOrder" - the controller is created newly for the new request. Which means you get a new instance of the controller and your viewModel is empty again
Your cshtml will never hit your "InsertOrder"
InsertOrder can map your formcollection directly into a class.
Minor: you shouldn't layout your html with a table - See: Why not use tables for layout in HTML?
Your controller should look like this
public class AddController : Controller
{
OrderEntities db = new OrderEntities();// New instance of db.
[HttpPost]
public ActionResult Index(string searchTerm)
{
var vm = new ViewModel();
if (string.IsNullOrEmpty(searchTerm))
{
vm.AllClients = new List<Client>();
}
else
{
vm.AllClients = db.Clients.Where(x =>
x.RefNo.ToString().Equals(searchTerm)).ToList();
foreach (Client client in vm.AllClients)
{
vm.ThisClient = client;//Attempt at a different solution
break;
}
}
return View(vm);
}
[HttpPost]
public ActionResult InsertOrder(ViewModel vm)
{
Order order = new Order();
order.ClientID = vm.AllClients[0].ID;//Here is where the error gets thrown
return RedirectToAction("Index");
}
And your view should set the form to this:
#using (Html.BeginForm("InsertOrder", "Add", FormMethod.Post))
Each request gets a fresh controller instance, so you cannot use the global Viewmodel variable. If you want to communicate between controller actions use ViewData object or simply send the data to the client and get it via FormCollection or your ViewModel class.

How to display item out of the foreach statement in MVC5 entity framework

I have a index in controller and view following.
When I enter student ID on textbox search, I want to display information of student and list all activities of them. How to do that.
Thank you so much!
Controller:
public ActionResult Index(string Sid)
{
var grad = db.Gradings.Include(g => g.Activity).Include(g => g.Student).Where(g => g.StudentID == Sid).ToList();
}
View:
<div class="col-xs-4">
<p>
#using (Html.BeginForm())
{
<p>
Student ID: #Html.TextBox("Sid",ViewBag.FilterValue as string)
<input type="submit" value="Search" />
</p>
}
<table class="table">
<tr>
<th>Activity Name</th>
....
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.Activity.Name)</td>
<td>#Html.DisplayFor(modelItem => item.Responsibility)</td>
....
</tr>
}
</table>
</div>
<h3> Total score: #ViewBag.Total</h3>
Thanks for your help!
You need one action to serve your view initially when the user requests it:
public ActionResult Index()
{
return View();
}
Then you need another action to process the user's request when the request submits the form:
public ActionResult Index(string Sid)
{
var grad = db.Gradings.Include(g => g.Activity).Include(g => g.Student).Where(g => g.StudentID == Sid).ToList();
return View(grad);
}
Now grad is a List<Grading> and above we are passing it to the view as the model so make sure you use it in your view. You may need to include the namespace for List and Grading:
#model List<Grading>
Finally instead of using foreach in your view, use a for loop so your HTML tags have unique IDs. Right now (with foreach), all Grading records will have the same name and id attributes.

Using checkbox for multiple deletion in asp-mvc [duplicate]

I have a view with a table that displays my model items. I've extracted the relevant portions of my view:
#model System.Collections.Generic.IEnumerable<Provision>
#using (Html.BeginForm("SaveAndSend", "Provision", FormMethod.Post))
{
if (Model != null && Model.Any())
{
<table class="table table-striped table-hover table-bordered table-condensed">
<tr>
...
// other column headers
...
<th>
#Html.DisplayNameFor(model => model.IncludeProvision)
</th>
...
// other column headers
...
</tr>
#foreach (var item in Model)
{
<tr>
...
// other columns
...
<td>
#Html.CheckBoxFor(modelItem => item.IncludeProvision)
</td>
...
// other columns
...
</tr>
}
</table>
<button id="save" class="btn btn-success" type="submit">Save + Send</button>
}
...
}
This works fine and the checkbox values are displayed correctly in the view depending on the boolean value of the IncludeProvision field for the given model item.
As per Andrew Orlov's answer, I've modified the view and controller and the SaveAndSend() controller method is now:
[HttpPost]
public ActionResult SaveAndSend(List<Provision> provisions)
{
if (ModelState.IsValid)
{
// perform all the save and send functions
_provisionHelper.SaveAndSend(provisions);
}
return RedirectToAction("Index");
}
However, at this point the passed in model object is null.
Including the Provision model object for completeness:
namespace
{
public partial class Provision
{
...
// other fields
...
public bool IncludeProvision { get; set; }
}
}
My question is, what is the best way to grab the checked/unchecked value from each checkbox and update the session IncludeProvision field for each model item when the 'SaveAndSend' button is clicked?
You cannot use a foreach loop to generate form controls for properties in a collection. It creates duplicate name attributes (in your case name="item.IncludeProvision") which have no relationship to your model and duplicate id attributes which is invalid html. Use either a for loop (you models needs to be IList<Provision>
for(int i = 0; i < Model.Count; i++)
{
<tr>
<td>....</td>
<td>#Html.CheckBoxFor(m => m[i].IncludeProvision)<td>
</tr>
}
or create an EditorTemplate for typeof Provision. In /Views/Shared/EditorTemplates/Provision.cshtml (note the name of the template must match the name of the type)
#model Provision
<tr>
<td>....</td>
<td>#Html.CheckBoxFor(m => m.IncludeProvision)<td>
</tr>
and in the main view (the model can be IEnumerable<Provision>)
<table>
#Html.EditorFor(m => m)
</table>
As #mattytommo said in comments, you should post your model to controller. It can be done with putting your checkbox inside a form. After clicking on button "Save and exit" all data from inputs inside this form will be serialized and sent to your controller where you can perform manipulations with session variables and so on. After that you can redirect wherever you like.
Model
public class YourModel
{
...
public bool IncludeProvision { get; set; }
...
}
View
#model YourModel
...
#using (Html.BeginForm("SaveAndSend", "Test", FormMethod.Post))
{
...
#Html.CheckBoxFor(model => model.IncludeProvision)
...
<button type="submit">Save and send</button>
}
...
Controller
public class TestController : Controller
{
...
[HttpPost]
public ActionResult SaveAndSend(YourModel model)
{
if (ModelState.IsValid)
{
// Some magic with your data
return RedirectToAction(...);
}
return View(model); // As an example
}
...
}

Categories

Resources