How can i use Dictionary in MVC4 ViewModel,Controller, View? - asp.net-mvc-4

I have a ViewModel like below,
public class EnrollPlanPriceLevelViewModel
{
public EnrollPlanPriceLevel EnrollPlanPriceLevels { get; set; }
public Dictionary<PriceLevel,decimal> Prices { get; set; }
}
Code in my View, but I am unable to create View for Prices. Please help me someone!
#{
int i = 0;
foreach (FCA.Model.PriceLevel p in ViewBag.PriceLevelID)
{
i = i + 1;
<div class="span4">
#Html.TextBoxFor(model => model.Prices[p], new { #placeholder = "Price" })
</div>
}
}
I want to call Dictionary object values in Controller how?

ViewModel :
public class EnrollPlanPriceLevelViewModel
{
public EnrollPlanPriceLevel EnrollPlanPriceLevels { get; set; }
public Dictionary<string,decimal> Prices { get; set; }
}
My Controller's Get method should intitialize 'Key' and Values like this. so that you can loop through in View for each KeyValue pair.
Controller's GET method:
public ActionResult Create()
{
var model = new EnrollPlanPriceLevelViewModel();
model.Prices = new Dictionary<string, decimal>();
foreach (PriceLevel p in db.PriceLevelRepository.GetAll().ToList())
{
model.Prices.Add(p.PriceLevelID.ToString(), 0);
}
return View(model);
}
View using Dictionary like this:
<div class="span12">
#{
foreach (var kvpair in Model.Prices)
{
<div class="span4">
#Html.TextBoxFor(model => model.Prices[kvpair.Key], new { #placeholder = "Price" })
#Html.ValidationMessageFor(model => model.Prices[kvpair.Key])
</div>
}
}
</div>
Controller's POST method: presenting dictionary's values

prices is a Dictionary item (e.g. KeyValuePair<PriceLevel, decimal>).
So you must bind each property of the pair: Key and Value separately.

Related

ASP.NET MVC: store model data as JSON string

I'm working on a (for the time being) self-learning project to create an Accounting Software package to manage Customer, Invoice, Estimate, etc. data.
I'm currently working on the Customer system. I know how to setup the application to store the different pieces of data in different columns, but I wanted to learn how to store everything as a JSON string.
Models:
[Table("Customers")]
public partial class CustomerDb
{
public int Id { get; set; }
public string Obj_Data { get; set; }
}
I then created a Customer model for the individual pieces of data:
public partial class Customer
{
public int Company_Id { get; set; }
public string Customer_Name { get; set; }
public string Customer_Company { get; set; }
public Dictionary<string, string> Phones { get; set; }
public List<Dictionary<string, string>> Emails { get; set; }
public string Terms { get; set; }
public Dictionary<string, string> Locations { get; set; }
public Dictionary<string, string> Preferences { get; set; }
public string Exemptions { get; set; }
}
Add New Customer View:
#model BSheets.Models.Custom.CustomerDb
#{
ViewBag.Title = "Add";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Customer</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Obj_Data, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.Obj_Data, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Obj_Data, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Add" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
CustomerController:
using BSheets.Models;
using BSheets.Models.Custom;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
namespace BSheets.Controllers
{
public class CustomerController : Controller
{
private BSheetsEntities _db = new BSheetsEntities();
private ViewModel _vm = new ViewModel();
// GET: Customer
public ActionResult Index(string search)
{
_vm.Companies = _db.Companies.ToList();
_vm.Customers = _db.Customers.ToList();
if (string.IsNullOrEmpty(search))
{
AllResults();
}
else
{
FilteredResults(search);
}
return View();
}
public PartialViewResult AllResults()
{
return PartialView(Json(_vm));
}
public PartialViewResult FilteredResults(string search)
{
return PartialView(Json(_vm));
}
// GET: Customer/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
CustomerDb customer = _db.Customers.Find(id);
if (customer == null)
{
return HttpNotFound();
}
return View(customer);
}
// GET: Customer/Add
public ActionResult Add()
{
return View();
}
// POST: Customer/Add
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Add([Bind(Include = "ID,Obj_Data")] CustomerDb customer)
{
if (ModelState.IsValid)
{
_db.Customers.Add(customer);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(customer);
}
// GET: Clients/Update/5
public ActionResult Update(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
CustomerDb customer = _db.Customers.Find(id);
if (customer == null)
{
return HttpNotFound();
}
return View(customer);
}
// POST: Clients/Update/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Update([Bind(Include = "ID,Obj_Data")] CustomerDb customer)
{
if (ModelState.IsValid)
{
_db.Entry(customer).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(customer);
}
// GET: Clients/Remove/5
public ActionResult Remove(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
CustomerDb customer = _db.Customers.Find(id);
if (customer == null)
{
return HttpNotFound();
}
return View(customer);
}
// POST: Clients/Remove/5
[HttpPost, ActionName("Remove")]
[ValidateAntiForgeryToken]
public ActionResult RemoveConfirmed(int id)
{
CustomerDb customer = _db.Customers.Find(id);
_db.Customers.Remove(customer);
_db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_db.Dispose();
}
base.Dispose(disposing);
}
}
}
In a sense, I managed to make this work: the views to Add/Update customer information have a single TextArea where I simply add the JSON string. Then, in the Customer Index view, I deserialize the JSON string into a Customer object and display the individual Customer values. I then created a separate app with formfields using HTML/JavaScript to spit out a JSON string that I can copy/paste into.
If it were just me using this, it's perfectly fine as is pasting in the JSON string. Let's say I wanted to setup my application for a different user, editing a minified JSON string is cumbersome.
I'd like to create a view based on the Customer model defined above and submit a JSON string to the database from the CustomerController. Can someone point me in the right direction? Thanks.
I took a break from this for a few weeks, and I finally came up with an answer.
If I'm understanding what's going on:
In the CustomerController's Update GET action, I simply deserialize the input CustomerDb's Obj_Data property (it's the JSON string in my case) as a Customer object. I then pass the Customer object back to the view and it's working nicely so far (of course I bind the relevant model properties):
// GET: Clients/Update/5
public ActionResult Update(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
CustomerDb customerDb = _db.Customers.Find(id);
if (customerDb == null)
{
return HttpNotFound();
}
Customer customer = JsonConvert.DeserializeObject<Customer>(customerDb.Obj_Data);
return View(customer);
}
// POST: Clients/Update/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Update([Bind(Include = "Id,Customer_Name,Customer_Company,Phones,Emails,Terms,Locations,Preferences,Exemptions")] Customer customer)
{
if (ModelState.IsValid)
{
_db.Entry(customer).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(customer);
}
There were a few things I had to change; for the customer model, I had to replace the Dictionary properties with separate string properties. For example, I removed the Phones Dictionary and replaced it with primary, alternate and fax string properties, and the Emails Dictionary is now a string. I'm sure there's a way to work with Dictionary properties, but every time I tested everything using them, I'd get a Null reference exception from them.
Then, some simple edits to the various controller actions to Add and Delete customer records from the database and it's working very nicely.
#Tetsuya Yamamoto, thanks again for the help.

Dropdownlist and ViewData in ASP.NET MVC 4

I have a problem with my code. Iwan to make a dropdownlist in my View.cshtml using ViewData and #Html.Dropdownlist. But i get error while i compile it.
Here is my code :
This is the model
public class DeviceComponentModel
{
public int devcomId { get; set; }
public int devcomcomponentId { get; set; }
public int? devcomdeviceId { get; set; }
public ComponentModel Component { get; set; }
public DeviceModel Device { get; set; }
}
This is the code in the controller.
public ActionResult addDevicecomponent()
{
ViewData["Device"] = new SelectList(debuDB.Devices, "Id", "Name");
ViewData["Component"] = new SelectList(debuDB.Components, "Id", "Name");
DeviceComponentModel mdlDevicecom = new DeviceComponentModel();
return View(mdlDevicecom);
}
[HttpPost]
public ActionResult addDeviceComponent(DeviceComponentModel mdlDevicecom)
{
try
{
DeviceComponent deviceComponent = new DeviceComponent()
{
Id = mdlDevicecom.devcomId,
device_id = mdlDevicecom.devcomdeviceId,
component_id = mdlDevicecom.devcomcomponentId
};
debuDB.DeviceComponents.InsertOnSubmit(deviceComponent);
debuDB.SubmitChanges();
return RedirectToAction("Index");
}
catch
{
return View(mdlDevicecom);
}
}
This is my code in the View
<div class="editor-label">
#Html.LabelFor(model => model.devcomdeviceId)
</div>
<div class="editor-field">
#Html.DropDownList("device_0",ViewData["Device"] as IEnumerable<SelectListItem>, "Choose Device")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.devcomcomponentId)
</div>
<div class="editor-field">
#Html.DropDownList("component_0",ViewData["Component"] as IEnumerable<SelectListItem>, "Choose Component")
</div>
But when i compiled it i got the error like this:
"There is no ViewData item of type 'IEnumerable' that has the key 'device_0'."
I dont know why cant i get the error like that. I hope some of you can give the solution of this error.
Regards,

Get multiple checked checkbox values in MVC controller

My code is below:
On page i have a products list with checkbox, code & name of that product.
When selecting multiple checkboxes, on submit click, i need to get the selected checkbox values & save it in the DB.
Product View Model class:
public class ProductVM
{
public ProductVM()
{
this.ProductsList = new List<Product>();
}
public List<Product> ProductsList { get; set; }
public class Product
{
public bool IsSelected { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
}
Product Controller:
[HttpGet]
public ActionResult Edit()
{
var model = new ProductVM();
var product1 = new ProductVM.Product
{
Code = "Product1",
Name = "Apple"
};
var product2 = new ProductVM.Product
{
Code = "Product2",
Name = "Banana"
};
model.ProductsList.Add(Product1);
model.ProductsList.Add(Product2);
return View(model);
}
[HttpPost]
public ActionResult Edit(ProductVM model)
{
if (model.ProductsList.Any(m => m.Selected))
{
//How to get the selected Code & Name here
}
}
Product View:
#model ProductVM
#using(Html.BeginForm())
{
foreach (var item in Model.ProductsList)
{
#Html.CheckBox("IsSelected", item.IsSelected)
#Html.Label(item.Code)
#Html.Label(item.Name)
}
<input type="submit" />
}
First, you need to use for instead of foreach. Otherwise Razor won't generate the proper field names. Second, you need hidden inputs for the Code and Name properties so these will get posted back:
for (var i = 0; i < Model.ProductsList.Count(); i++)
{
#Html.HiddenFor(m => m.ProductsList[i].Code)
#Html.HiddenFor(m => m.ProductsList[i].Name)
<label>
#Html.CheckBoxFor(m => m.ProductsList[i].IsSelected)
#Html.DisplayFor(m => m.ProductsList[i].Code)
#Html.DisplayFor(m => m.ProductsList[i].Name)
</label>
}
To demo my code from my comment to question- You can get idea from it
#foreach (var item in ViewBag.FocusesList)
{
<label>
<input type="checkbox" value="#item.ConfigurationId" name="FocusList" id="FocusList" /> #item.Value.Name
</label>
}
After submit you have all checked values split by comma in Request["FocusList"]

Delete value by updating field

I have this:
View:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Bycicle</h4>
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.id)
<div class="form-group">
<label class="control-label col-md-2" for="BycicleID">Mark</label>
<div class="col-md-10">
#Html.DropDownList("BycicleID", String.Empty)
#Html.ValidationMessageFor(model => model.id)
</div>
</div>
The controller:
public async Task<ActionResult> Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Bycicle bycicle = await db.Bycicles.FindAsync(id);
if (bycicle == null)
{
return HttpNotFound();
}
ViewBag.id = new SelectList(db.Bycicles, "BycicleID", "Mark", bycicle.id);
//ViewBag.Color = new SelectList(db.Bycicles, "Color", "Color", bycicle.Color);
//ViewBag.Weight = new SelectList(db.Bycicles, "Weight", "Weight", bycicle.Weight);
//ViewBag.Height = new SelectList(db.Bycicles, "Height", "Height", bycicle.Height);
return View(bycicle);
}
// POST: Bycicle/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(
[Bind(Include = "id, Mark, Color, Weight,Height")]
Bycicle bycicle)
{
if (ModelState.IsValid )
{
db.Entry(bycicle).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.id = new SelectList(db.Bycicles, "id", "Mark", bycicle.id);
return View(bycicle);
}
and the Model:
public class Bycicle
{
public int id { get; set; }
// [Column("Mark")]
public string Mark { get; set; }
public string Color { get; set; }
public string Weight { get; set; }
public string Height { get; set; }
public DateTime? LastTimeChecked { get; set; }
}
But so I can update the other values(Color, Weight, Height) But the Mark name will be deleted if I press save in the Update view. I also checked the db and the Mark value is gone after Update. But If I just press on Edit(update) Then first I see the value(Mark) in the dropdownlist, but after save the mark value is gone. Even if I didnt changed the Mark value in the Update view.
I just cant figure what is wrong.
Thank you!!
Oke,
I did understand it was a type mistake,
But what do you mean with the dropdonlists are not correct? Because you dont have a infinite mark list of bycicles. So that Is why I choose to make a dropdownlist with all the marks and the colors and the sizes. Because an admin fill in the marks, sizes, etc and then the user can select a mark, color, etc. But how will you design the different properties?
I dont have seperate tables for color, mark, etc. But is that necessary?
Thank you
Your not binding anything to the property Mark (what you have shown is bound to the ID property). Preferably the names of your select lists should not be the same as your properties.
In the controller
ViewBag.MarkList = new SelectList(db.Bycicles, "BycicleID", "Mark");
and in the view
#model YourAssembly.Bycicle
....
<div class="form-group">
#Html.LabelFor(m => m.Mark)
<div class="col-md-10">
#Html.DropDownListFor(m => m.Mark, (SelectList)ViewBag.MarkList)
#Html.ValidationMessageFor(m => m.Mark)
</div>
</div>
Note, since you binding all properties, you do not need [Bind(Include = "id, Mark, Color, Weight,Height")]
Edit
While passing values by ViewBag works, I recommend you use view models and strongly typed helpers so you have intellisense support as well as all the other benefits of using view models.
public class BycicleVM
{
public int id { get; set; }
// other properties of Bycicle that you want to edit
public SelectList MarkList { get; set; }
// other select lists
}
then assign the select lists to the view model rather than ViewBag

MVC4: ViewModel is empty on [HttpPost]

I'm trying to make a simple news system in MVC4. I'm very new at it, and I have a simple News base class that looks like this:
NewsPost:
public class NewsPost
{
public virtual int Id { get; set; }
public virtual string Subject { get; set; }
public virtual string Content { get; set; }
}
It's then attached to a category class like so:
NewsCategory:
public class NewsCategory
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<NewsPost> NewsPosts { get; set; }
}
I then have a controller for creating these NewsPosts:
NewsController:
private readonly INewMvcSiteDataSource _db;
public NewsController(INewMvcSiteDataSource db)
{
_db = db;
}
[HttpGet]
public ActionResult Create()
{
var model = new CreateNewsViewModel();
model.Categories = new SelectList(_db.NewsCategories, "Id", "Name");
return View(model);
}
[HttpPost]
public ActionResult Create(CreateNewsViewModel newsModel)
{
if (ModelState.IsValid)
{
int id = int.Parse(newsModel.SelectedCategories.Single(f => f.Selected).Value);
}
return View(newsModel);
}
And lastly, to facilitate with creating the NewsPost, I use a CreateNewsViewModel, like so:
CreateNewsViewModel:
public class CreateNewsViewModel
{
[Required]
public string Subject { get; set; }
[Required]
public string Content { get; set; }
public SelectList Categories { get; set; }
public SelectList SelectedCategories { get; set; }
}
My view looks like this:
Create.cshtml:
#model NewMvcSite.Models.CreateNewsViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CreateNewsViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Subject)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Subject)
#Html.ValidationMessageFor(model => model.Subject)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Content)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Content)
#Html.ValidationMessageFor(model => model.Content)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Categories)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Categories, Model.SelectedCategories)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
For some reason, my viewmodel isn't returned when I click the submit button to create the newspost, and since there is no parameterless constructor for [HttpPost] Create action, it fails with "No parameterless constructor defined for this object."
I've been trying going through other posts here stating the same problem, but I fail to see the connection between what they are doing and what I am doing. I hope there is someone out there who can help me.
Thank you.
If you want to select category from drop down list, first of all you should add property to your model to hold Id of selected category. Something like this (property Category):
public class CreateNewsViewModel
{
[Required]
public string Subject { get; set; }
[Required]
public string Content { get; set; }
public SelectList Categories { get; set; }
public SelectList SelectedCategories { get; set; }
public int Category { get; set; }
}
after that, you should change code to filling model.Category to this:
model.Categories = new SelectList(categories, "Id", "Name", model.Category);
and than, in your view, editor for Category should look like this:
<div class="editor-field">
#Html.DropDownListFor(m => m.Category, Model.Categories);
</div>
model.Categories = new SelectList(_db.NewsCategories, "Id", "Name");
At this line you fill Categories property
#Html.DropDownListFor(model => model.Categories, Model.SelectedCategories)
Here you trying create select with Categories name and SelectedCategories items
int id = int.Parse(newsModel.SelectedCategories.Single(f => f.Selected).Value);
Here you trying get selected item, BUT on form submit you only sending one number, not list. One of your SelectList must be int
I found the solution.
I had to change the ViewModel to this:
public IEnumerable<SelectListItem> Categories { get; set; }
public int SelectedCategoryId { get; set; }
I changed the controller to:
var model = new CreateNewsViewModel
{
Categories = new SelectList(_db.NewsCategories, "Id", "Name")
};
And changed View to this:
#Html.DropDownListFor(x => x.SelectedCategoryId, Model.Categories)