This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 4 years ago.
I can't get the values in controller from the post method used on the view and I don't know why. I saw a lot of forums and I thought that I was doing the right thing, but it doesn't seem to be the correct way to do because I can't get the values.
First I was using just the DisplayFor but that just shows and not pass the values then I join the HiddenFor to use that like an input like I saw in other forums. But the model always returns zero values.
AtribuiçãoController:
public IActionResult Create(List<Main> main)
{
return RedirectToAction(nameof(Index));
}
Index.cshtml:
#model ModelsLibrary.Main
<form asp-action="Create">
<table class="table">
<thead>
<tr>
<th>
<label>Disciplina</label>
</th>
<th>
<label>Turno</label>
</th>
<th>
<label>Tipologia</label>
</th>
<th>
<label>Docente</label>
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Turno)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.MetaDisciplina.Nome)
#Html.HiddenFor(modelItem => item.MetaDisciplina.Nome)
</td>
<td>
#Html.DisplayFor(modelItem => item.LetraTurno)
#Html.HiddenFor(modelItem => item.LetraTurno)
</td>
<td>
#Html.DisplayFor(modelItem => item.Tipologia.Tipo)
#Html.HiddenFor(modelItem => item.Tipologia.Tipo)
</td>
<td>
<select asp-for="Docente" asp-items="ViewBag.Docente"></select>
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
Index Method
[HttpGet]
public IActionResult Index()
{
var Turno = new List<Turno>();
Main main = new Main();
main.Turno = Turno;
ViewData["Ano"] = new SelectList(_context.AnoLetivo, "Ano", "Ano");
return View(main);
}
Hi have a problem binding the values to the controller. The method create should get values in the attributes but i don't know why and heaven don't know why the values not pass... I not pass a list to the view but i want one list in the create method.... If someone can help.
Your problem is in your Controller. Your create method do redirect to your Index method but you don't pass your List main
I think your code should be like this:
[HttpGet]
public ActionResult Index()
{
List<Main> main = (List<Main>)TempData["yourData"];
if (main == null)
{
main = new List<Main>();
}
return View("Index", main);
}
public IActionResult Create(List<Main> main)
{
TempData["yourData"] = main;
return RedirectToAction(nameof(Index));
}
The way your want to redirect your data is called the POST-REDIRECT-GET pattern. If your want more information about it, visit this website : https://andrewlock.net/post-redirect-get-using-tempdata-in-asp-net-core/
--------------------EDIT--------------------
You have a binding problem while passing data from View to Controller. You can use an TempData in your View to pass your complete Model to your controller:
#model List<WebApplication1.Models.TestObject>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#foreach (var item in Model)
{
#Html.DisplayFor(m => item.ToDisplay)
}
<form action="/Home/Create" method="post">
<button type="submit">Submit</button>
</form>
<script>
$('form').submit(function() {
#{ TempData["FullModel"] = Model; }
});
</script>
In this exemple, i use a jquery script to create a TempData and send it to the controller
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
List<TestObject> main = (List<TestObject>)TempData["yourData"];
if (main == null)
{
main = new List<TestObject>();
main.Add(new TestObject("1"));
main.Add(new TestObject("2"));
main.Add(new TestObject("3"));
main.Add(new TestObject("4"));
main.Add(new TestObject("5"));
}
return View("Index", main);
}
[HttpPost]
public ActionResult Create()
{
List<TestObject> main = (List<TestObject>)TempData["FullModel"];
TempData["yourData"] = main;
return RedirectToAction(nameof(Index));
}
}
Hope it was helpfull
Related
My VIEW (success.cshtml) is as below
#model IList<AzSample.Models.Employeelist>
#using (Html.BeginForm("Save","Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h2>Employees Details</h2>
<div>
<table>
<tr>
<th>Id</th>
</tr>
#foreach (var emp in Model)
{
<tr>
<td>#emp.ID</td>
</tr>
}
</table>
<button type="submit" value="save" onclick="#Url.Action("Save", "Home")">Save</button>
</div>
}
My Home Controller is as below
public class HomeController : Controller
{
public ActionResult Import(HttpPostedFileBase excelfile)
{
//Code to get data from Excel sheet
List<Employeelist> obj = new List<Employeelist>();
for( int row =2; row <=range.Rows.Count; row++)
{
Employeelist emp = new Employeelist();
emp.ID =((Excel.Range)range.Cells[row,1]).Text;
obj.Add(emp);
}
return View("success", obj);
}
[HttpPost]
public ActionResult Save(List<Employeelist> empLIst)
{
// Code fro storing the data got from VIEW.
}
}
My Model is as below
public class Employeelist
{
public string ID { get; set; }
}
I am calling import action method from some other page, and reading the data from Excel sheet and showing it on success.cshtml. Till here it works fine.
When ever i click on Save button, the debug point is coming back to Save action method, but the data (i.e. List<Employeelist>, basically all Employeelist.ID's ) is null ?
What is missing ?
The data for the empList is never submitted with the form. You should render <input> fields to submit your data.
#for(int i = 0; i < Model.Count(); ++i) {
#Html.HiddenFor(m => m[i].Id)
// send other properties
}
It is important that you bind to indexed properties so the MVC modelbinder can bind this as a collection. #Html.HiddenFor(m => m.Id) will not work.
See also Model Binding to a List MVC 4
--Edit--
As Stephen has pointed out, the modelbinder will try to bind this using the index, so your c# model must implement IList<T>.
Another pitfall are non-sequential indices.
You should use this > ASP.NET MVC TempData ?
ASP.NET MVC TempData dictionary is used to share data between controller actions. The value of TempData persists until it is read or until the current user’s session times out. Persisting data in TempData is useful in scenarios such as redirection, when values are needed beyond a single request.
The code would be something like this:
Import Method:
public ActionResult Import(HttpPostedFileBase excelfile)
{
//Code to get data from Excel sheet
for( int row =2; row <=range.Rows.Count; row++)
{
Employeelist emp = new Employeelist();
emp.ID =((Excel.Range)range.Cells[row,1]).Text;
obj.Add(emp);
}
TempData["doc"] = obj;
return View("success", obj);
}
View:
#model IEnumerable<AzSample.Models.Employeelist>
#using (Html.BeginForm("Save","Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h2>Employees Details</h2>
<div>
<table>
<tr>
<th>Id</th>
</tr>
#{var list = (List<AzSample.Models.Employeelist>)TempData["doc"];}
#{TempData["doc"] = list;}
#foreach (var emp in list)
{
<tr>
<td>#emp.ID</td>
</tr>
}
</table>
<button type="submit" value="save" onclick="#Url.Action("Save", "Home")">Save</button>
</div>
}
Save Method:
[HttpPost]
public ActionResult Save()
{
if (TempData["doc"] != null)
{
// Your code will be here
}
}
I just started studying MVC few days back. As far as I know, I did a small sample program. But some doubts I been facing. I am posting my doubts and code below. Please help me to understand it clearly.
Here I created four views and controller and a student class.
StudentClass
public class Student
{
public string Name { get; set; }
public string Age { get; set; }
public string Place { get; set; }
}
ViewOne
#model MyTestMVCApp.Models.Student
#{
ViewBag.Title = "ViewOne";
}
#using (Html.BeginForm())
{
<table style="border-color:Black;">
<tr>
<td>Name</td><td>Age</td><td>Place</td>
</tr>
<tr>
<td>--</td><td>--</td><td>--</td>
</tr>
</table>
<label>Enter Name : </label>
#Html.TextBoxFor(model => model.Name, new { name = "name"});
<input name="submit" type="submit" id="btnStart" class="button" value="Start Filling Details" />
ViewTwo.cshtml
#model MyTestMVCApp.Models.Student
#{
ViewBag.Title = "ViewTwo";
}
#using (Html.BeginForm())
{
<table style="border-color:Black;">
<tr>
<td>Name</td><td>Age</td><td>Place</td></tr>
<tr>
<td>#Model.Name</td><td>#Model.Age</td><td>#Model.Place</td>
</tr>
</table>
<label>Enter Age : </label>
#Html.TextBoxFor(model => model.Age, new { name = "age" });
<input name="submit" type="submit" id="btnNext" class="button" value="Next" />
}
ViewThree.cshtml
#model MyTestMVCApp.Models.Student
#{
ViewBag.Title = "ViewThree";
}
#using (Html.BeginForm())
{
<table style="border-color:Black;">
<tr><td>Name</td><td>Age</td><td>Place</td></tr>
<tr><td>#Model.Name</td><td>#Model.Age</td><td>#Model.Place</td></tr>
</table>
<label>Enter Age : </label>
#Html.TextBoxFor(model => model.Place, new { name = "place" });
<input name="submit" type="submit" id="btnNext" class="button" value="Next" />
}
ViewFour.cshtml
#model MyTestMVCApp.Models.Student
#{
ViewBag.Title = "ViewFour";
}
#{
<table style="border-color:Black;">
<tr><td>Name</td><td>Age</td><td>Place</td></tr>
<tr><td>#Model.Name</td><td>#Model.Age</td><td>#Model.Place</td></tr>
</table>
}
MyViewController.cs
public class MyViewController : Controller
{
public ActionResult ViewOne()
{
Student student = new Student();
return View(student);
}
[HttpPost]
public ActionResult ViewOne(Student student)
{
return View("ViewTwo", student);
//return RedirectToAction("ViewTwo",student);
}
[HttpPost]
public ActionResult ViewTwo(Student student)
{
return View("ViewThree", student);
//return RedirectToAction("ViewThree", student);
}
[HttpPost]
public ActionResult ViewThree(Student student)
{
return View("ViewFour", student);
}
}
My doubts
doubt 1. On the button click in ViewTwo,
[HttpPost]
public ActionResult ViewOne(Student student)
{
}
is debugging instead of ViewTwo's [HttpPost] actionresult.. Why ?
doubt 2. How can I pass the same instance of the student object I created in the ViewOne to all other Views , because my need is
On ViewOne, I get 'name' property of the student, then I pass the same object to ViewTwo.
On ViewTwo, I get 'age' property of the student, then I pass the same object to ViewThree.
On ViewThree, I get 'place' property of the student, then I pass the same object to ViewFour.
On ViewFour I display all the values of the student that I get through the above views.
It looks like you are returning ViewTwo from ViewOne's post action. When you do that you're still routing to the ViewOne action. The code below will show in your address bar as ViewOne, even though you're returning ViewTwo. Looks like you had the right idea at some point where you had a RedirectToAction call but you don't have an HttpGet for the ViewTwo action.
[HttpPost]
public ActionResult ViewOne(Student student)
{
return View("ViewTwo", student);
//return RedirectToAction("ViewTwo",student);
}
Another option, and this would probably be useful for you since you're trying to pass your Student object, would be to use RedirectToRoute where you pass the Student name, id or some other identifying item, as a parameter in the route. So the uri would end up looking something like this: ../MyView/ViewTwo/Joe
Because you are calling from ViewOne. if you want execute ViewTwo Action then use as below
#using (Html.BeginForm("ViewTwo", "MyView", FormMethod.Post))
It looks like it is the view definition that you have missed a small bit of logic.
the reason why you are hitting ViewOne on postback is because you are not setting the values the form it posting back to.
so in your ViewOne.cshtml and subsequent views you would need
#Html.BeginForm("ViewTwo","MyViewController")
{
//View code
}
The HTML.BeginForm will render with the RouteData if you do not provide it in the call for begin form.
I am having some trouble. I have a view as below
#model IEnumerable<Tipstr.Models.Posts>
<div class ="mainC">
<div class = "leftContent">
#Html.Partial("_LeaderboardPartial")
</div>
<div class = "rightContent">
#foreach (var item in Model) {
<h1 class ="ptitle">
#Html.DisplayFor(modelItem => item.title)
</h1>
<p class ="date">
posted #Html.DisplayFor(modelItem => item.date)
</p>
<p class ="post">
#Html.DisplayFor(modelItem => item.body)
</p>
<hr />
}
</div>
</div>
<div class="Cpad">
<br class="clear" /><div class="Cbottom"></div><div class="Cbottomright">
</div>
</div>
And I have a partial, _LeaderboardPartial, as shown
#model IEnumerable<Tipstr.Models.Leaderboard>
<table id="pattern-style-a" summary="Meeting Results">
<thead>
<tr>
<th scope="col">Username</th>
<th scope="col">Tipster Score</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.userName)</td>
<td> #Html.DisplayFor(modelItem => item.score)</td>
</tr>
}
</tbody>
</table>
I can't seem to get it to work I think it has something to do with passing in one model from the layout and then another from the partial. How can I get around this? I get the following error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[Tipstr.Models.Posts]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[Tipstr.Models.Leaderboard]'.
As #David Tansey said, you can have a view model that contains a reference to the two types
public class MyViewModel
{
public MyViewModel(IEnumerable<Posts> posts,
IEnumerable<Leaderboard> leaderboard)
{
//you can add null checks to ensure view model invariants
this.Posts = posts;
this.Leaderboard = leaderboard;
}
public IEnumerable<Posts> Posts { get; private set; }
public IEnumerable<Leaderboard> Leaderboard{ get; private set; }
}
And in your main view you will call Html.Partial like this
#model MyViewModel
<div class ="mainC">
<div class = "leftContent">
#Html.Partial("_LeaderboardPartial", this.Model.Leaderboard)
</div>
....
If you do this, you should change the foreach to iterate through Model.Posts instead of Model
#foreach (var item in this.Model.Posts)
This line of code:
#Html.Partial("_LeaderboardPartial") in the first view implies sending along its own model IEnumerable<Tipstr.Models.Posts> to the partial.
The partial expects a reference to type IEnumerable<Tipstr.Models.Leaderboard>.
Perhaps you need a view model that contains a reference to one of each of these types?
I am using ASP MVC 4.0 and would like to understand the basics of custom validation. In this particular case, the model is not at all strongly typed with the controller or view so I need something different.
What I'd like to do is accept a new username on signup to my service, look in the database, and re-present the original form with a message if that username is taken.
This is my input form:
#{
ViewBag.Title = "Index";
}
<h2>New account</h2>
<form action= "#Url.Action("submitNew", "AccountNew")" method="post">
<table style="width: 100%;">
<tr>
<td>Email:</td>
<td> </td>
<td><input id="email" name="email" type="text" /></td>
</tr>
<tr>
<td>Password:</td>
<td> </td>
<td><input id="password" name="password" type="password" /></td>
</tr>
<tr>
<td>Confirm Password:</td>
<td> </td>
<td><input id="passwordConfirm" name="passwordConfirm" type="password" /></td>
</tr>
<tr>
<td></td>
<td> </td>
<td><input id="Submit1" type="submit" value="submit" /></td>
</tr>
</table>
</form>
and here is my controller method upon submit:
public ActionResult submitNew()
{
SomeService service = (SomeService)Session["SomeService"];
string username = Request["email"];
string password = Request["password"];
bool success = service.guestRegistration(username, password);
return View();
}
If success is false, I'd just like to re-present the form with a message indicating so. I'm missing the basics of this error flow. Would you please help? thanks in advance.
You could add a ViewBag item
bool success = service.guestRegistration(username, password);
if (!success)
{
ViewBag.Error = "Name taken..."
}
return View();
But you should create a view model...
public class ViewModel
{
public string UserName {get; set;}
//...other properties
}
...strongly type your view and use the built in html helpers...
#model ViewModel
//...
#using BeginForm("SubmitNew", "AccountNew", FormMethod.Post)()
{
//...
<div>#Html.LabelFor(m => m.Username)</div>
<div>#Html.TextBoxFor(m => m.Username)</div>
<div>#Html.ValidationMessageFor(m => m.Username)</div>
}
...and leverage ModelState in the controller
[HttpPost]
public ActionResult SubmitNew(ViewModel viewModel)
{
if(ModelState.IsValid)
{
SomeService service = (SomeService)Session["SomeService"];
bool success = service.guestRegistration(viewModel.username, viewModel.password);
if (success)
{
return RedirectToAction("Index");
}
ModelState.AddModelError("", "Name taken...")"
return View(viewModel);
}
}
...or even write your own validator and just decorate your model property, eliminating the need to check success in the controller.
My ViewResult Controller:
public ViewResult Controller(int id)
{
List<Data> dataList = dataAccess.getDataById(id);
Results[] resultArray = new Results[dataList.Count];
ViewBag.results= resultArray;
return View(dataList);
}
My View:
#model IEnumerable<Solution.Models.Data>
#{
Solution.Models.Results[] res= ViewBag.results;
}
#using (Html.BeginForm()) {
<table>
#{
int i = 0;
foreach (var item in Model) {
<tr>
<td>
Snippet: #Html.DisplayFor(modelItem => item.text)
</td>
</tr>
<tr>
<td>
Translation: #Html.EditorFor(modelItem => res[i].text)
</td>
</tr>
i++;
}
}
</table>
<p>
<input class="CRUD-buttons" type="submit" value="Send" />
</p>
}
My Controller ActionResult:
[HttpPost]
public ActionResult Controller(/*List<Data> dataList, */Results[] results)
{
ResultText = results[0].text; //NullReferenceException
}
dataList and results are empty. I read a couple of posts on stackoverflow, but could not find a solution.
I took already a look on the following blog (link) but its MVC 2 code. :(
There are multiple ways to do this. What gets you here is that in order for you to receive the results parameter, the name for the generated edit controls should be results[0] for the first, results[1] for the second, etc with no gaps in the indexes (this is because of how DefaultModelBinder expects to find the fields named when the form is posted).
So one immediate (although not very good) solution would be to specify the name correctly:
#Html.TextBox(string.Format("results[{0}]", i), res[i].text)
A much better solution would be to put the results into your model (better yet, in a ViewModel created specifically for this view). For example, first of all you create a class that encapsulates one piece of data and the corresponding result:
class ItemViewModel
{
Solution.Models.Data TheData { get; set; }
Solution.Models.Results TheResults { get; set; }
}
You then make your view have a collection of these items as its model:
#model IEnumerable<ItemViewModel>
and then output the edit control with
Translation: #Html.EditorFor(modelItem => modelItem.TheResults)
Finally, you modify the postback action to accept an array of ItemViewModel:
[HttpPost]
public ActionResult Controller(ItemViewModel[] results)
General piece of advice: Try to avoid using EditorFor with objects that are not part of your view's model (for example, things you have passed through ViewBag or ViewData). If you do this, MVC will punish you.