I am developing an ASP.NET MVC 3 application in C# and I use Razor. I am now dealing with a problem concerning the binding of objects through ViewModels passed/received to/from the View by the Controller.
Let's make it clear. I have the following ViewModels:
public class ContainerViewModel
{
public int ContainerId {get; set;}
public string ContainerName {get; set;}
public List<ItemPostModel> ItemData {get; set;}
}
public class ItemPostModel
{
public int ItemId {get; set;}
public string ItemName {get; set;}
public int ItemValue {get; set;}
}
The ContainerViewModel is used to pass the data to the View. Its properties ContainerId and ContainerName are used just for display purposes. The List<ItemPostModel> property has to be filled using a Form. The View looks something like this (it is a simplified version):
<strong>#Model.ContainerName</strong>
#using (Html.BeginForm())
{
<fieldset>
#foreach(var item in Model.ItemData)
{
#Html.TextBox(item.ItemId);
#Html.TextBox(item.ItemName);
#Html.TextBox(item.ItemValue);
<p>
<input type="submit" value="Save" />
</p>
}
</fieldset>
}
The Controller corresponding action methods are as follows:
public ActionResult UpdateItems()
{
//fill in the ContainerViewModel lcontainer
return View("UpdateItems", lcontainer);
}
[HttpPost]
public ActionResult UpdateItems(int containerId, ItemPostModel itemData)
{
//store itemData into repository
}
The problem is that with this code the ItemPostModel itemData passed to the Post ActionMethod UpdateItems is always empty. The containerId is correctly passed. Same result if I use the following code in the Controller (obviously not DRY);
[HttpPost]
public ActionResult UpdateItems(ContainerViewModel container)
{
//extract itemData from ContainerViewModel container
//store itemData into repository
}
How can I "teach" the application that I want the form elements stored in the List<ItemPostModel>? Shall I modify the ModelBinder or there is a simpler way to perform this task? Thanks everybody for your answers.
Don't write loops in a view. Use editor templates:
<strong>#Model.ContainerName</strong>
#using (Html.BeginForm())
{
<fieldset>
#Html.EditorFor(x => x.ItemData)
<input type="submit" value="Save" />
</fieldset>
}
and inside the corresponding editor template (~/Views/Shared/EditorTemplates/ItemPostModel.cshtml):
#model ItemPostModel
#Html.TextBox(x => x.ItemId)
#Html.TextBox(x => x.ItemName)
#Html.TextBox(x => x.ItemValue)
And in the controller action you might need to specify the prefix:
[HttpPost]
public ActionResult UpdateItems(
int containerId,
[Bind(Prefix = "ItemData")]ItemPostModel itemData
)
{
//store itemData into repository
}
and that should be pretty much all. The editor template will take care of generating the proper input field names for the binding to work.
Related
This is not duplicate thread .
My case is to send array parameter without model. but other thread is send int, string parameter individual.
I know about how to post to controller with model object.
But sometimes I want to post data outside of model db and post in format object or array
How could I do?
View
<form action="/home/showdata" method="post">
<input type="text" name="arr.username" />
<input type="text" name="arr.password" />
<input type="text" name="arr.email" />
</form>
Controller
public class HomeController : Controller
{
[HttpPost]
public ActionResult ShowData(Array data)
{
return Content(data.username + data.password + data.email);
}
}
There are many different kinds of Models. E.g. Database Models, View Models, DTOs etc. So, in your case, the data you receive from the client is significantly different from the database model (which, by the way, is usually the case). This means you should create a model specific to a view, a View Model, then after validating the data, transfer that that data to the database model. For example:
public class SampleViewModel {
public int Id { get; set;}
public string Name { get; set; }
}
And then in your controller:
public IHttpActionResult SampleActionMethod(SampleViewModel model) {
if (!ModelState.IsValid) {
return BadRequest();
}
var sampleDbModel = new SampleDatabaseModel() {
FullName = model.Name,
ProductId = model.Id,
// ... some other properties ...
};
// ... Save the sampleDbModel ...
return Ok(); // .. or Created ...
}
This answers just shows you how to do what you are trying to do. But ideally, you should NOT use database model as parameters to action methods anyway. And there are a lot of other things involved, for which I suggest you look into Repository Pattern, Unit Of Work (for managing database tasks), and Automapper (for mapping stuff, if you want. E.g. view models to models) etc.
Hope this helps.
Hello i would recomended you FormCollection
<form action="/home/showdata" method="post">
<input type="text" name="username" />
<input type="text" name="password" />
<input type="text" name="email" />
</form>
in controller you can use FormCollection
public class HomeController : Controller
{
[HttpPost]
public ActionResult ShowData(FormCollection data)
{
string username=data.GetValues("username")[0];
string password=data.GetValues("password")[0];
string email=data.GetValues("email")[0];
return Content(username + password + email);
}
}
also if some html input has same name then you will get string array of their value.
The best solution here is to use a model. A model does not have to be related to a database table.
public ActionResult ShowData(Array data)
could be:
public ActionResult ShowData(YourModelNameHere data)
And you could define YourModelNameHere as something like:
public class YourModelNameHere
{
public string username {get; set;}
public string password {get; set;}
public string email {get; set;}
}
First you need to serialize your data on form and keep serialize data in hidden filed and after post get this data from formcollection using key and de-serialize
it.
This is just something that has been puzzling me, I'm wondering if there's a built in way for this.
Say you have a Package class
public class Package
{
public A AObject { get; set; }
public B BObject { get; set; }
}
And you have a view that uses this Package.
public ActionResult Action()
{
return View(new Package());
}
Now the view will accept this model and have 2 forms.
#model Path.To.Package
#Html.BeginForm("SubmitA", "MyController")
{
#Html.TextBoxFor(m => m.AObject.SomeProperty);
<input type="submit" />
}
#Html.BeginForm("SubmitB", "MyController")
{
#Html.TextBoxFor(m => m.BObject.AnotherProperty);
<input type="submit" />
}
If one would create two actions needed above that take Package as argument, this would work without question...
public JsonResult SubmitA(Package items) { ... }
public JsonResult SubmitB(Package items) { ... }
But at SubmitA the BObject would be null and in SubmitB AObject would be null.
My question here is whether you can submit only a part of the model? So the first form would only submit AObject and the second BObject so you could actually reach these via the following actions:
public JsonResult SubmitA (A a) { ... }
public JsonResult SubmitB (B b) { ... }
You can use the Prefix property of BindAttribute to bind to complex properties of a model. The attribute effectively removes the prefix from the submitted name/value pairs when binding to model.
Your controller methods would be
public JsonResult SubmitA([Bind(Prefix = "AObject")]A model) { ... }
public JsonResult SubmitB([Bind(Prefix = "BObject")]B model) { ... }
You should really use separate view model for each form. You can of course, use bind attribute or use specific property names in the controller action. But, that doesn't solve your real problem. You can only get either of the values and the other object will be unassigned or NULL. This is why you should have separate view model for each view / form. You can build your Package object once you have values for both objects.
I am very new to asp.net development. In my asp.net mvc project I have model "Employee" and I'm passing a list of "Employee" model to a RAZOR view and I'm trying to count different type of employees and show a summary.
my view is like this,
#{
int available = 0;
int onLeave = 0;
int away = 0;
int unAvailable = 0;
}
#foreach (var employee in Model){
<lable>#employee.Name</lable></br>
#if (#employee.Available){
#available=available+1;
}
#if (#employee.Unavailable){
#unAvailable=unAvailable;
}
#if (#employee.Away){
#away=away+1;
}
#if (#employee.Onleave){
#onLeave=onLeave+1;
}
}
<div>
<!--additional summary is displayed here-->
<label>Available:</label>#available
<label>Unavailable:</label>#unAvailable
<label>Away:</label>#away
<label>On Leave:</label>#onLeave
</div>
but when I run the my project variables "available","unAvailable","away" and "onLeave" don't get updated.
I'm sure that list is not empty because employee names are displaying.
can some explain me what is happening here and correct way of doing this
You should be doing this outside the before passing to the view like I mentioned in my original comment. You can create a new object called a ViewModel to represent the data exactly like you want it on the page. So I created a simple example, I only used the 4 properties of Employee you are displaying in you CSHTML page. On your View where you said your MODEL is either a list, arrary or whatever of Employee change it to EmployeeViewModel. Then in your controller where you get your list of employees set them to the Employees property of the Employee ViewModel.
public class EmployeeViewModel
{
public IEnumerable<Employee> Employees { get; set; }
public int TotalAvailable { get { return Employees.Count(emp => emp.Available); } }
public int TotalUnavailable { get { return Employees.Count(emp => emp.Unavilable); } }
public int TotalAway { get { return Employees.Count(emp => emp.Away); } }
public int TotalOnLeave { get { return Employees.Count(emp => emp.OnLeave); } }
}
public class Employee
{
public bool Available { get; set; }
public bool Unavilable { get; set; }
public bool Away { get; set; }
public bool OnLeave { get; set; }
}
//In the controller do this.
public ActionResult Index() //use your controller Action Name here
{
var employeeViewModel = new EmployeeViewModel { Employees = /*Your list of empoyees you had as a Model before here*/}
return View(employeeViewModel)
}
Change your CSHTML code to something like this:
#foreach(var employee in Model.Employees)
{
<label> #employee.Name </label></br>
}
<div>
<!--additional summary is displayed here-->
<label> Available:</label> #Model.TotalAvailable
<label> Unavailable:</label> #Model.TotalUnavailable
<label> Away:</label> #Model.TotalAway
<label> On Leave:</label> #Model.TotalOnLeave
</div>
An easy and quick way is:
<div>
<!--additional summary is displayed here-->
<label>Available:</label>#Model.Count(i => i.Available)<br>
<label>Unavailable:</label>do the same.
<label>Away:</label>do the same.
<label>On Leave:</label>do the same.
</div>
Make sure the model has already been "ToList()", or it might lead to mult-access of database.
Basically, I only use viewmodel when I need to pass more than 1 models to the view. Not worth in this case.
Make such calculations in View considered a BAD practice.
In your case better option will be create ViewModel with corresponding properties and then pass it to the model, previously calculating count for every type in controller using LINQ. Where you could reference your types like Model.available, Model.away and so on. Using ViewModel it is the best practice for MVC.
#Thorarins answer show you how to use LINQ in your code to calculate count for you types.
UPDATE:
You can use JS, but you should not, because it still not what supposed to happen in View. Work with data should not be handled in View. Don't be scared by ViewModels, they not that hard as it could seem. Please read this article which consider all ways to pass data to View, which has good example how create and pass ViewModel.
Mvc sample on how to do it:
you need a model class
public class EmployeeModel
{
public int Available {get; set;}
public int OnLeave {get; set;}
public int Away {get; set;}
public int UnAvailable {get; set;}
}
and a command:
public ActionResult Index()
{
var model = new EmployeeModel();
model.Available = employee.count(e=> e.available);
model.OnLeave = employee.count(e=> e.onLeave);
model.Away = employee.count(e=> e.away);
model.UnAvailable = employee.count(e=> e.unAvailable );
return View(model);
}
and a view
#model EmployeeModel
<div>
<!--additional summary is displayed here-->
<label>Available:</label>#Model.Available
<label>Unavailable:</label>#Model.UnAvailable
<label>Away:</label>#Model.Away
<label>On Leave:</label>#Model.OnLeave
</div>
I have a list of teams on my index page.
I'm trying to pass the text of an input(type text) from the index view back to the index controller, to reload the index page, this time only displaying items in my list which have matching text. eg - bob = bob
Index Controller
public ActionResult Index(string searchString)
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
var listOfTeams = from T in db.Teams
select T;
if (!String.IsNullOrEmpty(searchString))
{
listOfTeams = listOfTeams.Where(T => T.TeamName.Contains(searchString));
}
return View(listOfTeams.ToList());
}
How i'm trying to pass the data in the Index view
I've tried
<input type="text" id="inputTeamSearch" name="searchString" class="form-control" style="width:225px;height:60px" onblur="IsTextEmpty()" oninput="CheckTeams()" placeholder="Search">
#Html.ActionLink("Search", "Index")
and
#using(Html.BeginForm("Index", "Team"))
{
<input type="text" id="inputTeamSearch" name="searchString" class="form-control" style="width:225px;height:60px" onblur="IsTextEmpty()" oninput="CheckTeams()" placeholder="Search">
<input type="submit" id="Index" value="Index" />
Html.EndForm();
}
I'm sure this is probably a duplicate of some sort, if so please just pass me in the appropriate direction. I've looked for answers, but they're either long-winded or go into more complex detail than this.
So to post data to a controller you need a seperate post action which is decorated with the HttpPost attribute. This method needs to take a model as it's parameter:
[HttpPost]
Public ActionResult Index(IndexVM model)
{
var searchTerm = model.SearchTerm;
}
The view model needs to contain the fields that you intend to post.
Public class IndexVM
{
Public String SearchTerm { get; set; }
//Other model examples
public Int32 PageNumber { get; set; }
public Int32 NumOfItemsPerPage { get; set; }
}
Then your html needs to contain a text box that has the same name as the string property in your view model.
#Html.TextBoxFor(m => m.SearchTerm)
//And at the top of your html page you will need to include the model
#model Domain.Models.IndexVM
OR
<input type="text" name="SearchTerm">
Should work.
If you are already using an entity model you can create a new View model which contains the old entity and whatever else you need. So:
public class IndexVM
{
public Team Team { get; set; }
public String SearchTerm { get; set; }
}
Then on your index GET method where you're passing your team to your view you would have:
var view = new IndexVM();
view.Team = //your team object
return View(view);
I've been working on an MVC 4 Application and have run into a problem when attempting to update Models in a ViewModel.
My ViewModel (detailed below) contains one ComplexObjectOne and a List<ComplexObjectTwo>.
My GET ActionResult successfully populates the ViewModel from a database and everything displays correctly on my View.
The problem is encountered when attempting to pass the ComplexObjectOne and List<ComplexObjectTwo> to the POST ActionResult.
The ComplexObject is passed correctly but everything I've tried fails pass the List<ComplexObjectTwo> collection.
My ComplexModelOne Model
public class Test
{
public int Id {get;set;}
public string Result {get;set;}
public virtual ICollection<TestResult> TestResults {get;set;}
}
My ComplexModelTwo Model
public class TestResult
{
public int Id {get;set;}
public string Result {get;set;}
public string Comment {get;set;}
public virtual Test Test{get;set;}
}
My ViewModel
public class TestingViewModel
{
public TestingViewModel()
{
if(TestResults == null)
{
TestResults = new List<TestResult>();
}
}
public Test Test {get;set;}
public IEnumerable<TestResult> TestResults {get;set;}
}
My Edit() GET ActionResult
public ActionResult Edit(int id = 0)
{
var viewModel = new TestingViewModel();
Test test = testRepo.GetTestById(id);
var results = test.TestResults;
viewModel.Test = test;
viewModel.TestResults = results;
return View(viewModel);
}
My Edit() POST ActionResult
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(TestingViewModel model)
{
// do update - left out for brevity
}
My Edit.cshtml View
#model Namespace.Models.ViewModels.TestingViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.EditorFor(model => model.Test, "TestHeader")
<table>
<tr>
<th>Test</th>
<th>Result</th>
<th>Comment</th>
</tr>
#Html.EditorFor(model => model.TestResults, "TestResults")
</table>
<input type="submit" value="Update"/>
}
Within my View I do use a couple of EditorTemplates to display the property fields.
Any assistance, comments, or suggestions will be much appreciated. I'd like to be able to accomplish updating these entities on a single page instead of multiple pages which I resorted to in the Create() steps.
Thank you,
Patrick H. (stpatrck)
Replace:
#Html.EditorFor(model => model.TestResults, "TestResults")
with:
#Html.EditorFor(model => model.TestResults)
and then rename your EditorTemplates/TestResults.cshtml editor template to EditorTemplates/TestResult.cshtml (notice the missing s) and inside replace the model declaration from:
#model IEnumerable<TestResult>
to:
#model TestResult
Now obviously this will lead to getting rid of any for or foreach loops you might have written in this editor template because now ASP.NET MVC will automatically invoke the template for each element of the collection.
So for example:
#foreach (var item in Model)
{
#Html.EditorFor(x => item.SomeProperty)
}
will simply become:
#Html.EditorFor(x => x.SomeProperty)
Now look at the generated markup and notice the difference in the names of your input fields. Before you had:
<input type="text" name="item.SomeProperty" value="foo" />
and now you have:
<input type="text" name="TestResults[0].SomeProperty" value="foo" />
Now when you submit the form to the POST action the default model binder will be able to successfully bind the collection because now the naming convention is respected. You can read more about this convention in the following blog post.
Also you have circular references in your object graph which cannot be successfully serialized and model bound. You should use view models in order to break this circular dependency.