in a MVC C# View I show the records of specifics employees, for that I use MVCScaffolding and the model below
public class Developer
{
public int code { get; set; }
public string areaDev { get; set; }
public string nameDev { get; set; }
public string expDev { get; set; }
public string langDev { get; set; }
}
the view uses razor and for every record there is a checkbox input
#model IEnumerable<WebApplication1.Models.Developer>
#using(Html.BeginForm("ShowRecords","Home"))
{
<table class="table">
<tr>
<th>#Html.DisplayNameFor(model => model.code)</th>
<th>#Html.DisplayNameFor(model => model.areaDev)</th>
<th>#Html.DisplayNameFor(model => model.nameDev)</th>
<th>#Html.DisplayNameFor(model => model.expDev)</th>
<th>#Html.DisplayNameFor(model => model.langDev)</th>
<th>select</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.code)</td>
<td>#Html.DisplayFor(modelItem => item.areaDev)</td>
<td>#Html.DisplayFor(modelItem => item.nameDev)</td>
<td>#Html.DisplayFor(modelItem => item.expDev)</td>
<td>#Html.DisplayFor(modelItem => item.langDev)</td>
<td><input type="checkbox" name="code" value="#item.code" /></td>
</tr>
}
</table>
<input type="submit" value="SEND" />
}
and what I want is to retrieve the information of code(integer code) when the user click the checkbox of one/many specific records displayed in the view,
for that in my controller I receive a int[] as shown below
public ActionResult ShowRecords(int[] datos)
{
{
foreach(var item in datos)
{ ... some code goes here ... }
return View();
}
But I don't receive anything from the view, always receive NULL
could you please help me and tell how to retrieve the code info of the due checked row in my controller?
Edit:
just added the isChecked property
public class Developer
{
public int code { get; set; }
public string areaDev { get; set; }
public string nameDev { get; set; }
public string expDev { get; set; }
public string langDev { get; set; }
public bool isChecked { get; set; }
}
the controller that sends info to the view has the new property sets to false(in order to not present the checkbox checked)
while (dr.Read())
{
Models.Developer data = new Models.Developer();
data.code = Convert.ToInt32(dr["code"]);
data.areaDev = dr["areaDev"].ToString();
data.nameDev = dr["nameDev"].ToString();
data.expDev = dr["expDev"].ToString();
data.langDev = dr["langDev"].ToString();
data.isChecked = false;
Records.Add(data);
}
in my View I added this
#Html.CheckBoxFor(modelItem => item.isChecked)
and in the controller I expects to receive a list of developer model
public ActionResult ShowRecords(List<WebApplication1.Models.Developer> datos)
but stills receive NULL
Your generating checkboxes with name="code" therefore you POST method signature needs to be
public ActionResult ShowRecords(int[] code)
The code parameter will contain an array of the values of the checked checkboxes.
Based on your edit using a view model, your view will need to use a for loop or custom EditorTemplate so that the name attributes are generated correctly for binding to a collection (refer Post an HTML Table to ADO.NET DataTable for more detail)
#for (int i = 0; i < Model.Count; i++)
{
<td>
#Html.CheckBoxFor(m => m[i].code) // include hidden input so its submitted
#Html.DisplayFor(m=> m[i].code)
</td>
....
<td>#Html.CheckBoxFor(m => m[i].isChecked)</td>
}
and the POST method signature would be
public ActionResult ShowRecords(List<Developer> model)
Change the parameter of your action to IEnumerable<WebApplication1.Models.Developer> and then inspect the isChecked property of each of these (or just use LINQ to filter it down). If you don't like that approach, you could use Javascript to to return those ints. I will point out that returning the model while using more bandwidth (because it's more data going back to the server) but it conforms with the MVC pattern more than returning the ints.
You can create a viewModel with a Developper object and an additional isChecked property. Then modify your view to use:
IEnumerable <DevelopperViewModel>
as Model for your view and bind the value of isChecked.
Related
This question sort of has an answer on StackOverflow here, but it's over 5 years old and I'm not sure it's the best or only way, particularly because I already have a Dictionary-type object - a list of
I have a ViewModel:
public class SummaryList
{
public List<Summary> Summaries { get; set; }
public IEnumerable<SelectListItem> Managers { get; set; }
public SummaryList()
{
Managers = new DAL.GetManagersAsSelectListItem();
}
}
The Summary Model looks something like this:
[Key]
public int Id { get; set; }
... OtherProperties ...
public int ManagerId { get; set; }
public IEnumerable<SelectListItem> Managers { get; set; }
On a page where the user can edit this ViewModel, which shows the entire List of Summary objects, I can do this:
#model MyProject.ViewModels.SummaryList
#using (Html.BeginForm("SummaryListSave", "Controller", FormMethod.Post))
{
#Html.AntiForgeryToken()
if (Model != null)
{
if (Model.Summaries.Count > 0)
{
<table>
<thead>...</thead>
<tbody>
#for (int i = 0; i < Model.Summaries.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(m => m.Summaries[i].WhateverProperty)
</td>
<td>
#Html.DropDownListFor(m => m.Summaries[i].ManagerId, Model.Managers)
</td>
</tr>
}
</tbody>
</table>
}
}
}
i.e. I can show a DropDownList for each row which takes the ManagerId and because I provide the IEnumerable Managers it does my two-way binding for ddl options and for saving the edits.
I want to do the same as this but only DISPLAY the value, so not using a DropDownList, instead of showing ManagerId I want the respective value from the SelectListItem
Is there no mechanism in the Html helpers to do this, using my already existing/populated SelectListItem? I tried DisplayTextFor and a few others but no success yet.
If none exist, what would be the easiest way to implement something using the Enumerable
My first idea is to wrap the parameter on the line #Html.DisplayFor(m => m.OutbreakSummaries[i].WeeklyMeetingLeadId), which will give the Id, in a function that gets the value from Model.Managers but I don't know where to start with the syntax.
I'm not 100% sure I understood, but did you try using (SelectList) in your View, and also indicating the Value and Text properties when creating the SelectList in controller?
like this:
View:
#Html.DropDownListFor(m => m.Summaries[i].ManagerId, (SelectList) Model.Managers)
Controller:
Model.Managers = new SelectList(new DAL.GetManagersAsSelectListItem(), "ManagerID", "Description");
Replace Description wih the real property name inside Manager VM (for Text)
When this is configured like this, the DropDownList should show the Text and not the Value (id).
I am having alot of difficult getting my viewmodel to work correctly. As a bit of context i am using a viewmodel to use two models from another project. These models contain the User information and thier chosen Device in a localdb. However i cannot currently use a view to display the data from both of those models on one view so i created a viewmodel.
However I am current recieving:
Error: 'System.Collections.Generic.IEnumerable' does not contain a definition for 'UserID' and no extension method 'UserID' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?)
This error is occurring for all of the model objects. If i can get around it it will be the first step to a functioning view model. Any help would be greatly appreciated.
User.cs - Model (in project: FaceToFace)
namespace FaceToFace.Model
{
public class User
{
public int UserID { get; set; }
public string CodeName { get; set; }
public bool UseBriefInstructions { get; set; }
public ICollection<RegimeItem> RegimeItems { get; set; }
public Device Device { get; set; }
public virtual ICollection<Grading> UserGradings { get; set; }
public User()
{
this.RegimeItems = new List<RegimeItem>();
Device = new Device();
}
}
public class RegimeItem
{
public int RegimeItemID { get; set; }
public Exercise RegimeExercise { get; set; }
}
}
Device.cs - Model (in project: FaceToFace)
namespace FaceToFace.Model
{
public class Device
{
public int DeviceID { get; set; }
public string Name { get; set; }
}
}
UserDeviceViewModel.cs (in project: FaceToFaceWebsite)
namespace FaceToFaceWebsite.Models
{
public class UserDeviceViewModel
{
public UserDeviceViewModel()
{
User = new User();
Devices = new List<SelectListItem>();
}
public User User { get; set; }
public IList<SelectListItem> Devices { get; set; }
}
}
PatientController.cs - Only a segment of the entire page to avoid spam (Project: FaceToFaceWebsite)
namespace FaceToFaceWebsite.Controllers
{
public class PatientController : Controller
{
private F2FData db = new F2FData();
public ActionResult Index()
{
var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();
return View(viewModel);
}
}
}
Views/Patient/Index.cshtml (facetofacewebsite)
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
#*#model FaceToFaceWebsite.Models.UserDeviceViewModel*#
#*#model IEnumerable<FaceToFace.Model.User>*#
<h2>Your Patients</h2>
#*Showing #Model.Count() users*#
<p>#Html.ActionLink("Add New User", "Create")</p>
<table>
<tr>
<th>#Html.DisplayNameFor(model => model.UserID)</th>
<th>#Html.DisplayNameFor(model => model.CodeName)</th>
<th>#*#Html.DisplayNameFor(model => model.Device.Name)*#Device</th>
<th>#Html.DisplayNameFor(model => model.DeviceID)</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.UserID)</td>
<td>#Html.DisplayFor(modelItem => item.CodeName)</td>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
<td>#Html.DisplayFor(modelItem => item.DeviceID)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.UserID }) |
#Html.ActionLink("Details", "Details", new { id = item.UserID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.UserID })
</td>
</tr>
}
</table>
So what i REALLY need to know is that by using model properties from another project, what do i have to do differently. What am i currently doing wrong? what should i do so that the USER information and the DEVICE information can be show.
UPDATE
Thanks to Stephen Muecke, the solution to the issue of the index view not displaying the user db data was solved by changing the Action result in the index controller to:
public ActionResult Index()
{
var viewModel = db.Users.Select(u => new UserDeviceViewModel() { User = u, Device = u.Device }).ToList();
return View(viewModel);
}
UserDeviceViewModel contains a property named User not UserID (which is a property of User. Your loop needs to be
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(m => item.User.UserID)</td>
<td>#Html.DisplayFor(m => item.User.CodeName)</td>
Note you table headers wont work in this case.
Note also you are not really using a true 'view model'. A view model contains only those properties which you need for display/edit in a view (not just for dumping other models). Based on the view code you have shown it should be something like
public class UserDeviceViewModel
{
public int UserID { get; set; }
public string CodeName { get; set; }
public int DeviceID { get; set; }
public IList<SelectListItem> Devices { get; set; }
}
Although you view contains a reference to property Name (not sure what this is - perhaps DeviceName?) and your view does not use Devices (have you omitted some of the view?)
Remove the Ienumerable!
#model FaceToFaceWebsite.Models.UserDeviceViewModel
Look in your controller:
public ActionResult Index()
{
var viewModel = new
FaceToFaceWebsite.Models.UserDeviceViewModel();
return View(viewModel);
}
You are passing List (IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>) , while your view code expected to be FaceToFaceWebsite.Models.UserDeviceViewModel
Well you could pass the Correct type of ViewModel to your View:
In your View you have:
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
And in Controller you have:
var viewModel = new FaceToFaceWebsite.Models.UserDeviceViewModel();
return View(viewModel);
Try passing a List of your ViewModel:
var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();
return View(viewModel);
OR:
In your View change this:
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
To:
#model FaceToFaceWebsite.Models.UserDeviceViewModel
WHY are you getting that ERROR Message?
Because your ViewModel doesn't have UserId, CodeName etc.
BUT your User Class has UserId and CodeName
So In ViewModel you will access like this:
ViewModel.User.UserId and ViewModel.User.CodeName:
Like This:
<th>#Html.DisplayNameFor(model => model.User.UserID)</th>
<th>#Html.DisplayNameFor(model => model.User.CodeName)</th>
As per given code snippet, your View mapping and data model not sync up.
can you just follow below steps
clear all views.
first display only user level info
Verify user level info are you able to? next proceed further device level
put device level loop for your devices collection (make sure your Device collection model m not sure about your "SelectedListItem"
I'm trying to find the best correct way to do the following:
I have a ViewModel for a character editor called CharacterViewModel. This CharacterViewModel is populated with a Character object, a list of available ability scores a character can have, which are in another table.
I created an edit template for the drop down, and I'm trying to find a way to recuperate the list of edited abilities. I can't seem to get them back on the controller.
Here is the ViewModel code:
public class CharacterViewModel : DbContext
{
public Character Character { get; set; }
[UIHint("CharacterAbilityScores")]
public IEnumerable<CharacterAbilityScore> CharacterAbilityScores { get; set; }
public IEnumerable<SelectListItem> AbilityScoresSelectList { get; set; }
public IEnumerable<AbilityModifiersAndBonusSpellDTO> AbilityModifiersAndBonusSpellDTO { get; set; }
public CharacterViewModel()
: base("name=CharacterModels")
{
}
}
Here is the controller code for populating the ViewModel:
public async Task<ActionResult> Edit(int? id)
{
Character character = db.Characters.Find(id);
var model = new CharacterViewModel();
model.Character = character;
model.CharacterAbilityScores = character.CharacterAbilityScores;
// Creating the list of ability scores for the view
model.AbilityScoresSelectList = from amabs in db.AbilityModifiersAndBonusSpells
select new SelectListItem()
{
Value = amabs.score.ToString(),
Text = amabs.score.ToString()
};
return View(model);
}
The edit method signature in the controller (the CharacterAbilityScores property and the other complex ones are always empty on the return trip):
public async Task<ActionResult> Edit(CharacterViewModel characterViewModel)
Here is the related code in the edit view:
#model CampaignManager.Models.CharacterViewModel
#using (Html.BeginForm())
{
<div class="form-group">
#Html.EditorFor(model => model.CharacterAbilityScores, new { AbilityScoresSelectList = Model.AbilityScoresSelectList })
</div>
}
And finally, the EditorTemplate:
#model IEnumerable<CampaignManager.Entities.CharacterAbilityScore>
<table>
#foreach (var abilityScore in Model)
{
<tr>
<td>#abilityScore.Ability.Abbreviation</td>
<td>
#{
if (ViewData["AbilityScoresSelectList"] != null)
{
#Html.HiddenFor(z => abilityScore);
#Html.HiddenFor(z => abilityScore.AbilityId);
#Html.DropDownListFor(x => abilityScore.AbilityId, (IEnumerable<SelectListItem>)ViewData["AbilityScoresSelectList"], dropDownHTMLOptions);
}
}
</td>
<tr>
}
</table>
I've tried many different HiddenFor tricks, storing the whole collection, storing different id's... I'm a bit lost in there I'll admit. Maybe I'm doing this all wrong and I need another approach?
UPDATE
Here is the model for the CharacterAbilityScore entity:
public partial class CharacterAbilityScore
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CharacterId { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AbilityId { get; set; }
public int AbilityScore { get; set; }
public virtual Ability Ability { get; set; }
public virtual AbilityModifiersAndBonusSpell AbilityModifiersAndBonusSpell { get; set; }
public virtual Character Character { get; set; }
}
EditorFor() is designed to wok with collection where the EditorTemplate is the type in the collection (in your case you have made the EditorTemplate's model a collection (not the type) and are then giving each element a duplicate id attribute (invalid html) and duplicate name attributes (which cant be bound to a collection).
Change the template (Views/Shared/EditorTemplates/CharacterAbilityScore.cshtml) to:
#model CampaignManager.Entities.CharacterAbilityScore
<tr>
<td>#Html.DisplatFor(m => m.Ability.Abbreviation)</td>
<td>#Html.DropDownListFor(m => m.AbilityId, (IEnumerable<SelectListItem>)ViewData["AbilityScoresSelectList"])</td>
</tr>
and in the main view
#model CampaignManager.Models.CharacterViewModel
#using (Html.BeginForm())
{
<table>
#Html.EditorFor(model => model.CharacterAbilityScores, new { AbilityScoresSelectList = Model.AbilityScoresSelectList })
</table>
}
Side notes:
You have not posted the model for CharacterAbilityScore so a have
assumed it contains properties Abbreviation (for display only) and
AbilityId (associated with the dropdown).
You can not use #Html.HiddenFor() on a complex object (the value
will be the .ToString() output of the object) and having
#Html.HiddenFor() for the same property as the dropdown (and
located before #Html.DropDownListFor()) means that you will bind
to the hidden input on post back (i.e. the original value, not the
selected value from the dropdown)
I also recommend your view models do not derive from DbContext.
The purpose of a view model is to define the properties you want to
display/edit in the view
I am working on an ASP.NET MVC4 application. I have strongly-typed view of type Page where Page :
public class Page
{
public Page()
{
Date = DateTime.Now;
}
public int PageID { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public virtual List<Document> Documents { get; set; }
}
I render my view like that :
<tbody>
#for (int i = 0; i < Model.Page.Documents.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(m => Model.Page.Documents[i].Name)</td>
<td>#Html.DisplayFor(m => Model.Page.Documents[i].FileName)</td>
<td><img src="/Content/Images/delete.png", alt="Delete" /></td>
</tr>
}
</tbody>
What this produces is something like this:
So I wonder how exactly to apply any changes made to the Name property. What I can think of is just to make the whole table rendered in a #Html.BeginForm() and then on submit to iterate the documents and update them no matter if they were edited or not. I won't be working with large lists so this is an option but I'm trying to think of something better than that.
P.S
I am using Entity Framework 5 with Code First.
Add a boolean property "IsEdited" to your ViewModel.By default it's value is false.
Render a hidden field within the same where your "Name" textbox is.
<td>#Html.TextBoxFor(m => Model.Page.Documents[i].Name)#Html.HiddenFor(m=>Model.Page.Documents[i].IsEdited)</td>
Now "onchange" of "Name" textbox, set the value of the corresponding hidden field to "true".
Now on for post, filter out those models for which "IsEdited=false"
var editedDocuments=models.Where(x=>x.IsEdited)
So I am working on a web application and have encountered a new challenge that I have been stuck on for a few weeks. I will provide background information regarding my application and my data models, as well as the desired end results.
Basic App Info & Context of the Problem:
My app is intended to serve as a tool which landscape contractors can use to help with managing their business operations. My app will provide a place where they can create an account, then enter all of their customer information ("Client" data model), as well as the information about jobs they are doing for each of their clients ("Job" data model). There is a one-to-many relationship between clients and jobs (one client can have many jobs, but only one client for any given job).
Background:
I have two simple data models, "Client" and "Job". My app is being built in the ASP.net MVC3 framework. Using the Entity Framework scaffolding mechanism, I have created the basic CRUD views for each of these data models (create, read, update, delete). This is great for the most part (I can create new clients and jobs, and edit existing ones easy enough).
Business Problem:
I have the need to permit batch creating for new jobs in my application. I want my users (landscape contractors) to be able to enter all the mowing jobs they completed for the day. As such, I would like to have my view for this process populate a table with all active clients - each with a checkbox beside the row. Then I would like the user to be able to check boxes for each client they did a new job for (mowed their lawn), and submit the form (enter jobs done), where the result would be new jobs created for each of these clients.
Technical Problem:
My best guess is that I need to create a custom ViewModel in the controller and send it to the view, where the ViewModel would be a list of new jobs created based on currently active clients. Then in the view, the checkboxes could have the Client_ID (unique identifier for clients) as their value (this would be part of the ViewModel. When the user submits the form, the view would pass the ViewModel back to the controller. Then the controller could go through the list of ViewModel jobs and create a new job for each ViewModel job that has the checkbox checked.
So, my question is - how can I use the controller to do the following:
1.) Build a list of ViewModel jobs at runtime based on data from the Client list ("Client" data model)?
2.) How can I then pass that to the view?
3.) Once it comes back to the controller, how can I iterate through the list and modify my other data model accordingly (create new "Job" items)?
I created the custom ViewModel, which contains attributes from clients and jobs that I need to construct the new job entries (client name, client address, client id, job notes, crew, crew size, time on job, checkbox to indicate completion, etc.). Let's say the user has 50 clients that he mows lawns for which are active clients. I would want to construct a ViewModel with 50 rows (to represent each client that could possibly have their lawn mowed). Then I want to send that to the view and display it with checkboxes, which indicate whether the lawn was cut or not. When the model comes back to the view, the controller would then take the rows with checks in the checkboxes and create new "Job" rows in that table.
Thanks in advance for any assistance you can provide, I know this is probably an easy one for many of you. I am new to C# and MVC3.
UPDATE:
Here is my code -
Job Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace LawnTracker.Models
{
public class Job
{
[Key]
public int Job_ID { get; set; }
public int Client_ID { get; set; }
public int Account_ID { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string Notes { get; set; }
public string SvcRoute { get; set; }
public string Service { get; set; }
public string Date { get; set; }
public double SvcPriceOverride { get; set; }
public float SvcQty { get; set; }
public string UofM { get; set; }
public bool Invoiced { get; set; }
public string Crew { get; set; }
public int TechCount { get; set; }
public string TimeStart { get; set; }
public string TimeFinish { get; set; }
public double TimeSpent { get; set; }
public string Image1 { get; set; }
public string Image2 { get; set; }
public string Image3 { get; set; }
public double MulchUsed { get; set; }
public double FertUsed { get; set; }
public double HerbUsed { get; set; }
public string NextDue { get; set; }
}
}
My MowingJobViewModel model -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace LawnTracker.Models
{
public class MowingJobViewModel
{
public int Client_ID { get; set; }
public bool Completed { get; set; }
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
public double SvcPriceOverride { get; set; }
public string UofM { get; set; }
public int SvcQty { get; set; }
public string Notes { get; set; }
public string Date { get; set; }
public string Crew { get; set; }
public int TechCount { get; set; }
public string SvcRoute { get; set; }
public string Schedule { get; set; }
}
}
And my JobController -
// GET: /Job/CreateMowing
public ActionResult CreateMowing(string route = "", string sched = "")
{
List<SelectListItem> listItems = new List<SelectListItem>();
listItems.Add(new SelectListItem()
{
Value = "Lump Sum",
Text = "Lump Sum"
});
listItems.Add(new SelectListItem()
{
Value = "Hours",
Text = "Hours"
});
ViewBag.Units = new SelectList(listItems, "Value", "Text");
ViewBag.Routes = db.Clients.Select(r => r.SvcRoute).Distinct();
ViewBag.Sched = db.Clients.Select(r => r.MowSched).Distinct();
var model = from r in db.Clients
orderby r.SvcRoute
where (r.Mowing == true) &&
(r.Status == "Active") &&
(r.SvcRoute == route || (route == "")) &&
(r.MowSched == sched || (sched == ""))
select r;
if (model.Count() > 0)
{
ViewBag.total = model.Select(r => r.MowPrice).Sum();
}
else
{
ViewBag.total = 0.00;
}
/* Build a list of MowingJobViewModel objects based on the above defined list of clients
* who are subscribed to mowing and active. This will enable batch entry for new jobs done.
* This list of MowingJobViewModel objects will be sent to the client after a HTTP GET
* request for the CreateMowing view. The user will be able to check boxes associated
* with each client in the client list. When the form is submitted, the controller
* receives the model back with the updated information (completed, notes, etc.) about
* each job. Then the controller must update the jobs table, adding the new jobs based on
* the view model returned from the view / client.
*
*/
//Create a new list of MowingJobViewModel objects
IEnumerable<MowingJobViewModel> mjList = new List<MowingJobViewModel>();
//iterate through the list of clients built from earlier (in model)...
foreach (var item in model)
{
//create new MowingJobViewModel object MJ and add it to the list
mjList.Add(new MowingJobViewModel()
{
Client_ID = item.Client_ID,
Completed = false,
Name = (item.FirstName + " " + item.LastName),
Street = item.Address1,
City = item.City,
SvcPriceOverride = item.MowPrice,
UofM = "Lump Sum",
SvcQty = 1,
Notes = "",
Date = "",
Crew = "",
TechCount = 2,
SvcRoute = item.SvcRoute,
Schedule = item.MowSched,
});
}
return View(mjList);
}
**I don't have my view ("CreateMowing.cshtml") worked out correctly, but here is what I have-**
#model IEnumerable<LawnTracker.Models.MowingJobViewModel>
#{
ViewBag.Title = "Enter Mowing Jobs";
}
<h2>Enter Mowing Jobs</h2>
<div style="float: left; clear:both; width: 100%;">
<b>Total Jobs: #Html.Encode(Model.Count())</b><br />
<b>Total Revenue: $#Html.Encode(ViewBag.total)</b><br /><br />
</div>
<p></p>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<div style="float: right; clear:both; width: 100%;">
#using (Html.BeginForm("CreateMowing", "Job", FormMethod.Get))
{
<table>
<tr>
<th></th>
<th>
Route
</th>
<th>Schedule</th>
<th></th>
</tr>
<tr>
<td>
Show:
</td>
<td>
#Html.DropDownList("route", new SelectList(ViewBag.Routes), "--ALL--")
</td>
<td>
#Html.DropDownList("sched", new SelectList(ViewBag.Sched), "--ALL--")
</td>
<td>
<input type="submit" value="Filter" />
</td>
</tr>
</table><br /><br />
}
</div>
<table>
<tr>
<th>
Completed
</th>
<th>
First Name
</th>
<th>
Last Name
</th>
<th>
Street
</th>
<th>
City
</th>
<th>
Service Route
</th>
<th>
Price
</th>
<th>
Units
</th>
<th>
Qty
</th>
<th>
Notes
</th>
<th>
Schedule
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
<input type="checkbox" name="invoiced" value="#item.Client_ID" >
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Address1)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.SvcRoute)
</td>
<td>
#Html.DisplayFor(modelItem => item.MowPrice)
</td>
<td>
</td>
<td>
</td>
<td>
</td>
<td>
#Html.DisplayFor(modelItem => item.MowSched)
</td>
</tr>
}
</table>
<div>
<br />
#Html.ActionLink("Back to List", "Index")
</div>
You've created a view-model that contains the display attributes / data entry fields for a particular client when creating a job. Since you didn't give it a name, I'll call it NewJobEntry. As you guessed, you now need a view-model that has a property of type List<NewJobEntry> to represent a variable number of new jobs that can be created at once.
Your controller will populate the list, adding a NewJobEntry instance for each client.
Your view, when provided the model containing the list, can enumerate the list and generate a row for each NewJobEntry.
Once the data is posted back to your controller, the default ASP.NET MVC binder can handle lists but there are some quirks. The default binder will automatically hydrate objects in a list so long as the field names have sequential indices. To ensure these indices are correctly generated, you will want to bind using lambda expressions in your view that have a sequential index:
Html.CheckboxFor(model => model.JobList[i].IsSelected)
You might consider using a for loop instead of a foreach loop when enumerating your list.
You also have to be careful with checkboxes, because an unchecked checkbox does not actually appear in the post data sent to the controller. This will break binding, because the indices sent in the post data may no longer be sequential (there will be gaps wherever there are unchecked checkboxes). Therefore, I would recommend you include a hidden field for the client Id. This way, the post data will always contain at least one field with the appropriate sequential index for each list entry.
Html.HiddenFor(model => model.JobList[i].ClientId)
Now your post data will be something along the lines of:
JobList_0_IsSelected=true
JobList_0_ClientId=12345
JobList_1_ClientId=12346
JobList_2_IsSelected=true
JobList_2_ClientId=12347
The default binder will handle this and rebuild your list. Note that without the hidden field for client Id, index 1 would be missing, and the default binder would not correctly rebuild the list.
Hope that helps!