I am trying to learn ASP.NET MVC (C#), and am currently tackling ViewModels. I understand the purpose of ViewModels, and I can get data through to the screen using them, but am having difficulties with understanding their relationship with - another new element for me - interfaces.
I want to achieve the following View:
You can see I have a simple initial insert form for adding a new staff member, with a dropdown for salutations. Following this, there is a second form for editing where I iterate through available staff members, placing their values into relevant input fields, where the salutation dropdown defaults to their relative salutation.
I have two Domain Models / tables, Prm_Staff and Prm_Salutation, which I am accessing (wrong word, I think) via the ViewModel Staff_VM:
public class Staff_VM
{
public int StaffID { get; set; }
public int SalutationID { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public bool Active { get; set; }
public List<Prm_Salutation> AvailableSalutations { get; set; }
}
public class StaffMembers
{
public Staff_VM StaffVm;
public IEnumerable<Staff_VM> ListStaffVms;
}
In my controller:
var activeSalts = (from a in db.Prm_Salutations
where a.Active == true
orderby a.Desc ascending
select a).ToList();
var model = new StaffMembers
{
ListStaffVms = (from a in db.Prm_Staffs
where a.Active == true
orderby a.LName ascending
select new Staff_VM
{
StaffID = a.Prm_StaffID,
SalutationID = a.SalutationID,
FName = a.FName,
LName = a.LName,
Active = a.Active,
AvailableSalutations = activeSalts
}),
StaffVm = new Staff_VM()
{
AvailableSalutations = activeSalts
},
};
return View("StaffMembers", model);
In the View, I refer to that model #model November.ViewModels.StaffMembers:
#*Record New Staff Member*#
<tr>
<td>
#Html.DropDownListFor(
model => model.StaffVm.SalutationID,
Model.StaffVm.AvailableSalutations.Select(option => new SelectListItem
{
Text = option.Desc.ToString(),
Value = option.Prm_SalutationID.ToString()
}
),
"Choose...")
</td>
<td>#Html.EditorFor(model => model.StaffVm.FName)</td>
<td>#Html.EditorFor(model => model.StaffVm.LName)</td>
<td>#Html.EditorFor(model => model.StaffVm.Active)</td>
</tr>
#*Update Existing Staff Members*#
#foreach (var staff in Model.ListStaffVms)
{
<tr>
<td>#Html.HiddenFor(model => staff.StaffID)#Html.ValueFor(model => staff.StaffID) </td>
<td>
#Html.DropDownListFor(
model => staff.SalutationID, staff.AvailableSalutations.Select(option => new SelectListItem
{
Selected = (option.Prm_SalutationID == staff.SalutationID),
Text = option.Desc.ToString(),
Value = option.Prm_SalutationID.ToString()
}
),
"Choose...")
</td>
<td>#Html.EditorFor(model => staff.FName)</td>
<td>#Html.EditorFor(model => staff.LName)</td>
<td>#Html.EditorFor(model => staff.Active)</td>
<td>Delete</td>
</tr>
}
ActionResult:
public ActionResult UpdateStaff(StaffMembers list)
{
if (ModelState.IsValid)
{
foreach (var staffVm in list.ListStaffVms)
{
Prm_Staff staff = db.Prm_Staffs.Find(staffVm.StaffID);
staff.SalutationID = staffVm.SalutationID;
staff.FName = staffVm.FName;
staff.LName = staffVm.LName;
staff.Active = staffVm.Active;
}
db.SaveChanges();
ViewBag.rtrn = "Successfully Updated.";
return RedirectToAction("Parameters", new { param = "Staff Members", rtrn = ViewBag.rtrn });
}
else
{
ViewBag.rtrn = "Failed ! Please try again.";
return RedirectToAction("Parameters", new { param = "Staff Members", rtrn = ViewBag.rtrn });
}
}
EDIT: Updated to show most recent changes
I think you should consider change your ViewModel. Also do something like below:
ViewModel
public class Staff_VM
{
public int ID { get; set; }
public int SalutationID { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public bool Active { get; set; }
}
public class MyViewModel
{
public Staff_VM StaffVm { get; set; }
public List<Staff_VM> ListStaffVms { get; set; }
public List<Prm_Salutation> AvailableSalutations { get; set; }
}
Add_Update_Staff Action
[HttpGet]
public ActionResult Add_Update_Staff()
{
var model = new MyViewModel
{
ListStaffVms = (from a in db.Prm_Staffs
where a.Active == true
orderby a.LName ascending
select new Staff_VM
{
ID = a.Id,
SalutationID = a.SalutationID,
FName = a.FName,
LName = a.LName,
Active = a.Active
}),
AvailableSalutations = (from p in db.Prm_Salutations
where a.Active == true
orderby p.Desc ascending
select p).ToList()
};
return View(model);
}
Update Staff Post
[HttpPost]
public ActionResult Add_Update_Staff(MyViewModel model, string buttonType)
{
if (buttonType == "Insert")
{
if (ModelState.IsValid)
{
//save a new staff info
return RedirectToAction("Index", "Home");
}
}
if (buttonType == "Update")
{
foreach (var staffVm in model.ListStaffVms)
{
// update each record here
}
return RedirectToAction("Index", "Home");
}
model.AvailableSalutations = (from p in db.Prm_Salutations
orderby p.Desc ascending
select p).ToList();
return View(model);
}
View
You may need to add validation for insert and update staff info
#using (Html.BeginForm("Add_Update_Staff", "Staff"))
{
<tr>
<td>
#Html.DropDownListFor(
model => model.StaffVm.SalutationID, Model.AvailableSalutations.Select(option => new SelectListItem
{
Text = option.Desc.ToString(),
Value = option.Prm_SalutationID.ToString()
}
), "Choose...")
</td>
<td>#Html.EditorFor(model => model.StaffVm.FName)</td>
<td>#Html.EditorFor(model => model.StaffVm.LName)</td>
<td>#Html.EditorFor(model => model.StaffVm.Active)</td>
</tr>
<input type="submit" value="Insert" name="buttonType" />
for (int i = 0; i < Model.ListStaffVms.Count(); i++)
{
<tr>
<td>#Html.HiddenFor(m => m.ListStaffVms[i].ID)#Html.ValueFor(m => m.ListStaffVms[i].ID) </td>
<td>
#Html.DropDownListFor(
m => m.ListStaffVms[i].SalutationID, Model.AvailableSalutations.Select(option => new SelectListItem
{
Selected = (option.Prm_SalutationID == Model.ListStaffVms[i].SalutationID),
Text = option.Desc.ToString(),
Value = option.Prm_SalutationID.ToString()
}), "Choose...")
</td>
<td>#Html.EditorFor(model => model.ListStaffVms[i].FName)</td>
<td>#Html.EditorFor(model => model.ListStaffVms[i].LName)</td>
<td>#Html.EditorFor(model => model.ListStaffVms[i].Active)</td>
<td>Delete</td>
<hr />
</tr>
}
<input type="submit" value="Update" name="buttonType" />
}
Related
I have lots of models with same basic structure in my MVC project. So, I created a master class like below.
public class MasterTemplate
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Description { get; set; }
public DateTime? UpdatedOn { get; set; }
public string UpdatedBy { get; set; }
}
And I created all my model classes like below.
public class Industry : MasterTemplate
{
}
public class Caste : MasterTemplate
{
}
public class Gender : MasterTemplate
{
}
public class Qualification : MasterTemplate
{
}
public class BloodGroup: MasterTemplate
{
}
There are many more like this. Following is my code for IndustryController.
public class IndustryController : Controller
{
private ApplicationDbContext _context { get; set; }
private string UserId { get; set; }
public IndustryController()
{
_context = new ApplicationDbContext();
UserId = System.Web.HttpContext.Current.User.Identity.GetUserId();
}
public ActionResult Index(int id = 0)
{
Industry data = new Industry();
if (id > 0)
data = _context.Industries.SingleOrDefault(c => c.Id == id);
if (data == null)
data = new Industry();
return View("Industry", data);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(Industry data)
{
if (!ModelState.IsValid)
return View("Industry", data);
var record = _context.Industries.Where(c => c.Description.Trim().ToLower() == data.Description.Trim().ToLower() && c.Id != data.Id);
if (record.Count() > 0)
{
ModelState.AddModelError("Duplicate Industry", "Industry already exist");
return View("Industry", data);
}
Industry cm = new Industry();
if (data.Id >= 1)
{
cm = _context.Industries.SingleOrDefault(c => c.Id == data.Id);
cm.Description = data.Description;
cm.UpdatedOn = DateTime.Now;
cm.UpdatedBy = UserId;
}
else
{
cm = data;
_context.Industries.Add(cm);
}
_context.SaveChanges();
return RedirectToAction("Index", new { id = 0 });
}
And following is my code for IndustryView
#model Industry
#{
ViewBag.Title = "Industries";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h3>Industries Management</h3>
<div class="row">
<div class="col-md-4">
#using (#Html.BeginForm("Save", "Industry"))
{
#Html.ValidationSummary("Please correct the following")
#Html.HiddenFor(m => m.Id)
<div class="form-group">
<div>
#Html.LabelFor(m => m.Description)
#Html.TextBoxFor(m => m.Description, new { #class = "form-control", autocomplete = "off" })
#Html.ValidationMessageFor(m => m.Description)
</div>
</div>
#Html.AntiForgeryToken()
<button type="submit" class="btn btn-primary btn-sm">Save</button>
}
</div>
<div class="col-md-8">
<table class="table table-sm" id="mydata">
<thead>
<tr>
<th>
Industries
</th>
<th>
</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
#section scripts
{
#Scripts.Render("~/bundles/jqueryval")
<script>
$(document).ready(function () {
$("#mydata").DataTable({
ajax: {
url: "/api/get/industries",
dataSrc: ""
},
columns: [
{
data: "description"
},
{
data: "id",
render: function (data) {
var url = '#Url.Action("Index", "Industry", new { id = "__data__" })';
return 'Edit';
}
}
]
});
});
</script>
}
Now my problem is, code for controller and views for all the models in my project is almost similar. It is as above. So, I wanted to generalize them and create a single controller and view which can be used for all my other models. I am new to generics, tried the following code, but still not able to figure out the way forward. It is so confusing for me.
public interface IMaster
{
int Id { get; set; }
string Description { get; set; }
}
public class GenericController : Controller
{
private ApplicationDbContext _context { get; set; }
private string UserId { get; set; }
public GenericController()
{
_context = new ApplicationDbContext();
UserId = System.Web.HttpContext.Current.User.Identity.GetUserId();
}
public ActionResult Index(int id = 0)
{
IMaster data = null;
if (id > 0)
data = _context.Industries.SingleOrDefault(c => c.Id == id);
if (data == null)
data = new Industry();
return View("Generic", data);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(IMaster data)
{
if (!ModelState.IsValid)
return View("Generic", data);
var record = _context.Industries.Where(c => c.Description.Trim().ToLower() == data.Description.Trim().ToLower() && c.Id != data.Id);
if (record.Count() > 0)
{
ModelState.AddModelError("Duplicate Industry", "Industry already exist");
return View("Generic", data);
}
Industry cm = new Industry();
if (data.Id >= 1)
{
cm = _context.Industries.SingleOrDefault(c => c.Id == data.Id);
cm.Description = data.Description;
cm.UpdatedOn = DateTime.Now;
cm.UpdatedBy = UserId;
}
else
{
cm.Id = data.Id;
cm.Description = data.Description;
_context.Industries.Add(cm);
}
_context.SaveChanges();
return RedirectToAction("Index", new { id = 0 });
}
}
Can somebody guide me in right direction, need to create a generic controller and view for all the similar models in my project.
I have not run it, but I am pretty confident, that this should do the trick! Actually the only truly generic part is the controller. The other stuff is just the usual polymorphism. And thank you for the inspiration. It was fun thinking about such a solution. Maybe I'll build something similar in the future.
A word of caution: You will bind the name of your controllers to the name of each Model. Just be aware of this! There is a naming schema that must be kept or you break it.public class [ModelName]Controller : MasterController<ModelName>{}The ajax endpoints will end with the value of [PluralName](Read on in the View to know, what I mean.)
You will need an additional Property in the MasterTemplate. Ideally make it abstract, so you won't forget to implement it in the derived classes. This is for the Plural Name in the View's header and the ajax call in the View.
public abstract class MasterTemplate
{
[Key]
public int Id { get; set; }
public abstract string PluralName {get;}
[Required]
[StringLength(255)]
public string Description { get; set; }
public DateTime? UpdatedOn { get; set; }
public string UpdatedBy { get; set; }
}
Industry will then look like this
public class Industry: MasterTemplate
{
public override string PluralName => "Industries"
}
Make a truly generic Controller and derive all other Controllers from it like
public class IndustryController : MasterController<Industry>
{
//done everthing else is in the master :)
}
And here the generic MasterController<T>.
public class MasterController<T> : Controller where T : MasterTemplate, new()
{
private ApplicationDbContext _context { get; set; }
private string UserId { get; set; }
public MasterController()
{
_context = new ApplicationDbContext();
UserId = System.Web.HttpContext.Current.User.Identity.GetUserId();
}
public ActionResult Index(int id = 0)
{
T data = (id > 0)
? data = _context.Set<T>().SingleOrDefault(c => c.Id == id) ?? new T()
: new T();
return View("View", data);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(T data)
{
if (!ModelState.IsValid)
return View("View", data);
var record = _context.Set<T>().Where(c => c.Description.Trim().ToLowerInvariant() == data.Description.Trim().ToLowerInvariant() && c.Id != data.Id);
if (record.Count() > 0)
{
ModelState.AddModelError($"Duplicate {typeof(T).Name}", $"{typeof(T).Name} already exist");
return View("View", data);
}
if (data.Id >= 1)
{
T cm = _context.Set<T>().SingleOrDefault(c => c.Id == data.Id);
cm.Description = data.Description;
cm.UpdatedOn = DateTime.Now;
cm.UpdatedBy = UserId;
}
else
{
_context.Set<T>().Add(data);
}
_context.SaveChanges();
return RedirectToAction("Index", new { id = 0 });
}
Name the View "View" (or just the same, as you call it in the MasterController) and place it in the Shared Folder, for every controller to find it there.
#model MasterTemplate
#{
string name = Model.GetType().Name;
ViewBag.Title = name;
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h3>#Model.PluralName Management</h3>
<div class="row">
<div class="col-md-4">
#using (#Html.BeginForm("Save", name))
{
#Html.ValidationSummary("Please correct the following")
#Html.HiddenFor(m => m.Id)
<div class="form-group">
<div>
#Html.LabelFor(m => m.Description)
#Html.TextBoxFor(m => m.Description, new { #class = "form-control", autocomplete = "off" })
#Html.ValidationMessageFor(m => m.Description, $"{name} is required.", new { #class = "text-danger" })
</div>
</div>
#Html.AntiForgeryToken()
<button type="submit" class="btn btn-primary btn-sm">Save</button>
}
</div>
<div class="col-md-8">
<table class="table table-sm" id="mydata">
<thead>
<tr>
<th>
#(name)
</th>
<th>
</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
#section scripts
{
#Scripts.Render("~/bundles/jqueryval")
<script>
$(document).ready(function () {
$("#mydata").DataTable({
ajax: {
url: "/api/get/#(Model.PluralName)",
dataSrc: ""
},
columns: [
{
data: "description"
},
{
data: "id",
render: function (data) {
var url = '#Url.Action("Index", "#(name)", new { id = "__data__" })';
return 'Edit';
}
}
]
});
});
</script>
}
My GET method of controller construct and fill Model, which including Dictionary<int, MyClass>, and transmit that to View. But after, POST controller method get not null model with empty Dictionary.
Model:
public class CheckBoxItem
{
public string Name { get; set; }
public double Data { get; set; }
public bool Selected { get; set; }
}
public class CreateNewEventModel
{
[Required(ErrorMessage = "Error text")]
[Display(Name = "Header name")]
public string EventName { get; set; }
public Dictionary<int, CheckBoxItem> CheckBoxDataItems { get; set; }
public CreateNewEventModel()
{
CheckBoxDataItems = new Dictionary<int, CheckBoxItem>();
}
}
Controller:
public ActionResult CreateEvent()
{
CreateNewEventModel model = new CreateNewEventModel();
// FILL MODEL
foreach (var user in db.UsersInfo.ToList())
{
model.CheckBoxDataItems.Add(user.Id, new CheckBoxItem()
{
Name = user.Name,
Data = 0,
Selected = false
});
}
// THERE IS FULL MODEL
return View(model);
}
[HttpPost]
public ActionResult CreateEvent(CreateNewEventModel model)
{
// THERE IS model.Event name include text
// BUT model.CheckBoxDataItems is empty
if (ModelState.IsValid)
{
...
return View(model);
}
return View(model);
}
View:
#model HLyaa.Models.CreateNewEventModel
#{
ViewBag.Title = "Create Event";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Создание события</h2>
#if (Model.CheckBoxDataItems.Count() != 0)
{
using (Html.BeginForm("CreateEvent", "Events", FormMethod.Post))
{
#Html.ValidationSummary()
<div>
#Html.LabelFor(model => model.EventName)
<div>
#Html.EditorFor(model => model.EventName)
</div>
</div>
<table>
#foreach (var kvpair in Model.CheckBoxDataItems)
{
<tr>
<td>
#Html.CheckBoxFor(model => model.CheckBoxDataItems[kvpair.Key].Selected)
</td>
<td>
#Html.DisplayFor(model => model.CheckBoxDataItems[kvpair.Key].Name)
#Html.HiddenFor(model => model.CheckBoxDataItems[kvpair.Key].Selected)
#Html.HiddenFor(model => model.CheckBoxDataItems[kvpair.Key].Name)
</td>
<td>
#Html.TextBoxFor(model => model.CheckBoxDataItems[kvpair.Key].Data, new { #type = "number" })
</td>
</tr>
}
</table>
<br />
<input type="submit" value="Next" />
}
}
How I can transmit data inside dictionary from View to Controller?
Dictionary no, List/Array yes, but you will have to make some modifications.
Modify models
public class CheckBoxItem {
public int UserId { get; set; }
public string Name { get; set; }
public double Data { get; set; }
public bool Selected { get; set; }
}
public class CreateNewEventModel {
[Required(ErrorMessage = "Error text")]
[Display(Name = "Header name")]
public string EventName { get; set; }
public List<CheckBoxItem> CheckBoxDataItems { get; set; }
public CreateNewEventModel() {
CheckBoxDataItems = new List<CheckBoxItem>();
}
}
Modify GET method CreateEvent
public ActionResult CreateEvent() {
var model = new CreateNewEventModel();
//...FILL MODEL
foreach (var user in db.UsersInfo.ToList()) {
model.CheckBoxDataItems.Add(new CheckBoxItem() {
UserId = user.Id,
Name = user.Name,
Data = 0,
Selected = false
});
}
// THERE IS FULL MODEL
return View(model);
}
Update View
<table>
#for (var i = 0; i < Model.CheckBoxDataItems.Count; i++) {
<tr>
<td>
#Html.CheckBoxFor(model => model.CheckBoxDataItems[i].Selected)
</td>
<td>
#Html.DisplayFor(model => model.CheckBoxDataItems[i].Name)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].UserId)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].Selected)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].Name)
</td>
<td>
#Html.TextBoxFor(model => model.CheckBoxDataItems[i].Data, new { #type = "number" })
</td>
</tr>
}
</table>
CheckBoxDataItems should be populated now when you post it to controller
As I answered in this post, you need to call "ToArray()[*]" on your dictionary before accessing its key and value so you can specify an index that's used by the ASP model binder to send your data back to the controller. ;)
After reading this tutorial http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application I have created some models, controllers and views.
The recipes are showing just fine in the view, but I can't get the RecipeLines to show.
RecipeModel
public class RecipeModel
{
[Key]
public int RecipeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public virtual ICollection<RecipeLine> RecipeLines { get; set; }
}
RecipeLine
public class RecipeLine
{
[Key]
public int RecipeLineId { get; set; }
public int RecipeId { get; set; }
public double Quantity { get; set; }
public UnitOfMeasureModel UnitOfMeasure { get; set; }
public IngredientModel Ingredient { get; set; }
}
RecipeViewModel
public class RecipeViewModel
{
public IEnumerable<RecipeModel> RecipeModels { get; set; }
public IEnumerable<RecipeLine> RecipeLines { get; set; }
}
Recipecontroller
public class RecipeController : Controller
{
private RecipeApplicationDb db = new RecipeApplicationDb();
[HttpGet]
public ActionResult Index(int? id)
{
var viewModel = new RecipeViewModel();
viewModel.RecipeModels = db.Recipes
//.Include(i => i.Name)
.Include(i => i.RecipeLines);
if (id != null)
{
ViewBag.RecipeId = id.Value;
viewModel.RecipeLines = viewModel.RecipeModels.Where(i => i.RecipeId == id.Value).Single().RecipeLines;
}
return View(viewModel);
}
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
RecipeModel recipeModel = db.Recipes.Find(id);
if (recipeModel == null)
{
return HttpNotFound();
}
return View(recipeModel);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
And the view
#model RecipeApplication.Models.RecipeViewModel
#{
ViewBag.Title = "Recepten";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
Naam
</th>
<th>
Omschrijving
</th>
<th>
Afbeelding
</th>
</tr>
#foreach (var item in Model.RecipeModels) {
string selectedRow = "";
if(item.RecipeId == ViewBag.RecipeId)
{
selectedRow = "success";
}
<tr class="#selectedRow" valign="top">
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#if (item.ImageUrl != null)
{
#Html.DisplayFor(modelItem => item.ImageUrl)
}
</td>
<td>
#Html.ActionLink("Select", "Index", new { id = item.RecipeId}) |
#Html.ActionLink("Edit", "Edit", new { id=item.RecipeId }) |
#Html.ActionLink("Details", "Details", new { id=item.RecipeId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.RecipeId })
</td>
</tr>
}
</table>
#if (Model.RecipeLines != null)
{
foreach (var item in Model.RecipeLines)
{
string selectedRow = "";
if (item.RecipeId == ViewBag.id)
{
<p>#item.Quantity #item.UnitOfMeasure #item.Ingredient</p>
}
}
}
When selecting the recipe, the line does get a proper color, and I see an id-value in the URL-string.
If someone could help with this one, that would be awesome.
You're comparing item.RecipeId to ViewBag.id, which doesn't exist. The ViewBag member you set in the controller action was ViewBag.RecipeId.
However, you don't need this conditional at all. All of the recipe lines are already for that recipe id, because you specifically set only those recipe items in Model.RecipeLines.
//change your controller action
[HttpGet]
public ActionResult Index(int? id)
{
if(id == null) return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var model = new RecipeViewModel();
var data = db.RecipeModel.Include(i => i.RecipeLines)
.Where(x=>x.RecipeId == id)
.ToList();
model.RecipeModels = data;
return View(model);
}
//change your viewModel
public class RecipeViewModel
{
public IEnumerable<RecipeModel> RecipeModels { get; set; }
}
//this is in the view
#if (Model.RecipeLines != null)
{
foreach (var item in Model.RecipeModels.RecipeLines)
{
<p>
#item.Quantity
#item.UnitOfMeasure
#item.Ingredient
</p>
}
}
Model
public class AllControls
{
public List<Group> getChkItems { get; set; }
public bool chk { get; set; }
}
public class Group
{
public int ID { get; set; }
public string Name { get; set; }
}
Controller:
[HttpGet]
public ActionResult Index()
{
List<Group> li = new List<Group>()
{
new Group() { ID = 1, Name = "C#" },
new Group() { ID = 1, Name = "Asp.NET" },
new Group() { ID = 1, Name = "SQL" }
};
AllControls model = new AllControls();
model.getChkItems = li;
return View(model);
}
[HttpPost]
public ActionResult Index(AllControls e)
{
return View(e);
}
View:
#using (Html.BeginForm())
{
foreach (var x in #Model.getChkItems)
{
#Html.CheckBoxFor(m => m.chk, new { value = #x.ID }) #x.Name
<br />
}
<input type="submit" value="Submit" id="btn" />
}
How can I get the selected checkbox value and text in the controller?
Here goes my solution. Let your model be as shown below.
public class CheckboxModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
public class MainModel
{
public List<CheckboxModel> CheckBoxes { get; set; }
}
And let your Controller GET Action be as shown below.
public ActionResult GetDatas()
{
MainModel model = new MainModel();
var list = new List<CheckboxModel>
{
new CheckboxModel{Id = 1, Name = "India", Checked = false},
new CheckboxModel{Id = 2, Name = "US", Checked = false},
new CheckboxModel{Id = 3, Name = "UK", Checked = false}
};
model.CheckBoxes = list;
return View(model);
}
And POST Action be as shown below.
[HttpPost]
public ActionResult PostDatas(MainModel model)
{
return View(model);
}
The View should be as shown below.
#model WebApplication1.Controllers.MainModel
#using (Html.BeginForm("PostDatas","Home"))
{
for (var i = 0; i < Model.CheckBoxes.Count; i++)
{
<table>
<tr>
<td>
#Html.HiddenFor(m => Model.CheckBoxes[i].Id)
#Html.HiddenFor(m => Model.CheckBoxes[i].Name)
#Html.CheckBoxFor(m => Model.CheckBoxes[i].Checked)
</td>
<td>
#Html.DisplayFor(m => Model.CheckBoxes[i].Name)
</td>
</tr>
</table>
}
<input id="submit" type="submit" value="submit" />
}
View will be rendered as shown below.
When you select India and US and click on submit button, you will get POST parameters as below.
I need a help of displaying the view of comodities, by excluding the duplicate, but adding quantity to the count
E.g.
Name ComodityModel count SellerName
Iphone 3gs 1 Neal
Iphone 4g 1 Jane
Iphone 3gs 1 Neal
Iphone 3gs 1 Jane
Output should be
Name ComodityModel count SellerName
Iphone 3gs 2 Neal
Iphone 4g 1 Jane
Iphone 3gs 1 Jane
I need to filter it by Comodity model and SellerName. I have database and model Comodity
public class Comodity
{
public int ID { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
[Required(ErrorMessage = "Date is required")]
//[DisplayFormat(DataFormatString = "{0:d}")]
[DataType(DataType.Date)]
public DateTime ? RegisterDate { get; set; }
[Required(ErrorMessage = "Comodity Model must be specified")]
public string ComodityModel { get; set; }
[Required(ErrorMessage = "Color must be specified")]
[StringLength(15)]
public string Color { get; set; }
[Required(ErrorMessage = "Price Required")]
[Range(1, 1000, ErrorMessage = "Price must be between $1 and $100")]
[DisplayFormat(DataFormatString = "{0:c}")]
public decimal Price { get; set; }
[Required(ErrorMessage = "Seller name must be specified")]
[StringLength(15)]
public string SellerName { get; set; }
public int CountCom { get; set; }
}
public class ComodityDBContext : DbContext
{
public DbSet<Comodity> Comodities { get; set; }
}
and Controller where I defined Actionresult MyView
public ActionResult MyView(DateTime? startDate, DateTime? endDate, string comModel, string searchString)
{
// if (string.IsNullOrEmpty(comModel))
// {
// return View(comodities);
// }
// else
// {
// return View(comodities.Where(x => x.ComodityModel == comModel));
// }
DateTime dtNow;
dtNow = DateTime.Today;
if (!startDate.HasValue)
{
startDate = new DateTime(dtNow.Year, dtNow.Month, 1);
endDate = startDate.Value.AddMonths(1).AddDays(-1);
}
if (startDate.HasValue && !endDate.HasValue)
{
endDate = (new DateTime(startDate.Value.Year, startDate.Value.Month, 1)).AddMonths(1).AddDays(-1);
}
ViewBag.startDate = startDate.Value.ToShortDateString();
ViewBag.endDate = endDate.Value.ToShortDateString();
var viewDate = from r in db.Comodities
where r.RegisterDate >= startDate.Value && r.RegisterDate <= endDate.Value == true
// orderby r.RegisterDate
select r.RegisterDate;
var SelectListName = new List<string>();
var SelectNameQry = from m in db.Comodities
select m.SellerName;
SelectListName.AddRange(SelectNameQry.Distinct());
ViewBag.searchString = new SelectList(SelectListName);
var comModelLst = new List<string>();
var comModelQry = from d in db.Comodities
orderby d.ComodityModel
select d.ComodityModel;
comModelLst.AddRange(comModelQry.Distinct());
ViewBag.comModel = new SelectList(comModelLst);
var comodities = from m in db.Comodities
select m;
IDictionary<string, IList<string>> dict = new Dictionary<string, IList<string>>();
var queryC = from c in db.Comodities
group c by c.ComodityModel into g
where g.Count() > 1
select new { ComodityModel = g.Key, CCount = g.Count() };
foreach (var item in queryC)
{ // comodities = comodities.Where(item => item.Name && item => item.CCount);
//View("", item.ComodityModel, item.CCount);
// ViewBag(item.ComodityModel, item.CCount);
String key = item.ComodityModel;
if (dict.ContainsKey(key))
{
// add the class name into an existing "string" collection
dict[key].Add(item.ComodityModel);
}
else
{
// instantiate a new "string" collection and add the class name.
dict[key] = new List<string> { item.ComodityModel };
}
int maxCourseCount = 0;
foreach (var k in dict.Keys)
{
int valueCount = dict[k].Count;
if (valueCount > maxCourseCount)
maxCourseCount = valueCount;
}
}
if (!String.IsNullOrEmpty(searchString))
{
comodities = comodities.Where(s => s.SellerName.Contains(searchString));
}
if (startDate.HasValue && endDate.HasValue)
{
comodities = comodities.Where(r => r.RegisterDate >= startDate.Value && r.RegisterDate <= endDate.Value);
}
if (string.IsNullOrEmpty(comModel))
{
return View(comodities);
}
else
{
return View(comodities.Where(x => x.ComodityModel == comModel));
}
}
I am new in MVC and have no idea how to count and make view. Please, help. Also, is there some suggestion about date picking. It is showing incorrectly. I assume that it only comparing the day, not whole day-month-year. Thanks in advance
Finally MyView
#model IEnumerable<SaleCenter.Models.Comodity>
#{
ViewBag.Title = "total";
}
<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>
<h2>total</h2>
<p>
#using (Html.BeginForm()){
<p> startdate:#Html.TextBox("startDate", null, new { #class = "datePicker" })
enddate : #Html.TextBox("endDate", null, new { #class = "datePicker" }) </p>
<p> model: #Html.DropDownList("comModel", "All")
seller: #Html.DropDownList("SearchString", "All")
<input type="submit" value="Total" /></p>
}
</p>
<table>
<tr>
<th>
product name
</th>
<th>
Model
</th>
<th>
seller
</th>
<th>
quantity
</th>
<th>
graphic
</th>
</tr>
#foreach (var item in Model)
{
int w = (int)(2000 * item.CountCom / 100);
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.ComodityModel)
</td>
<td>
#Html.DisplayFor(modelItem => item.SellerName)
</td>
<td>
#Html.DisplayFor(modelItem => item.CountCom)
</td>
<td>
<img src="#Url.Content("~/Content/Images/graph.gif")" width="#w.ToString()px" height="6px" alt="" />
</td>
</tr>
}
</table>
<script type="text/javascript">
$(document).ready(function () {
$('.datePicker').datepicker({ firstDay: 1, dateFormat: 'dd.mm.yy', showOn: 'button',
buttonImage: '/Content/images/calendar.gif',
duration: 0
});
});
</script>
For showing the commodity and it's count in the view. Quick way it to create a anonymous object from your comodities by grouping with name and get it's count. Return this anonymous object as the model to the view.
Better approach is to create ViewModel specific to this purpose, so that you could create a tightly bound view.
Here is a method to plop into your ComodityController.cs file:
private int getComodityCount(string param_Name, string param_ComodityModel, string param_SellerName)
{
return db.Comodity.Where(x => x.Name == param_Name && x.ComodityModel == param_ComodityModel && x.SellerName == param_SellerName).Count();
}
Now, to get the number of duplicates within the Collection of Comodity you call:
int count = getComodityCount("iPhone","3gs","Neal");
What's more, if you change the privacy setting from private to public, you can even call the method from your view like this:
Here is the method changed to public:
public int getComodityCount(string param_Name, string param_ComodityModel, string param_SellerName)
{
return db.Comodity.Where(x => x.Name == param_Name && x.ComodityModel == param_ComodityModel && x.SellerName == param_SellerName).Count();
}
and here is the code for your View:
#{
int count = new ComodityController().getComodityCount("iPhone","3gs","Neal");
}
I hope this helps. The Where(linq) method is REALLY handy for database querying.