How to display a List of objects in C# to javascript using Google JS API in MVC3? - asp.net-mvc-3

I have a .Net MVC3 website where I want to draw Pie Charts, but the Google Charts API or maybe JSON doesn't work right in my code and I don't know why.
I tried to refer from this sets of codes: MVC4 with Google JS API
This is my ChartController Code:
public ActionResult Sales()
{
return Json(CreateList(), JsonRequestBehavior.AllowGet);
}
public class ItemForChart
{
public String Name { get; set; }
public Int32 Qty { get; set; }
}
public IEnumerable<ItemForChart> CreateList()
{
List<ItemForChart> list = new List<ItemForChart>();
ItemForChart itm1 = new ItemForChart() { Name = "A", Qty = 1 };
ItemForChart itm2 = new ItemForChart() { Name = "B", Qty = 2 };
ItemForChart itm3 = new ItemForChart() { Name = "C", Qty = 3 };
ItemForChart itm4 = new ItemForChart() { Name = "D", Qty = 4 };
list.Add(itm1); list.Add(itm2); list.Add(itm3); list.Add(itm4);
return list;
}
This is my View Code for the Sale ActionResult
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" >
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(drawChart);
function drawChart() {
$.get('/Chart/Sales', {},
function (data) {
var tdata = new google.visualization.DataTable();
tdata.addColumn('string', 'Name');
tdata.addColumn('number', 'Qty');
for (var i = 0; i < data.length; i++) {
tdata.addRow([data[i].Name, data[i].Qty);
}
var options = {
title: "Sample Charts"
};
var chart1 = new google.visualization.AreaChart(document.getElementById('chart_div1'));
var chart2 = new google.visualization.LineChart(document.getElementById('chart_div2'));
var chart3 = new google.visualization.PieChart(document.getElementById('chart_div3'));
var chart4 = new google.visualization.ColumnChart(document.getElementById('chart_div4'));
chart1.draw(tdata, options);
chart2.draw(tdata, options);
chart3.draw(tdata, options);
chart4.draw(tdata, options);
});
}
</script>
<div id="chart_div1" style="width: 900px; height: 500px;">
</div>
<div id="chart_div2" style="width: 900px; height: 500px;">
</div>
<div id="chart_div3" style="width: 900px; height: 500px;">
</div>
<div id="chart_div4" style="width: 900px; height: 500px;">
</div>
And when I run the program, it shows this,
[{"Name":"A","Qty":1},{"Name":"B","Qty":2},{"Name":"C","Qty":3},{"Name":"D","Qty":4}]
^it doesn't show any charts but only that.
or if there are other Charts API that I can use that is also easy to learn, I am open for suggestion.

It seems like you are browsing to the JSON controller endpoint localhost/MyApp/Chart/Sales (which is consumed by the Ajax callback in google.setOnLoadCallback to feed data to the chart) - it looks like the JsonRresult is working fine. In order to view the Graph, you'll need to browse to the "Sale ActionResult" route (something like localhost/MyApp/SomeController/Sale)
You also have a typo:
for (var i = 0; i < data.length; i++) {
tdata.addRow([data[i].Name, data[i].Qty]);
// ^ closing array brace
}
Fixed jsFiddle
Not directly related, but note that you can use Object initializer syntax on the server C# to simplify matters:
public IEnumerable<ItemForChart> CreateList()
{
return new List<ItemForChart>
{
new ItemForChart() { Name = "A", Qty = 1 },
new ItemForChart() { Name = "B", Qty = 2 },
new ItemForChart() { Name = "C", Qty = 3 },
new ItemForChart() { Name = "D", Qty = 4 }
}
}

Related

Pushing array to select in knockout gives just one option

I am using MVC and a Razor View I'm trying to bound data received from a controller to a select using a knockout model, If I try to push directly the dynamic array I get only one option like this one
Only one option select:
I'm sure that I'm missing something stupid, I have already tried to return a new SelectList and using optionsText and optionsValue but didn't do the work.
I'm sure the knockout model is correct because if I write
viewModel.dliveryDates.push("option1","option2");
it works as expected
Here's my controller code that reads some data from database and send it back to the view
[HttpPost]
public JsonResult GetDeliveryDates(string code)
{
OrderHeaderPageModel instance = ObjectFactory.Create<OrderHeaderPageModel>();
instance.DeliveryDateRanges = PopulateDeliveryDateRanges(code);
return Json(instance.DeliveryDateRanges.ToArray());
}
Here's is my View code
#Html.DropDownList("deliveryranges", new SelectList(string.Empty, "Code", "Description"), "- Seleziona -", new { #data_bind = "options:dliveryDates" })
And finally my knockout model
function OrderHeaderViewModel() {
var self = this;
self.save = function () {
return true;
}
self.dliveryDates = ko.observableArray([]);
}
var viewModel = new OrderHeaderViewModel();
ko.applyBindings(viewModel, el);
$("#ordertypes").change(function () {
var postUrl = "/checkout/getdeliverydates";
$("#deliveryranges").empty();
$.post(postUrl,
{
code: $("#ordertypes").val(),
__RequestVerificationToken: Sana.Utils.getAntiForgeryToken()
}, function (data) {
var arry = [];
var array = $.map(data, function (value, index) {
return [value];
});
$.each(data, function (i, data) {
arry.push(data.Code);
});
viewModel.dliveryDates.push(arry);
}
);
})
It looks like the code is doing some extra work mapping data that is not used in the ajax callback. Hope the following code helps.
function OrderHeaderViewModel() {
var self = this;
self.getData = function() {
//function to simulate filling the array from the server.
var data = ["Item 1", "Item 2", "Item 3", "Item 4"];
self.dliveryDates(data);
var mappedData = data.map(function(item, index) {
return {
id: index,
description: item
};
});
viewModel.mappedDliveryDates(mappedData);
}
self.save = function() {
return true;
}
//added to put the selected values in
self.selectedValue = ko.observable();
self.selectedMappedValue = ko.observable();
self.mappedDliveryDates = ko.observableArray([]);
self.dliveryDates = ko.observableArray([]);
}
var viewModel = new OrderHeaderViewModel();
ko.applyBindings(viewModel);
$("#ordertypes").change(function() {
var postUrl = "/checkout/getdeliverydates";
$("#deliveryranges").empty();
$.post(postUrl, {
code: $("#ordertypes").val(),
__RequestVerificationToken: Sana.Utils.getAntiForgeryToken()
}, function(data) {
// if the data needs to be transformed and is already an array then you can use
var mappedData = data.map(function(item, index) {
return {
id: index,
description: item
};
});
// If the data is already in the format that you need then just put it into the observable array;
viewModel.mappedDliveryDates(mappedData);
viewModel.dliveryDates(data);
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Server Data Values -
<select data-bind="options: dliveryDates, value:selectedValue, optionCaption: 'Choose...'"></select>
<br/> Mapped Values -
<select data-bind="options: mappedDliveryDates, optionsText:'description', value: selectedMappedValue, optionCaption: 'Choose...'"></select>
<br/>
<button data-bind="click: getData">Load Data</button>
<br/>
<br/>
<pre data-bind="text: ko.toJSON($root)"></pre>

KendoUI grid with draggable only works when I have developer tools open

I've created a nice little KendoUI grid with drag and drop. It works great... as long as I have the developer tools open. Any idea why this would work with the dev tools open, but doesn't work at all with just using the browser?
Edit to add the code:
my cshtml page:
<div id="DisplayOrderGridContainer">
<div class="validation-error-box" id="errorMessages" style="display:none">
<span>Error!</span>
<ul id="message">
<li>The Record you attempted to edit was modified by another user after you got the original value. The edit operation was canceled.</li>
</ul>
</div>
<div style="padding-bottom: 15px;">
#Html.ActionLink("Back", Model.BackActionName, Model.ControllerName, Model.BackRouteValues, new { id = Model.ControllerName + "_Back", #class = "k-button" })
#if (Model.AllowClearingDisplayOrder)
{
#Html.ActionLink("Clear Order", Model.ClearActionName, Model.ControllerName, Model.BackRouteValues, new { #id = "clear-button", #class = "k-button float-right" })
}
</div>
<div id="KendoGridContainer">
<div id="ChangeDisplayOrderGrid"
class="grid"
data-role="grid"
data-bind="source:data, events:{dataBound:onDataBound,columnHide:OnColumnHide,columnShow:OnColumnShow}"
data-filterable="false"
data-sortable="false"
data-column-menu="true"
data-row-template="rowTemplate"
#*data-toolbar='[{ "template": kendo.template($("#toolbarTemplate").html()) }]'*#
data-columns='[{title:"Short Name", field:"NameField", width: 80, headerAttributes:{id: "#Model.ControllerName" + "_ShortName"}},
{title:"Description", field:"DescriptionField", width:300, headerAttributes:{id: "#Model.ControllerName" + "_Description"}},
{title:"Display Order", field:"Display", width:140, headerAttributes:{id: "#Model.ControllerName" + "_Display"}},
{title:"Value", field:"Value", hidden: true, headerAttributes:{id: "#Model.ControllerName" + "_Value"}}]'>
</div>
<script id="rowTemplate" type="text/x-kendo-tmpl">
<tr class="k-master-row" data-uid="#:uid#">
<td class="text-right">#=NameField#</td>
<td class="text-right">#=DescriptionField#</td>
<td class="text-right">#=Display#</td>
<td class="text-right" style="display:none;">#=Value#</td>
</tr>
</script>
<div id="grid" data-grid-url="#(Url.Action(Model.ActionName, Model.ControllerName))" data-grid-viewdataid="#ViewData.ID()"></div>
</div>
<input type="hidden" id="displayOrderId" />
<input type="hidden" id="Id" />
my js page:
$(document).ready(function () {
UnsavedWarningsModule.ClearUnsavedChanges();
var newData = new kendo.data.DataSource({
transport: {
read: {
url: $('#grid').attr('data-grid-url'),
dataType: "json"
}
},
schema: {
model: {
id: "Id"
}
}
})
var viewModel = new kendo.observable({
data: newData,
onDataBound: function (e) {
pfsKendoGridEvents.SetSelectedRow_MVVMGrid("KendoGridContainer", e.sender, $('#grid').attr('data-grid-viewdataId'))
},
OnColumnHide: function (e) {
pfsKendoGridEvents.OnHideShowColumns(e);
},
OnColumnShow: function (e) {
pfsKendoGridEvents.OnHideShowColumns(e);
}
});
kendo.bind($("#DisplayOrderGridContainer"), viewModel);
kendo.addDragAndDropToGrid = function (gridId, rowClass, viewModel) {
if (!gridId) { throw "Parameter [gridId] is not set."; }
if (!rowClass) { throw "Parameter [rowClass] is not set."; }
$(rowClass).kendoDraggable({
hint: function (element) {
var shortName = element[0].cells[0].firstChild.data;
var desc = element[0].cells[1].firstChild.data;
var dispOrder = element[0].cells[2].firstChild.data;
element[0].innerHTML = "<td class=\"text-right dragOver\" style=\"width:95px\">" + shortName + "</td><td class=\"text-right dragOver\" style=\"width:382px\">" + desc + "</td><td class=\"text-right dragOver\" style=\"width:173px\">" + dispOrder + "</td>";
return element;
},
axis: "y",
container: $(gridId)
});
$(gridId).kendoDropTargetArea({
filter: rowClass,
drop: function (e) {
var srcUid = e.draggable.element.data("uid");
var tgtUid = e.dropTarget.data("uid");
var ds = $(gridId).data("kendoGrid").dataSource;
var srcItem = ds.getByUid(srcUid);
var tgtItem = ds.getByUid(tgtUid);
var dstIdx = ds.indexOf(tgtItem);
ds.remove(srcItem);
ds.insert(dstIdx, srcItem);
e.draggable.destroy();
UnsavedWarningsModule.SetUnsavedChanges();
kendo.addDragAndDropToGrid(gridId, rowClass, viewModel);
},
dragenter: function (e) {
e.draggable.hint.css("opacity", 0.3);
},
dragleave: function (e) {
e.draggable.hint.css("opacity", 1);
var srcUid = e.draggable.element.data("uid");
var tgtUid = e.dropTarget.data("uid");
var ds = $(gridId).data("kendoGrid").dataSource;
var srcItem = ds.getByUid(srcUid);
var srcDispOrd = srcItem.Display;
var tgtItem = ds.getByUid(tgtUid);
var tgtDispOrd = tgtItem.Display;
var dstIdx = ds.indexOf(tgtItem);
//--update display orders after dropping
ds._data.forEach(function (data) {
//if dragging it to a spot with higher dispOrder
if (tgtDispOrd > srcDispOrd) {
if (data.Display <= tgtDispOrd && data.Display > srcDispOrd) {
data.Display -= 1;
}
}
//if dragging it to a spot with lower dispOrder
if (srcDispOrd > tgtDispOrd) {
if (data.Display >= tgtDispOrd && data.Display < srcDispOrd) {
data.Display += 1;
}
}
});
srcItem.Display = tgtDispOrd;
//--end
ds.remove(srcItem);
ds.insert(dstIdx, srcItem);
}
});
};
var dataService = (function () {
"use strict";
var self = {};
self.getAddresses = function () {
var data = new kendo.data.ObservableArray([newData]);
// Manual create a promise, so this function mimicks an Ajax call.
var dfd = new $.Deferred();
dfd.resolve(data);
return dfd.promise();
};
return self;
})(kendo);
var controller = (function (dataService, viewModel) {
"use strict";
var _dataService = dataService;
var _vm = viewModel;
var self = {};
self.handleAddressesRefresh = function (data) {
_vm.set("addresses", new kendo.data.DataSource({ data: data }));
kendo.bind($("#KendoGridContainer"), _vm);
kendo.addDragAndDropToGrid("#ChangeDisplayOrderGrid", ".k-master-row", _vm);
};
self.show = function () {
$.when(_dataService.getAddresses())
.then(self.handleAddressesRefresh);
};
return self;
})(dataService, viewModel);
controller.show();});
I think it's something to do with the timing of the loading of the page, possibly with the promise I'm using?
Thanks!

View not rendering after foreach loop MVC

Goal: I am attempting to populate a table based on a dropdown list using Razor syntax to populate my table.
Summary: I am passing the model into my View and looping thru each object within my model. I am able to see the objects populated within the model when debugging the View, however, when the page actually displays, There is nothing in the table and the only thing that is displaying is the dropdown.
Question: What may be the problem with the page rendering?
My view is as follows:
#model IEnumerable<FantasySportsMVC.Models.PlayerDetails>
#{
ViewBag.Title = "Position";
}
<h2>Position</h2>
<body>
<div>
#Html.DropDownList("ddlTournaments",(IEnumerable<SelectListItem>)ViewBag.Tournaments, new { id="ddlTournament", name="ddlTournament"})
</div>
<div>
<input type="button" id="btnGetData" value="Show me some stuff, Yo!" />
</div>
<div id="results">
</div>
<table id="tbDetails">
#if(Model != null)
{
<tbody>
#foreach (var player in Model)
{
<tr>
<td>#player.LastName</td>
<td>#player.FirstName</td>
<td>#player.Position</td>
</tr>
}
</tbody>
}
</table>
</body>
<script type="text/javascript">
function SendTournamentId() {
var data = JSON.stringify({ id : $("#ddlTournament option:selected").val()});
$.ajax({
url: '/Leaderboard/Position',
type: 'POST',
dataType: 'json',
data: data,
contentType: 'application/json; charset=utf-8',
success: function (result) {
//return JSON.stringify($("#ddlTournament option:selected").val());
$("#ddlTournament option:selected").val(result.d.id);
}
});
}
$(function () {
$('#btnGetData').click(SendTournamentId);
});
</script>
My Controller is as follows:
public class LeaderboardController : Controller
{
public ActionResult Position()
{
ViewBag.Tournaments = GetTournamentDetailsSelectList();
return View();
}
[HttpPost]
public ActionResult Position(string id)
{
ViewBag.Tournaments = GetTournamentDetailsSelectList();
var tournamentId = id;
var url = ConstructLeaderboardUrl(tournamentId);
var xmlToJsonUrl = ConvertXmltoJson(url);
List<PlayerDetails> details = BindDataTablePlayerDetails(xmlToJsonUrl);
return View(details);
}
}
private static List<PlayerDetails> BindDataTablePlayerDetails(string url)
{
dtAttributeList = new DataTable();
var details = new List<PlayerDetails>();
try
{
//ConvertXmltoJson(url);
// Construct Datatable
dtAttributeList.Columns.Add("Last Name", typeof(string));
dtAttributeList.Columns.Add("First Name", typeof(string));
dtAttributeList.Columns.Add("Position", typeof(string));
// Add rows to Datatable from Json
for (int i = 0; i < doc.GetElementsByTagName("player").Count; i++)
{
dtAttributeList.Rows.Add(
doc.GetElementsByTagName("player").Item(i).Attributes["last_name"].Value,
doc.GetElementsByTagName("player").Item(i).Attributes["first_name"].Value,
doc.GetElementsByTagName("player").Item(i).Attributes["position"].Value);
}
// Add rows from Datatable to PlayerDetails
foreach (DataRow row in dtAttributeList.Rows)
{
var player = new PlayerDetails();
player.LastName = row["Last Name"].ToString();
player.FirstName = row["First Name"].ToString();
player.Position = row["Position"].ToString();
details.Add(player);
}
}
catch (Exception e)
{
throw new Exception();
}
return details;
}

Paging/Sorting not working on web grid used in Partial View

I have a partial view where I am showing a web grid depending upon a value selected from a page.
For drop down I have used
#Html.DropDownListFor(
x => x.ItemId,
new SelectList(Model.Items, "Value", "Text"),
new {
id = "myddl",
data_url = Url.Action("Foo", "SomeController")
}
)
For drop down item select I have used
$(function() {
$('#myddl').change(function() {
var url = $(this).data('url');
var value = $(this).val();
$('#result').load(url, { value: value })
});
});
and below is my action
public ActionResult Foo(string value)
{
SomeModel model = ...
return PartialView(model);
}
everything works good, but when I try doing a paging or sorting on my webgrid which is on my partial view I am showing a new window with the web grid.
I wanted to be able to sort and page on the same page without postback
Please help
The following example works fine for me.
Model:
public class MyViewModel
{
public string Bar { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Foo(string value)
{
var model = Enumerable.Range(1, 45).Select(x => new MyViewModel
{
Bar = "bar " + value + " " + x
});
return PartialView(model);
}
}
Index.cshtml view:
<script type="text/javascript">
$(function () {
$('#myddl').change(function () {
var url = $(this).data('url');
var value = $(this).val();
$.ajax({
url: url,
type: 'GET',
cache: false,
data: { value: value },
success: function (result) {
$('#result').html(result);
}
});
});
});
</script>
#Html.DropDownList(
"id",
new[] {
new SelectListItem { Value = "val1", Text = "value 1" },
new SelectListItem { Value = "val2", Text = "value 2" },
new SelectListItem { Value = "val3", Text = "value 3" },
},
new {
id = "myddl",
data_url = Url.Action("Foo", "Home")
}
)
<div id="result">
#Html.Action("Foo")
</div>
Foo.cshtml partial:
#model IEnumerable<MyViewModel>
#{
var grid = new WebGrid(
canPage: true,
rowsPerPage: 10,
canSort: true,
ajaxUpdateContainerId: "grid"
);
grid.Bind(Model, rowCount: Model.Count());
grid.Pager(WebGridPagerModes.All);
}
#grid.GetHtml(
htmlAttributes: new { id = "grid" },
columns: grid.Columns(
grid.Column("Bar")
)
)
Notice that I have used a GET request to refresh the grid instead of POST because this way the value query string parameter selected in the dropdown is preserved for future sorting and paging.

changing rally basic dropdown menus after display

I have a rally.sdk.ui.basic.Dropdown menu in a Rally App that I would like to change based on user input. So I will call display() on the dropdown menu, and then later I want to change the contents of that menu.
Has anybody done this? When I try, it crashes. I've also tried calling destroy() on the dropdown menu and then calling display() on a new menu that I allocate, but that crashes as well with an obscure dojo error.
Any suggestions on how to change dropdown menus on the fly?
I've included a very stripped down example below of trying to destroy and then re-display the menu:
<html>
<head>
<title>Incoming Defects by Severity</title>
<meta http-equiv="X-UA-Compatible" content="IE=7" >
<meta name="Name" content="Defects by Severity" />
<script type="text/javascript" src="https://rally1.rallydev.com/apps/1.29/sdk.js"></script>
<script type="text/javascript">
function DefectChart() {
this.display = function() {
var defectVersionDropdown;
var count = 1;
function makeDefectChart(results){
initDefectVersionDropdown();
};
function renderPage() {
var queryConfig = [];
var startDate = '2011-06-06';
var endDate = '2012-02-02';
var queryArray = ['CreatedDate >= "' + startDate + '"', 'CreatedDate <= "' + endDate + '"'];
var versionFilter = defectVersionDropdown ? defectVersionDropdown.getDisplayedValue() : 'ALL';
if (versionFilter != 'ALL') {
queryArray.push('FoundInBuild contains "' + versionFilter + '"');
}
// console.log(queryArray);
queryConfig.push({
type : 'Defects',
key : 'defects',
query: rally.sdk.util.Query.and(queryArray),
fetch: 'Severity,State,LastUpdateDate,CreationDate,OpenedDate,AcceptedDate,LastUpdateDate,ClosedDate,Environment,FoundInBuild'
});
rallyDataSource.findAll(queryConfig, makeDefectChart);
}
function defectVersionChange(sender, eventArgs) {
var version = eventArgs.value;
renderPage();
}
function initDefectVersionDropdown() {
if (defectVersionDropdown != null) {
defectVersionDropdown.destroy();
defectVersionDropdown = null;
}
if (defectVersionDropdown == null) {
console.log('initDefectVersionDropdown');
count++;
var menuItems = [{label: "ALL", value: "ALL"}];
for (var i=0; i < count; i++) {
menuItems.push({label: count, value: count});
}
var config = {
label: "Found In Version:",
items: menuItems
};
defectVersionDropdown = new rally.sdk.ui.basic.Dropdown(config);
defectVersionDropdown.addEventListener("onChange", defectVersionChange);
defectVersionDropdown.display("defectVersionDiv");
}
}
var workspaceOid = '__WORKSPACE_OID__'; if (workspaceOid.toString().match(/__/)) { workspaceOid = XXX; }
var projectOid = '__PROJECT_OID__'; if (projectOid.toString().match(/__/)) { projectOid = XXX; }
rallyDataSource = new rally.sdk.data.RallyDataSource( workspaceOid,
projectOid,
'__PROJECT_SCOPING_UP__',
'__PROJECT_SCOPING_DOWN__');
initDefectVersionDropdown();
renderPage();
}
}
function getDataAndShow() {
var defectChart = new DefectChart();
defectChart.display();
}
function loadRally() {
rally.addOnLoad(getDataAndShow);
}
loadRally();
</script>
</head>
<body>
<div id="defectVersionDiv"></div>
</body>
</html>
Destroying the old one creating and displaying a new one is the correct way to do this. This is a common pattern when developing apps using the App SDK. If you provide a code snipped along with the dojo error you are getting the community can probably be of better assistance.

Resources