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.
Related
I have a Job model and StatusOnHold model.
I added navigation property StatusOnHold in the Job model.
from some reason, when I'm saving the Job model with an empty StatusOnHold, I'm still getting value in the StatusOnHoldId in the Job model.
when StatusOnHold is empty, I'm trying to receive NULL value in the StatusOnHoldId in the Job model.
when StatusOnHold is not empty, I'm trying to get StatusOnHoldId and save the value in the StatusOnHold model (which it's working like that now).
Thank you so much.
Here is my Models...
public class StatusOnHoldViewModel
{
public int Id { get; set; }
public string Note { get; set; }
}
public class JobViewModel
{
[Key]
public int Id { get; set; }
public string JobNote { get; set; }
public JobStatus JobStatus { get; set; }
public CompanyViewModel Company { get; set; }
public CustomerViewModel Customer { get; set; }
public StatusOnHoldViewModel StatusOnHold { get; set; }
}
Here is the Controller...
public async Task<IActionResult> Create(JobViewModel jobViewModel)
{
if (ModelState.IsValid)
{
var job = _mapper.Map<Job>(jobViewModel);
var newjobId = await _jobRepository.AddAsync(job);
return RedirectToAction("details", new { id = newjobId });
}
return View();
}
And here is the view...
<div class="form-group row">
<label class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<div class="m-1">On-Hold</div>
<textarea asp-for="StatusOnHold.Note" style="height:86px; min-height:86px" class="form-control" placeholder="Reason..."></textarea>
<span asp-validation-for="StatusOnHold.Note" class="text-danger"></span>
</div>
</div>
You don't have property for the StatusOnHoldViewModel the navigation property alone will not work.
So add
public int StatusOnHoldViewModelId { get; set; }
to your JobViewModel
StatusOnHoldId will not be empty and it always have data, if you do not write note in textarea, it will look like { "id":0,"note":null} on action for jobViewModel` ,this wll create a new record.
A workaround is that you could set StatusOnHold as null when note is null:
public async Task<IActionResult> Create(JobViewModel jobViewModel)
{
if (ModelState.IsValid)
{
if(jobViewModel.StatusOnHold.Note == null)
{
jobViewModel.StatusOnHold = null;
}
var job = _mapper.Map<Job>(jobViewModel);
var newjobId = await _jobRepository.AddAsync(job);
return RedirectToAction("details", new { id = newjobId });
}
return View();
}
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"]
I am now kind of stuck here.
I have my Models here
namespace DSS.Models
{
public class SupplierItems
{
public string GroupID { get; set; }
public string GroupName { get; set; }
public List<SupplierLineItems> LineItems { get; set; }
}
}
and have Properties of Supplier Items are defined in Model as
namespace DSS.Models
{
public class SupplierLineItems
{
public string Frequency { get; set; }
public string UnitPrice { get; set; }
public int Index { get; set; }
}
}
My Controller is just hardcoding the Supplier Items and line items
namespace DSS.Controllers
{
public class HomeController : Controller
{
[HttpPost]
public ActionResult GetLineItems(SupplierItems SupplierItems)
{
return View();
}
public ActionResult Index()
{
List<SupplierLineItems> ListOfLineItems = new List<SupplierLineItems>();
SupplierLineItems LineItems = new SupplierLineItems();
LineItems.Frequency = "Regulate";
LineItems.UnitPrice = "20";
LineItems.Index = 1;
ListOfLineItems.Add(LineItems);
SupplierLineItems LineItems1 = new SupplierLineItems();
LineItems1.Frequency = "Regulate";
LineItems1.UnitPrice = "20";
LineItems.Index = 2;
ListOfLineItems.Add(LineItems1);
SupplierItems Items = new SupplierItems();
Items.GroupID = "1";
Items.GroupName = "Core Group";
Items.LineItems = ListOfLineItems;
return View(Items);
}
}
}
Here is my View
#model DSS.Models.SupplierItems
#using DSS.Models
#{
ViewBag.Title = "Home Page";
{
<a type="submit" href="#Url.Action("GetLineItems", "Home", Model)">#Model.GroupName</a>
<br />
foreach (var item in Model.LineItems)
{
#item.Frequency<br />
#item.UnitPrice<br />
#item.Index<br />
}
}
}
When I click on Anchor tag of my view and pass the Model to "GetLineItems" of Home Controller, My Supplier items get passed successfully but List of Line items always comes as null. I am not sure what is going wrong . Anyone, please help
enter code here
You should use ajax post to the controller or simply use form submission to achieve this
instead of using url.action which is an url form of submission
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.
I have a strange problem and I don't know if this is actually possible.
What I want is, to be able to list all the values from my model and and edit them directly in the list.
Here's what I have:
Model Linker:
public class StoreLinkerModel
{
//public Guid? id { get; set; }
public IEnumerable<Stores> StoresAndOpeninghours { get; set; }
}
public class Stores
{
public long ID { get; set; }
public string StoreName { get; set; }
public string Address { get; set; }
public string Zip { get; set; }
public string City { get; set; }
}
My Controller:
public ActionResult Overview()
{
var model = new StoreLinkerModel
{
StoresAndOpeninghours = new[]
{
new Stores()
{
ID = 0,
Address = "Enghavevej 70"
},
new Stores()
{
ID=1,
Address = "Løngangsgade 30"
},
}
};
return View(model);
}
[HttpPost]
public ActionResult Overview(StoreLinkerModel model)
{
if (ModelState.IsValid)
{
var x = "go go go";
}
return RedirectToAction("Overview");
}
My overview.aspx page:
#model streetoffrs.web.Models.StoreLinkerModel
#{
ViewBag.Title = "Overview";
Layout = "~/Views/Shared/_dashboard.cshtml";
}
#Html.EditorFor(x => x.StoresAndOpeninghours)
and my EditorTemplate stores.aspx
#model streetoffrs.web.Models.Stores
#using (Html.BeginForm("Overview", "Dashboard", FormMethod.Post, new { name = "id" + #Html.DisplayFor(m => m.ID) }))
{
#Html.EditorFor(x => x.Address)
<input type="submit" class="left btn btn-primary" value="Ret butiksdata">
}
<br />
The list is being generated as it should, and when I hit the first button at the first editorfor it will post the model to my controller, but when I push the 2nd button, the model is null, but the first button still works!
Is this possible at all, if yes what am I missing, if not, tell me how I can accomplish this.
thanks in advance!
you need edit post action like this:
[HttpPost]
public ActionResult Overview(StoreLinkerModel model)
{
if (ModelState.IsValid)
{
var x = "go go go";
}
return View(model);
}
the RedirectToAction will be go to the first Overview Action,so you will be lost the data.