MVC5 not passing JSON property names through to view - json

I'm just starting out with MVC, JSON, AJAX, etc and as a side project have been trying to create a data viz dashboard.
Today I followed this guide on how to pass a simple table of data from SQL as JSON to my view: http://techfunda.com/howto/292/list-records-using-json
It mostly worked: the JsonResult comes through from my controller and contains the values but not the property names.
This causes a problem because I'm referencing the property names when I process the data for display in JavaScript.
Here's the SQL data:
Here's my Model:
public partial class vw_Dash_ChartData : IEnumerable<object>
{
[Key]
[JsonProperty(PropertyName = "Classification")]
public string Classification { get; set; }
[JsonProperty(PropertyName = "Count")]
public int Count { get; set; }
public IEnumerator<object> GetEnumerator()
{
yield return Classification;
yield return Count;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
(You'll notice I tried to manually set the [JsonProperty(...)] stuff...it didn't help.)
And here's my JsonResult:
public JsonResult ChartDataJson()
{
var data = new List<vw_Dash_ChartData>();
data = db.vw_Dash_ChartDatas.ToList();
var jsonData = Json(data, JsonRequestBehavior.AllowGet);
return jsonData;
}
(Initially I was sending the data straight through from my DbContext but then thought perhaps it would help to use my vw_Dash_ChartData model. It didn't make a difference).
My view looks like the following:
#{
ViewBag.Title = "Charts";
AjaxOptions options = new AjaxOptions
{
//Confirm = "Are you sure?",
LoadingElementId = "divLoading",
OnSuccess = "processDataMethod",
Url = Url.Action("ChartDataJson")
};
}
<script type="text/javascript">
function processDataMethod(data) {
var output = $("#dataZone");
output.empty();
for (var i = 0; i < data.length; i++) {
var chartData = data[i];
output.append("<tr><td>" + chartData.Classification + "</td><td>" + chartData.Count + "</td></tr>");
}
}
</script>
<div>
<table>
<thead>
<tr>
<th>Classification</th>
<th>Count</th>
</tr>
</thead>
<tbody id="dataZone">
</tbody>
</table>
</div>
#using (Ajax.BeginForm(options))
{
<div id="divLoading" style="color: red; font-size: larger;">
Loading...
</div>
<div>
<button type="submit" id="btnClicky" >Clicky</button>
</div>
}
<script>
$("#btnClicky").trigger("click");
</script>
When I load the page, this is what I get:
and this is the JSON object shown in the browser developer tools;
Any tips/ideas gratefully received! Also, if I'm doing anything stupid do let me know as I'd like to learn best practice for this stuff.

How about?
var jsonData = Json(data.Select(x=> new {
Classification = x.Classification,
Count = x.Count
})
);
return jsonData;

Related

How to pass selected checkboxes ID values from view to the controller

I am a newbie to spring boot and thymeleaf,
I have a list of books ina table with checkboxes, I am not sure how to pass selected booksId s from the view to the controller and use them by the borrow or Return bttons? could you please help?
Here is my Html file https://wtools.io/paste-code/b5g4
and this is the relevant part from my bookService implementation :
public void borrowBook(String userEmail, String bookIds, Model model) {
if (!CollectionUtils.isEmpty(books)) {
User user = userRepository.findByEmail(userEmail);
List<String> requestedBooks = getRequestedBookIds(bookIds);
List<Book> borrowedBooks = new ArrayList<>();
List<Book> invalidBooks = new ArrayList<>();
for (Book book : books) {
if (requestedBooks.contains(book.getId()) && !book.isBorrowed() && user != null) {
book.setBorrowed(true);
book.setBorrowedBy(user.getFirstName());
borrowedBooks.add(book);
model.addAttribute("bookStatus", "Book BOrrowed By " + user.getFirstName());
} else {
invalidBooks.add(book);
model.addAttribute("bookStatus", "No Books are available");
}
}
model.addAttribute("inValidBooks", invalidBooks);
model.addAttribute("bookList", borrowedBooks);
}
}
#SuppressWarnings("unchecked")
private List<String> getRequestedBookIds(String bookIds) {
List<String> requestedBookIds = null;
try {
requestedBookIds = new ObjectMapper().readValue(bookIds, ArrayList.class);
} catch (Exception e) {
e.printStackTrace();
}
return !CollectionUtils.isEmpty(requestedBookIds) ? requestedBookIds : new ArrayList<>();
}
and this is from the controller:
#GetMapping(value = "/available", produces = MediaType.APPLICATION_JSON_VALUE)
public String getAvailableFreeBooks(Model model) {
List<Book> availableBooks= bookService.getAllAvailaBooks();
model.addAttribute("listBooks", availableBooks);
return "available_books";
}
In your html you would probably:
<input type="checkbox" th:field="*{requestedBooks}" value="${book.getId}">
omit the id (if you don't need it).
use th:field (instead of name).
set value to the id of the current book.
In your controller: requestedBooks (#ModelAttribute("requestedBooks") List<String> requestedBooks) will (should) contain all checked book ids.
Ref: https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#checkbox-fields
A sample repository:
https://github.com/xerx593/soq67602860
Uppdate:
To process the checkboxes client-sided (jquery),
you can obtain an array of ids like:
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$("#btnBorrow").click(function() {
var reqBookIds = new Array();
$('input[name="requestedBooks"]:checked').each(function() {
reqBookIds .push(this.value);
});
alert("Number of selected Books: "+reqBookIds .length+"\n"+"And, they are: "+reqBookIds);
// do stuff with reqBookIds ...
)};
});
</script>
With the mentioned <input type="checkbox" .../> (consider that <input/> should be inside a <form/>!!) and a button like:
<button id="btnBorrow">Borrow</button>
..the userEmail must come from client side???

Passing Data from View to Partial in .NET CORE

I read this and tried to implement the ViewDataDictionary in my app but did not work.
In my view, I've the below code:
#{
var myTest = new
{
UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78",
UserName = "iggy",
};
}
#Html.Partial("~/Partials/test.cshtml", myTest)
and the test.cshtml is very simple, when I write #Model, I get { UserId = cdb86aea-e3d6-4fdd-9b7f-55e12b710f78, UserName = iggy }
How can I extract this as JSON, so I can read #Model.UserName
I tried using:
<script type="text/javascript">
#
{
<text>
var obj = JSON.parse(#Model);
</text>
}
</script>
and tried:
<script type="text/javascript">
#:var obj = JSON.parse(#Model);
</script>
and tried:
#Html.Raw(Json.Encode(object))
but nothing worked, any help!!
I came across similar problem when I was converting my .net application into .net core.
In .net I could just return Json(jsonModel) in my controller and use it in my view as data.UserID, data.UserName (see code below adjusted to match your sample).
In .net core I had to SerializeObject in my controller first and then use JSON.parse in the view javascript section to make it work.
The following works in .NET CORE. Assuming you have some model:
public class SomeModel
{
public string UserId { get; set; }
public string UserName { get; set; }
}
in your controller return Json object:
using Newtonsoft.Json;
[HttpPost]
public IActionResult someAction()
{
SomeModel jsonModel = new SomeModel();
jsonModel.UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78";
jsonModel.UserName = "iggy";
var serializedJsonModel = JsonConvert.SerializeObject(jsonModel);
return Json(serializedJsonModel);
}
and in your view javascript section you can retrieve values from your model:
<script type="text/javascript">
$.post("#Url.Action("someAction", "YourController")",
function (data) {
var oJson = JSON.parse(data);
UserId = oJson.UserId;
UserName = oJson.UserName; });
</script>
If you're only interested in JSON serialization of your anonymous type, you can simply declare the #model of the partial as object.
In your main view:
#{
var myTest = new
{
UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78",
UserName = "iggy",
};
}
#Html.Partial("~/Partials/test.cshtml", myTest)
And in your test.cshtml partial:
#model object
<script type="text/javascript">
var obj = #Html.Raw(Json.Encode(Model));
</script>

Data Table transfer to Model then to the view

Ok so after alot of research i have concluded that passing a data table to a view is a bad idea ,so how do i pass my data table to a Model and then be able to access each row and column in the view?Sorry i am new to MVC
I start with a simple SQL statement
StringBuilder sbSQL = new StringBuilder();
//// define a list of CustomerModel objects
DataSet tempDS = new DataSet();
//string xSQL = "SELECT PropertyAddress,PropertyTypeDesc,PropertyID FROM KDOR_vwPropertyGeneral ORDER BY PropertyAddress";
System.Data.SqlClient.SqlDataAdapter DbCmd = new System.Data.SqlClient.SqlDataAdapter();
string sqlWhereCont = " WHERE ";
sbSQL.Append("SELECT ");
sbSQL.Append("PropertyAddress As PropertyAddress,");
sbSQL.Append("PropertyTypeDesc As PropertyTypeDesc,");
sbSQL.Append("PropertyID as PropertyID");
sbSQL.Append(" FROM [KDOR_vwPropertyGeneral] ");
if (!string.IsNullOrEmpty(user.Address))
{
sbSQL.Append(sqlWhereCont + "(PropertyAddress) LIKE '" + user.Address + "%'");
sqlWhereCont = "AND ";
}
sbSQL.Append(" ORDER BY ");
sbSQL.Append(" PropertyAddress ");
string MyConnectionString = ConfigurationManager.ConnectionStrings["WLConnection"].ConnectionString;
System.Data.SqlClient.SqlConnection cnn = new System.Data.SqlClient.SqlConnection(MyConnectionString);
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(sbSQL.ToString(), cnn);
cmd.CommandTimeout = 30000;
DbCmd.SelectCommand = cmd;
move the data to a DataSet and Data Table
DbCmd.Fill(tempDS, "ResultSet");
DataTable resultSet = tempDS.Tables["ResultSet"];
Add items to Model
var vm = new List<BedroomModel>();
foreach (DataRow dr in tempDS.Tables[0].Rows)
{
vm.Add(new BedroomModel {PropertyAdd = dr.ItemArray[0].ToString() });
vm.Add(new BedroomModel { PropertyDesc = dr.ItemArray[1].ToString() });
vm.Add(new BedroomModel { PropertyID = dr.ItemArray[2].ToString() });
}
Now how to i Access each Item and loop through them in a view? Cause i get an error here is a look at my view
#model DataBaseTest.Models.BedroomModel
#{
ViewBag.Title = "Result";
}
<h2>Result</h2>
#{
ViewBag.Title = "Result";
}
<table border ="1">
<thead>
#* <tr>
#foreach (var col in Model.Columns) {
<th>
#col.ColumnName
</th>
}
</tr>*#
<tr>
<th>Property Address</th>
<th>Property Description</th>
<th>Property ID</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.PropertyAdd)
{
<tr>
<td>#Model.PropertyAdd</td>
</tr>
}
</tbody>
Model
namespace DataBaseTest.Models
{
public class BedroomModel
{
public string Address { get; set; }
public string PropertyAdd { get; set; }
public string PropertyID { get; set; }
public string PropertyDesc { get; set; }
public IEnumerable<BedroomModel> BedroomModels { get; set; }
}
}
Again Sorry i am new to MVC
Any advice Would be Greatly Appreciated.
You are telling your view that it should look for a single BedroomModel object, when you actually want to pass it a List<BedroomModel> object.
#model List<DataBaseTest.Models.BedroomModel>
As a result, your Model property will be the list itself, so your foreach loop only needs to loop through the Model, not Model.BedroomModels.
#foreach (var item in Model)
{
<tr>
<td>#item.PropertyAdd</td>
<td>#item.PropertyDesc</td>
<td>#item.PropertyID</td>
</tr>
}
Because of this change, you can remove the BedroomModels property from your BedroomModel class.
public class BedroomModel
{
public string Address { get; set; }
public string PropertyAdd { get; set; }
public string PropertyID { get; set; }
public string PropertyDesc { get; set; }
}
That would fix your problem, but I also noticed that when populating your vm list, you are adding three items into the List, where you should only be adding one BedroomModel object. Your foreach loop should look like this:
var vm = new List<BedroomModel>();
foreach (DataRow dr in tempDS.Tables[0].Rows)
{
vm.Add(new BedroomModel
{
PropertyAdd = dr.ItemArray[0].ToString(),
PropertyDesc = dr.ItemArray[1].ToString(),
PropertyID = dr.ItemArray[2].ToString()
};
}
You must also make sure that you are sending the ViewModel into the View. Your Action should return:
return View(vm);
Add this to your view at the top.
#model DataBaseTest.Models.BedroomModel
This will make the view a strongly typed view. Meaning that it knows what model it will be using to display the data and what properties are available.
Then assign your collection in your controller method prior to your view rendering.
I edited this portion. I just now noticed that you were adding a new object for each property instead of assigning the properties to one object and then adding them. Then you will get a null reference.
public ActionResult MyView()
{
BedroomModel lModel = new BedroomModel();
lModel.BedroomModels = new List<BedroomModels>();
DataSet tempDS = CallToBLLOrDAL(); //Do what you need to do to get your data.
//Assign your values to the ViewModel (Aka lModel in this instance).
foreach (DataRow dr in tempDS.Tables[0].Rows)
{
//I am unsure exactly why you are calling ItemArray and adding a new Model for each column.
//Your way.
lModel.BedroomModels.Add(new BedroomModel { PropertyAdd = dr.ItemArray[0].ToString() });
lModel.BedroomModels.Add(new BedroomModel { PropertyDesc = dr.ItemArray[1].ToString() });
lModel.BedroomModels.Add(new BedroomModel { PropertyID = dr.ItemArray[2].ToString() });
//Try this
lModel.BedroomModels.Add(new BedroomModel { PropertyAdd = dr.ItemArray[0].ToString(), PropertyDesc = dr.ItemArray[1].ToString(), PropertyID = dr.ItemArray[2].ToString()});
}
return View(lModel);
}
Then in your view.
#foreach (BedroomModel lBedroomModel in Model.BedroomModels)
{
<td>
#lBedroomModel.PropertyAdd
</td>
}
Debugging tips.
You can set a break point in your view inside of your foreach. Drag your Model down to your watches. You should be able to see the population of your model and all of the values within the current scope.
I am unsure why the coloring is off on the view code snippet.
Let me know what you think.
Please set as answer if this helps.

Html.ListBoxFor Object reference not set to an instance of an object Error

I am using view model to display a dropdownlist and i am also trying to get the value of the selected list, here is my view model
public class CreateJobViewModel
{
public int[] SelectedIndustriesIds { get; set; }
public IList<SelectListItem> IndustriesList { get; set; }
}
My controller
public ActionResult Create()
{
var industryList = repository.GetAllIndustries();
var model = new CreateJobViewModel
{
IndustriesList = industryList.Select(i => new SelectListItem
{
Value = i.IndustryId.ToString(),
Text = i.Name
}).ToList()
};
return View("~/Views/Dashboard/Job/Create.cshtml", model);
}
My post controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateJobViewModel model)
{
try
{
var job = new Job()
{
Title = "hi",
EmploymentHourId = 1,
LocationId = 1,
Salary = 50,
SalaryPeriodId = 1,
PostCode = 2131,
Role = "world",
Description = "hello",
IsPublished = false,
ShiftId = 1,
WorkDayId = 1,
NumberOfPosition = 5,
Meal = false,
SecondYearVisa = true,
Sponsorship = true,
Accommodation = true,
DurationId = 1,
IndustryExperiencePeriod = 5,
Id = User.Identity.GetUserId(),
};
foreach (int id in model.SelectedIndustriesIds)
{
var industry = repository.Industry(id);
job.Industries.Add(industry);
}
foreach (int id in model.SelectedSpecialRequirementsId)
{
var special = repository.SpecialRequirement(id);
job.SpecialRequirements.Add(special);
}
repository.AddJob(job);
return RedirectToAction("Create");
}
catch
{
return View("~/Views/Dashboard/Job/Create.cshtml");
}
}
Every time i try to submit the selected value, i get Object reference not set to an instance of an object Error on the following line in my view:
#model Taw.WebUI.Models.CreateJobViewModel
#Html.ListBoxFor(m => m.SelectedIndustriesIds, Model.IndustriesList) -- here i get the error
Any reason why?
When you submit the form your throwing an exception (confirmed in the comments) and in the catch block you are returning the view, which throws the exception you are seeing because Model.IndustriesList is null. You need to re-assign the value before you return the view.
Since you need to assign SelectLists in the GET method and in the POST method if you return the view, I tend to re-factor this to a separate method to keep the controller code a bit cleaner. Note the following code is based on your model property being public SelectList IndustriesList { get; set; } which is a bit simpler than building IList<SelectListItem>
private void ConfigureViewModel(CreateJobViewModel model)
{
var industryList = repository.GetAllIndustries();
model.IndustriesList = new SelectList(industryList, "IndustryId", "Name")
// any other common stuff
}
and then in the action methods
public ActionResult Create()
{
var model = new CreateJobViewModel();
ConfigureViewModel(model);
return View(model);
}
public ActionResult Create(CreateJobViewModel model)
{
try
{
....
}
catch
{
ConfigureViewModel(model);
return View(model);
}
}
Note its also good practice to test if the model is valid before attempting to save it
public ActionResult Create(CreateJobViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model); // return the view so the user can correct validation errors
}
....

How can I override the name attribute of a RadioButtonFor?

I'm trying to group together radiobuttons that are creating using a for loop and Razor syntax. Here is the code:
#for (var i = 0; i < Model.Sessions.Count(); i++)
{
#Html.HiddenFor(it => it.Sessions[i].Id)
#Html.RadioButtonFor(it => it.Sessions[i].Checkbox, "0", new {#class = "Sessions", #id = id, #name="Sessions"})
#Html.LabelFor(it => it.Sessions[i].Name, Model.Sessions[i].Name)
<span class="time-span"><em>#Model.Sessions[i].StartTime</em><em>#Model.Sessions[i].EndTime</em></span>
<br />
}
The third line inside the for loop is where the problem is. Basically the name doesn't change and it's always "Sessions[x].Checkbox". The checkbox is a property (bool) of a custom class. I can't seem to get the hang of debugging Razor stuff, so any help would be greatly appreciated, I'm guessing this will be extremely obvious to someone here.
EDIT
Dimitrov's post helped a lot. Below is the final code I used. I use the #class and #id attributes to be able to use Javascript to select the session originally picked (since this is an edit, not create form).
#for (var i = 0; i < Model.Sessions.Count(); i++)
{
#Html.HiddenFor(it => it.Sessions[i].Id)
var SId = #Model.Sessions[i].Id;
#Html.RadioButtonFor(it => it.selectedSession, Model.Sessions[i].Id, new { id = SId, #class = "Sessions" })
#Html.LabelFor(it => it.Sessions[i].Name, Model.Sessions[i].Name)
<span class="time-span"><em>#Model.Sessions[i].StartTime</em><em>#Model.Sessions[i].EndTime</em></span>
<br />
}
If you want to be able to select only a single radio button you need to have a single property on your view model to hold the selected session id, like this:
public class SessionViewModel
{
public int SelectedSessionId { get; set; }
public IList<Session> Sessions { get; set; }
}
public class Session
{
public int Id { get; set; }
public string Name { get; set; }
}
and then have a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new SessionViewModel
{
SelectedSessionId = 2,
Sessions = Enumerable.Range(1, 5).Select(x => new Session
{
Id = x,
Name = "session" + x,
}).ToList()
};
return View(model);
}
[HttpPost]
public ActionResult Index(SessionViewModel model)
{
return Content("Thank you for selecting session id: " + model.SelectedSessionId);
}
}
and finally a view:
#model SessionViewModel
#using (Html.BeginForm())
{
for (var i = 0; i < Model.Sessions.Count(); i++)
{
#Html.HiddenFor(x => x.Sessions[i].Id)
#Html.RadioButtonFor(x => x.SelectedSessionId, Model.Sessions[i].Id, new { id = "session_" + i })
#Html.Label("session_" + i, Model.Sessions[i].Name)
<br/>
}
<button type="submit">OK</button>
}