Telerik MVC Chart ClientEvents.OnDataBinding - asp.net-mvc

I want to show a poll result for a specific poll question.
When question list clicked i want to bind my chart with a query according to selected questionId.
So my plan was;
1. get questionId from selected question row. it's ok.
defining ClientEvents.OndataBinding event on my chart. So i would be able to
pass questionId with;
function onChartDataBinding(e) {
e.data = $.extend(e.data, { questionId: questionId });
}
using $('#ChartPollResults').data('tChart').rebind(); on question list grid row selected event.
This scenario works when i have second grid binding according to first grids selected row.
But it seems there is no ClientEvents.OnDataBinding event on chart control.
And "rebind()" method isn't supported on Chart control.
The chart conrol i use is below.
#(Html.Telerik().Chart<QuestionResult>()
.Theme("WebBlue")
.Name("ChartPollResults")
.Title("Poll Question Choice Number vs. Choice Count")
.Legend(legend => legend.Position(ChartLegendPosition.Bottom))
.Series(series =>
{
series.Bar("ChoseCount").Name("Choice Count").Gap(5);
})
.CategoryAxis(axis => axis.Categories(o => o.ChoiceNumber))
.DataBinding(dataBinding => dataBinding.Ajax().Select("_PollResultChartBinding", "Poll", new { questionId = 0 }))
.HtmlAttributes(new { style = "width: %100px; height: 270px" })
)
My Controller binding method;
public ActionResult _PollResultChartBinding(int questionId = 0)
{
//questionId = 3;
if (!ModelState.IsValid || questionId == 0)
return Json(new List<QuestionResult>());
PollQuestionDefinition pollQuestion = service.Get(questionId);
List<PollAnswer> pollAnswers = service.GetPollAnswersByQuestion(questionId);
PollQuestionResultUI result = new PollQuestionResultUI(pollQuestion, pollAnswers);
return Json(result.Results);
}
When i comment out //questionId = 3; line i can see the results for the question wiht Id= 3 in chart with no problem.
But i can't pass questionId to chart.
Thanks in advance.

This is my Opinion for your source code should be like this:
//controller binding method
public ActionResult _PollResultChartBinding(string questionId)
{
//questionId = 3;
int _questionId = String.IsNullOrEmpty(questionId) ? 0 : Convert.ToInt32(questionId);
if (_questionId == 0)
return Json(new List<QuestionResult>());
PollQuestionDefinition pollQuestion = service.Get(_questionId);
List<PollAnswer> pollAnswers = service.GetPollAnswersByQuestion(_questionId );
PollQuestionResultUI result = new PollQuestionResultUI(pollQuestion, pollAnswers);
return Json(result.Results);
}
**in your view, there is no problem with the code**.
but, for rebind that chart, this is the code :
example : (running from developer console for IE or firebug for firefox browser)
var chartPollResult = $('#ChartPollResults').data('tChart');
chartPollResult.rebind({ questionId: "0"});
but if you want make it into function, code should be like this:
function rebindChartPollResult(e, param) {
e.data('tChart').rebind({ questionId: param});
}
calling method :
rebindChartPollResult($('#ChartPollResults'), "0");
reference for telerik chart ajax binding (not included how to rebind chart) :
http://demos.telerik.com/aspnet-mvc/chart/ajaxbinding

First of all, one thing I immediately noticed is that you set questionId equal to 0 in your ActionResult parameters. I actually just modified a Chart I had up and running to pass in
new { questionID = 0}
as an additional parameter to my Ajax select statement and it passed in fine.
As for passing the parameter you could consider using a POST to the server on Grid selection and pass the parameter that way. I know it might not be ideal here, but you could theoretically either populate the Chart in that post, or just set a ViewData entry to contain the particular questionID that you are looking for.
I also noticed that you submitted this to the Telerik forums and from the response there it would seem that the above approach might actually work pretty well, or you could use the approach mentioned there (partial view with ajax calls).

Related

MVC 4 #HTML.DropdownlistFor onchange event needs to send SelectedIndex value

I am using MVC 4 and have an #HTML.DropdownlistFor() which loads the rest of the page based on it's selection. On change of the listbox, I want to reload the page so that it load the correct data. As well there are two other parameters that are needed to load the page correctly.
In Create.cshtml view
#Html.DropDownListFor(model => model.SelectedMissionID, Model.MissionsToDisplay, new { onchange = "ReloadPage();" })
function ReloadPage() {
window.location = '#Html.Raw(Url.Action("CreateEdit", "DailyLog", new { ID = 1, ID2 = 0, dailyLogDate = Model.LogDate }))';
}
Where it says ID = 1, I want the selected index of the DropdownlistFor(SelectedMissionID), how do I get this?
I'm not sure if this is the best way to go to the controller, or if I should use a JQUERY .ajax post, is there a better way?
I seems something ideal for an ajax post.
What you have done is OK, inside the ReloadPage() function, just get the selected value in the DropDown, do the Ajax call passing the parameters, and in the success callback put your returned HTML in some container.
The idea is that you replace a part of your view with the return of your Ajax call, not all the page.
To get the selected value, using jQuery:
var selectedValue = $("select[name='SelectedMissionID']").val();
And that can be placed in your ReloadPage() function.
$.post( "your action URL", { ID: selectedValue, OtherParam: "xxxxx" })
.done(function( data ) {
alert( "Data Loaded: " + data );
//here you need to get the HTML, and put in a container
});

Vaadin Grid Row Index

In a vaadin table if we do
table.setRowHeaderMode(RowHeaderMode.INDEX);
we get a column with the row index.
Is it possible to to the same with a vaadin grid?
So far I haven't seen such an option, but you should be able to fake it with a generated column. Please see below a naive implementation which should get you started (improvements and suggestions are more than welcome):
// our grid with a bean item container
Grid grid = new Grid();
BeanItemContainer<Person> container = new BeanItemContainer<>(Person.class);
// wrap the bean item container so we can generated a fake header column
GeneratedPropertyContainer wrappingContainer = new GeneratedPropertyContainer(container);
wrappingContainer.addGeneratedProperty("rowHeader", new PropertyValueGenerator<Long>() {
private long index = 0;
#Override
public Long getValue(Item item, Object itemId, Object propertyId) {
return index++;
}
#Override
public Class<Long> getType() {
return Long.class;
}
});
// assign the data source to the grid and set desired column order
grid.setContainerDataSource(wrappingContainer);
grid.setColumnOrder("rowHeader", "name", "surname");
// tweak it a bit - definitely needs more tweaking
grid.getColumn("rowHeader").setHeaderCaption("").setHidable(false).setEditable(false).setResizable(false).setWidth(30);
// freeze the fake header column to prevent it from scrolling horizontally
grid.setFrozenColumnCount(1);
// add dummy data
layout.addComponent(grid);
for (int i = 0; i < 20 ; i++) {
container.addBean(new Person("person " + i, "surname " + i));
}
This will generate something similar to the image below:
There is a Grid Renderer that can be used to do this now. It is in the grid renderers add-on https://vaadin.com/directory/component/grid-renderers-collection-for-vaadin7. It is compatible with Vaadin 8 as well.
Here is how it could be used (there are a few different options for how to render the index).
grid.addColumn(value -> "", new RowIndexRenderer()).setCaption("Row index");
Worth to mention that I use the following with Vaadin 18 flow and works perfectly.
grid.addColumn(TemplateRenderer.of("[[index]]")).setHeader("#");
Ok, it took me more than a while to figure this out. I don't know why you need this, but if your purpose is to find which grid row was clicked, then you can get the index from the datasource of your control via the itemClick event of your listener.
In my case, my datasource is an SQLContainer, and I already had it available (see ds var) so I did it this way:
grid.addListener(new ItemClickEvent.ItemClickListener() {
#Override
public void itemClick(ItemClickEvent event) {
Object itemId = event.getItemId();
int indexOfRow = ds.indexOfId(itemId);
}
});
You usually add a datasource to your control when you initialize it, via constructor or by setting the property. If you got you Grid from somewhere with an already-attached datasource, you can always get it with something like this:
SQLContainer ds = (SQLContainer)gred.getContainerDataSource();
I use this trick:
int i = 0;
grid.addComponentColumn(object -> {
i++;
return new Label("" + i);
}).setCaption("");

Change the Telerik DateTimePicker element ID, but keep the name

I have a ASP.NET MVC page with multiple forms on it, where each form edits a different instance of the same class. Each form also includes a Telerik DateTimePicker control. What I want is to have the element IDs change, but not the element name. So I have something like this:
string idString = string.Format("MyObject{0}", Model.ID)
#Html.Telerik().DatePickerFor(m => m.SomeDate).HtmlAttributes(new { id = idString + "-SomeDate" })
Now this works mostly fine, except that at the bottom of the page, the auto-generated Javascript that Telerik puts in looks like:
jQuery('#SomeDate').tDateTimePicker({
format:'M/d/yyyy h:mm tt',
minValue:new Date(1899,11,31,0,0,0,0),
maxValue:new Date(2100,0,1,0,0,0,0),
startTimeValue:new Date(2013,3,22,0,0,0,0),
endTimeValue:new Date(2013,3,22,0,0,0,0),
interval:30,
selectedValue:new Date(2013,3,22,11,9,1,180)
});
Note that my idString value didn't get put in. I can try:
#Html.Telerik().DatePickerFor(m => m.SomeDate).Name(idString + "SomeDate")
And this makes the auto-generated Javascript correct (jQuery('#MyObject123-SomeDate')) but now the element name is wrong, so TryUpdateModel fails.
How can I get this to work? Thanks.
EDIT: I hacked this using the following Javascript, but this is a kludgy way to fix this.
$(function () {
window.setTimeout(function () {
for (var i = 0; i < document.getElementsByTagName("input").length; i++) {
var obj = document.getElementsByTagName("input")[i];
if (obj.id.indexOf("SomeDate") == -1)
continue;
obj.name = "SomeDate";
}
}, 150)
});
The solution was to use the Telerik object's .InputHtmlAttributes method.
#Html.Telerik().DatePickerFor(m => m.SomeDate).InputHtmlAttributes(new { id = idString + "-SomeDate" })

missunderstanding mvc default binding

I have multiselect jquery plagin (Choosen) and when I use it in 'Multiple Select' mode I expect in controller next values:
posted string = 'value1,value2...'
really have
posted string = 'value2'
only if I reffer directly to FormCollection I'll get expected values as below:
[HttpPost]
public ActionResult TagSearech(/*string tagSelect*/FormCollection c)
{
// only one value here
// string[] names = tagSelect.Split(',');
// as expected: value1,....
string expectedValue = c['tagSelect'];
return View();
}
I cant understand what might cause this behavior.
EDIT
Here is View:
#using (Html.BeginForm("TagSearech", "Tag"))
{
#Html.DropDownList("tagSelect", Model, new { #class = "chzn-select", data_placeholder = "tag names", multiple = "" })
<input type="submit"/>
}
MVC will attempt to bind the input data on the URL into the model. I haven't seen how Chosen.js posts the data back to the server, but essentially its coming in in the wrong format, so MVC binds the first element it sees to the string Model.
The FormsCollection retrieves all of the data that was posted in the URL, which is why all of your selected values can be seen there.
Did you try changing the incoming model from string to string[], and see if all of the items are bound to the array?

jqgrid + EF + MVC: Is it possible to export in excel, using always the same controller action?

I am using jqgrid (standard) with EF 4 + MVC3. I'd like to implement excel export and if possible using the same action controller used to populate the grid.
I wonder if is it possible / logical to pass an additional parameter, for example. Which method you would suggest me?
I ask this question because I am still approaching to implement excel export and I'd like to optimize / re-use code, if possible.
To generate excel, I'd like to use this library by Dr Stephen Walther, which has three types of output and allows to define headers too. Please tell me if you find it valid for my purpose.
About the jqgrid code, I found this interesting answer by Oleg, but I do not understand if could be applied to my needs.
Unfortunately, by now I only found parts of solutions for excel export with EF MVC, but no solution or complete examples...
Here's the _Index partial view containing my jqgrid
<table id="mygrid"></table>
<div id="pager2"></div>
jQuery("#mygrid").jqGrid({
url:'controller/jqIndex',
datatype: "json",
colNames:['id','field1', ...],
colModel:[
{name:'id',index:'id', width:55},
{name:'field1',index:'field1', width:90},
...
],
rowNum:10,
rowList:[10,20,30],
pager: '#pager2',
sortname: 'id',
viewrecords: true,
sortorder: "desc",
caption:"modal jquery + jqgrid test"});
jQuery("#list2").jqGrid('navGrid','#pager2',{edit:false,add:false,del:false});
//TODO
???
...some code to call the controller action with the `excel` parameter set `true`
CONTROLLER (BASED ON OLEG'S IMPLEMENTATION)
public ActionResult jqIndex(string sidx, string sord, int page, int rows, bool _search, string filters, bool excel) // note the excel parameter <<
{
var context = new TManagerContext();
var objectContext = context.ObjectContext();
var set = objectContext.CreateObjectSet<Ticket>();
var serializer = new JavaScriptSerializer();
Filters f = (!_search || string.IsNullOrEmpty(filters)) ? null : serializer.Deserialize<Filters>(filters);
ObjectQuery<Ticket> filteredQuery = (f == null ? (set) : f.FilterObjectSet(set));
filteredQuery.MergeOption = MergeOption.NoTracking; // we don't want to update the data
int totalRecords = filteredQuery.Count();
var pagedQuery = filteredQuery.Skip("it." + sidx + " " + sord, "#skip",
new ObjectParameter("skip", (page - 1) * rows))
.Top("#limit", new ObjectParameter("limit", rows));
int pageIndex = Convert.ToInt32(page) - 1;
int pageSize = rows;
int totalPages = (int)Math.Ceiling((float)totalRecords / (float)pageSize);
var queryDetails = (from e in pagedQuery
select new
{
e.TicketID,
e.field1,
...
}).ToList();
var result = new
{
total = totalPages,
page = page,
records = totalRecords,
rows = (from e in queryDetails
select new
{
id = e.TicketID,
cell = new string[]
{
e.field1,
...
}
}).ToArray()
};
if (excel) {
ExportExcel(result); // if possible, pass filter parameters too, column order, etc...
}
return Json(result, JsonRequestBehavior.AllowGet);
}
Please sorry if the question could be silly, I am just a (enthusiast) beginner.
Thanks for your precious help!
Best Regards
Larry - A few comments.
You shouldn't be doing that much logic in your controller. Move all
of that business logic to another class/service. Then your action
method would be just a few lines. A quick example
public JsonResult jqIndex(string sidx, string sord, int page, int rows,
bool _search, string filters){
return JSON(this.GridQueryService.GetJQGrid(sidx,sord,page,rows,_search,filters), JsosnRequestBehavior.AllowGet);
}
2.I know you don't want to repeat code (which point 1 helps) but there are many parameters and things here that simply do not apply to Excel (page, rows).
3.Passing boolean parameters to change how things function can get messy fast. Lets assume that you now need to pass more/less data to the Excel file, now you have nested conditions all over the place and Unit Testing would just be crappy.
4.An excel action method will should have a FileResult return type, not a
JSON result (I guess they are all action results, but this makes your intention all the more clear in your code. Your definition should be something like
public FileResult GetExcelFile(string sidx, string sord, bool _search,
string filters){
//do stuff to return Excel
}
If you create your Service in point one in such a way that you have two methods that return different items, but share a common query/search base function, then you are really staying Dry while following the Single Responsibility Principle. An example of this service might be (very rough example, should give you some things to think about):
public class GridQueryService{
public YourViewModel GetJQGrid(sidx, page, row, _search, filters){
//Get the base data
var myData = this.GetGridData(sidx, _search, filters);
//Create your view model and return it back to controller
}
public StreamWriter GetExcelFIle(sidx, _search, filters){
//Get the base data
var myData = this.GetGridData(sidx, _search, filters);
//Create your Excel file and return it to the controller
}
private ObjectQuery<Ticket> GetGridData(string sidx, bool _search, string filters){
//do your data grabbing here - you never return the raw data back to anything outside
//of this service, so it should be ok to make private
}
}

Resources