I have this in my controller:
public ActionResult Index()
{
var viewModels = _dataSyncService.Get().Select(provider => new IndexViewModel
{
Selected = false, Provider = provider
}).ToList();
return View(viewModels);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(IEnumerable<IndexViewModel> viewModels)
{
//Breakpoint on this to check viewModels
}
ViewModel:
public class IndexViewModel
{
public bool Selected { get; set; }
public IDataSyncProvider Provider { get; set; }
}
And my Index.cshtml:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<button type="submit" class="btn blue">Trigger Selected</button>
#foreach (var sync in Model)
{
<tr>
<td>
#Html.CheckBoxFor(s => sync.Selected)
</td>
<td>#sync.Provider.FriendlyName</td>
</tr>
}
}
But my models are posted back, viewModels always comes back as null. I read alot about having to assign Id's to the check box etc but I thought that's what Html.CheckBoxFor is for.
I'm sure I'll be kicking myself about this at some point but I could do with some guidance please.
If you want to use the built in Html helpers like Html.CheckBoxFor then you need to use a for loop instead of the foreach in order to the helpers render the correct input names:
#for(int sync = 0; sync < Model.Count; sync++)
{
<tr>
<td>
#Html.CheckBoxFor(m => Model[sync].Selected)
</td>
<td>#Model[sync].Provider.FriendlyName</td>
</tr>
}
You can read more about binding to lists here: Model Binding To A List
This is because, you are just redirecting it using return RedirectToAction("Index");
In your POST action
try :
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(IEnumerable<IndexViewModel> viewModels)
{
return View(viewModels);
}
Hope will help.
Related
Im working on an application which can download emails. In my view if checkbox/checkboxes are checked, i want to pass an ID to controller to download this messages. I will try to explain with code:
#using (Html.BeginForm("DownloadData", "Messages", FormMethod.Post))
{
<table class="table table-bordered table-hover">
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td><input type="checkbox"
name="selectedIds[#i].messageID"/></td>
<td>#Model[i].messageFrom</td>
<td>#Model[i].messageSubject</td>
<td>#Model[i].messageDate</td>
</tr>
}
</table>
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Download" />
</div>
}
The #i in name="selectedIds[#i].messageID" doesnt going to be good because its increasing and if i check: 1st the 2nd and the 4th message, it will add only the first two elements to my list.
The controller:
[HttpPost]
public ActionResult DownloadData(List<MessagesModels> selectedIds)
{
return View(...);
}
The model:
public class MessagesModels
{
public int messageID { get; set; }
public string messageSubject { get; set; }
...
}
I think it isnt hard, but cant find the sollution. Thanks for those who can help!
To avoid changing your message model just for this view (or if you are unable to begin with) I would recommend creating a new model for this view to handle selecting like this:
public class MessageSelectionViewModel
{
public MessagesModels Message { get; set; }
public bool IsSelected { get; set; }
}
Your view would now be a list of these new view objects so would look more like this:
<td>#Html.CheckBoxFor(item => item[i].IsSelected)</td>
<td style="display:none;">#Html.HiddenFor(item => item[i].Message.Id)</td>
<td>#Model[i].Message.messageFrom</td>
<td>#Model[i].Message.messageSubject</td>
<td>#Model[i].Message.messageDate</td>
Your post method would become:
[HttpPost]
public ActionResult DownloadData(IEnumerable<MessageSelectionViewModel> selectedMessages)
{
foreach (int messageId in selectedMessages.Where(m => m.IsSelected == true).Select(m => m.Message.Id))
{
}
return View(...);
}
Of course if it's not a big deal you could save some time and effort and just add IsSelected to your model like Stephen suggested.
First, I'd like to say that I did a lot of researches, tried many ways but none of it worked. I'd like to avoid:
writing my own model binder
packing form into json
reading directly from FormCollection object
My model class looks like that:
public class ListViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsChecked { get; set; }
}
I'm passing it to the view as a IEnumerable collection.
I'm trying to have all the form data packed into IEnumerable, like here
[HttpPost]
[Authorize(Roles = "User")]
public ActionResult EditVisitLists(List<ListViewModel> model)
{
//...
}
Unfortunately, my every attemp fails, I'm receiving null as a model (probably model binder doesn't recognize the form the way I'd like it to)
Here is my latest attemp:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(m => item.Name)
</td>
<td>
#Html.CheckBoxFor(m => item.IsChecked, new { id = "[" + item.Id + "].Name" })
#Html.HiddenFor(m => item.Name)
</td>
</tr>
}
Just use a for loop instead of a foreach to get correct binding
#for (var i = 0; i < Model.Count; i++) {
#Html.Displayfor(m => Model[i].Name
#Html.CheckBoxFor(m => Model[i].IsChecked)
#Html.HiddenFor(m => Model[i].Name)
}
Look at the html generated, you will now have different name and id attributes, which should bind fine.
VS'12 KendoUI InternetApplication Template C# asp.net EF Code First
My Question is how to pass both the Regular ( are passing now ) values and the Ienumerable(passing null) into my controller and saving them to the Database using EF Code First in a Many-2-Many Relationship manor.
The Following is what i have tried
Main View
#model OG.Models.UserProfiles
#using (Html.BeginForm())
{
<div class="editor-field">
<div class="Containter">
<div>
#Html.DisplayFor(model => model.UserName)
</div>
<div class="contentContainer">
#foreach (var item in Model.Prospects)
{
<table>
<tr>
<td>
#Html.Label("Current Prospects")
</td>
</tr>
<tr>
<td>
#Html.DisplayNameFor(x=>item.ProspectName)
</td>
</tr>
</table>
}
</div>
</div>
<div class="contentContainer2">
#Html.Partial("_UsersInProspectsDDL", new OG.ModelView.ViewModelUserInProspects() { Users = Model.UserName })
</div>
</div>
}
Partial View
#model OG.ModelView.ViewModelUserInProspects
<label for="prospects">Prospect:</label>
#(Html.Kendo().DropDownListFor(m=>m.Prospects)
.Name("Prospects")
.HtmlAttributes(new { style = "width:300px"}) //, id = "countys"})
.OptionLabel("Select Prospect...")
.DataTextField("ProspectName")
.DataValueField("ProspectID")
.DataSource(source => {
source.Read(read =>
{
read.Action("GetCascadeProspects", "ChangeUsersInfo")
.Data("filterProspects");
})
.ServerFiltering(true);
})
.Enable(false)
.AutoBind(false)
.CascadeFrom("Clients")
</div>
Model for PartialView
public class ViewModelUserInProspects
{
public string Clients { get; set; }
public IEnumerable<dbClient> AvailableClients { get; set; }
public string Prospects { get; set; }
public IEnumerable<dbProspect> AvailableProspects { get; set; }
public string Users { get; set; }
public IEnumerable<UserProfiles> AvailableUsers {get;set;}
}
}
Main Model
Standart SimpleMemberShipUserTable
Post Method
[HttpPost]
public ActionResult UsersInProspect(
[Bind(Include= "ProspectName, ProspectID")]
UserProfiles userprofiles, ViewModelUserInProspects values, FormCollection form)
//<- Trying different things sofar
{
if (ModelState.IsValid)
{
//string something = form["Prospects"];
int prosID = Convert.ToInt16(values.Prospects);
int UserID = userprofiles.UserID; // <- THIS VALUE is null atm.
This is where i need to save both ID's to the EF Generated / Mapped Table. Unsure how.
db.Entry(userprofiles).CurrentValues.SetValues(userprofiles);
db.Entry(userprofiles).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userprofiles);
}
Please take a look Here
Goes over ViewModels
What EditorTemplate are and how to use them
What the GET Method would look like
What the Edit View would look like
Give you a View Example
What the Post Method would look like
Extension to: How do you handle multiple submit buttons in ASP.NET MVC Framework?
Let us say a view is composed of partial views bound with related models, let's say a student is required to provide multiple contact persons(partial view bound to Person model) and multiple contact numbers(partial view bound to a model) to get registered, sorry for the bad example. Once a contact person or number is added, an action (child postback or whatever) is called which validates the related model (not student model), adds it in a list and returns to the same page for further processing. Once all are added, the parent/master action validates whole student model and processes it.
How to validate the specific model for which an action is being called, add it to the page and return the same student view with added values in response?
This solution uses #2 (Session) since its simpler to code however this demonstrates the principles.
Views
Index View:
#using StackOverflow.Models
<div>
#{ Html.RenderPartial("PersonGrid", Model.Persons, new ViewDataDictionary()); }
#Html.Partial("NewPerson", new Person())
#{ Html.RenderPartial("ContactGrid", Model.Contacts, new ViewDataDictionary()); }
#Html.Partial("NewContact", new Contact())
#using(Html.BeginForm("Validate", "Home", FormMethod.Post))
{
<input type="submit" value="Validate" />
}
</div>
Person Grid
#model IList
<table>
<thead>
<tr>
<td>First Name</td>
<td>Last Name</td>
</tr>
</thead>
<tbody>
#if (Model != null && Model.Any())
{
foreach (var person in Model)
{
<tr>
<td>#person.FirstName</td>
<td>#person.LastName</td>
</tr>
}
}
else
{
<tr>
<td colspan="2" style="text-align: center">No persons available</td>
</tr>
}
</tbody>
</table>
Contact Grid
#model IList
<table>
<thead>
<tr>
<td>Phone</td>
</tr>
</thead>
<tbody>
#if (Model != null && Model.Any())
{
foreach (var contact in Model)
{
<tr>
<td>#contact.Phone</td>
</tr>
}
}
else
{
<tr>
<td>No contacts available</td>
</tr>
}
</tbody>
</table>
New Person
#model StackOverflow.Models.Person
#using (Html.BeginForm("NewPerson", "Home", FormMethod.Post))
{
<div>
#Html.Hidden("PersonViewState", TempData["PersonViewState"])
#Html.LabelFor(m => m.FirstName)<br />
#Html.TextBoxFor(m => m.FirstName)<br />
<br />
#Html.LabelFor(m => m.LastName)<br />
#Html.TextBoxFor(m => m.LastName)<br />
<br />
<input type="submit" value="Submit" />
</div>
}
New Contact
#model StackOverflow.Models.Contact
#using (Html.BeginForm("NewContact", "Home", FormMethod.Post))
{
<div>
#Html.LabelFor(m => m.Phone)<br />
#Html.TextBoxFor(m => m.Phone)<br />
<br />
<input type="submit" value="Submit" />
</div>
}
Models
public class Person
{
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
}
public class Contact
{
[Display(Name = "Phone")]
public string Phone { get; set; }
}
public class HomeModel
{
public IList<Person> Persons { get; set; }
public IList<Contact> Contacts { get; set; }
}
Helpers
public static class PersistenceMechanism
{
public static IList GetPersons()
{
return (IList<Person>) HttpContext.Current.Session["__Persons"];
}
public static IList GetContacts()
{
return (IList<Contact>) HttpContext.Current.Session["__Contacts"];
}
public static void Update(IList<Person> persons)
{
HttpContext.Current.Session["__Persons"] = persons;
}
public static void Update(IList<Contact> contacts)
{
HttpContext.Current.Session["__Contacts"] = contacts;
}
}
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new HomeModel
{
Persons = PersistenceMechanism.GetPersons(),
Contacts = PersistenceMechanism.GetContacts()
};
return View(model);
}
[HttpGet]
public ActionResult PersonGrid()
{
var persons = PersistenceMechanism.GetPersons();
return PartialView(persons);
}
[HttpGet]
public ActionResult ContactGrid()
{
var contacts = PersistenceMechanism.GetContacts();
return PartialView(contacts);
}
[HttpPost]
public ActionResult NewPerson(Person model)
{
var persons = PersistenceMechanism.GetPersons() ?? new List<Person>();
persons.Add(model);
PersistenceMechanism.Update(persons);
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult NewContact(Contact model)
{
var contacts = PersistenceMechanism.GetContacts() ?? new List<Contact>();
contacts.Add(model);
PersistenceMechanism.Update(contacts);
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Validate()
{
var persons = PersistenceMechanism.GetPersons();
var contacts = PersistenceMechanism.GetContacts();
// validate
// ...
return RedirectToAction("Index");
}
}
To repeat the question to ensure that I have the idea of what you are asking.
Your page page is build from two partial views with different models. Both of these partial views contain a form which will on submit build a grid of records on the UI. The main page will have a further validate button which will then validate the entire contents of both grids on postback?
In this situation I will like to have two forms where the submit event is powered by Ajax. Either jQuery / Microsoft Ajax. Both forms will submit to two separate actions which will accept their respective models, Person and Contact. Each submit button will return its respective partial view which will be a grid showing the collection of items that have been submitted so far. The partial view returned will update a specified target (e.g. div) since we are using AJAX.
Of course it will be necessary to remember the previous items that were submitted so far in order reconstruct the grid with the new item added. This will mean some soft of persistent storage is required. Some options available are:
Database
Session
Hidden form field(s) (preferred). It is possible to use the array model binding mechanism to support this or a simple serialized object (or list of objects) in a single hidden field.
Validation now becomes simple as the model are available server side through your persistent storage mechanism on postback.
This question is a bit different than most. My code works but I don't understand why it works.
I am trying to understand why changes made in the form get persisted after posting to the server.
Model:
public class TestUpdateModel
{
public int Id { get; set; }
public List<CarrierPrice> Prices { get; set; }
public TestUpdateModel() { } // parameterless constructor for the modelbinder
public TestUpdateModel(int id)
{
Id = id;
Prices = new List<CarrierPrice>();
using (ProjectDb db = new ProjectDb())
{
var carriers = (from c in db.Carriers
select c);
foreach (var item in carriers)
{
var thesePrices = item.Prices.Where(x => x.Parent.ParentId == Id);
if (thesePrices.Count() <= 0)
{
Prices.Add(new CarrierPrice
{
Carrier = item
});
}
else
Prices.Add(thesePrices.OrderByDescending(x => x.DateCreated).First());
}
}
}
}
Controller:
public ViewResult Test(int id = 1)
{
TestUpdateModel model = new TestUpdateModel(id);
return View(model);
}
[HttpPost]
public ViewResult Test(TestUpdateModel model)
{
model = new TestUpdateModel(model.Id);
return View(model); // when model is sent back to view, it keeps the posted changes... why?
}
View
#model Namespace.TestUpdateModel
#{ ViewBag.Title = "Test"; }
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id)
#Html.EditorFor(x => x.Prices)
<input type="submit" value="Save" />
}
EditorTemplate
#model Namespace.CarrierPrice
<tr>
<th>
#Html.HiddenFor(model => model.CarrierPriceId)
#Html.HiddenFor(model => model.DateCreated)
#Model.Carrier.Name
</th>
<td>
$#Html.TextBoxFor(model => model.Fee)
</td>
</tr>
The Source of My Confusion
1) Load the page in the browser
2) Change the value of the model.fee TextBox
3) Submit
At this point I would expect that model = new TestUpdateModel(model.Id); would create a new TestUpdateModel object and wipe out my changes so the original values re-appear when the view is returned. But what actually happens is my changes in the form are persisted to the postback.
Why does this happen?
Thanks for any help.
No. The reason is that the View Engine looks in the ModelState to fill your values before it looks at the model when it renders your view. If there are posted values, then it will find those first and use them.
If you want to override this behavior, then you need to clear the ModelState first with:
ModelState.Clear();