问题
I am relatively new to MVC
(and Kendo
) but I have managed to setup a grid which contains multi column headers using the columns.Group
functionality.
@(Html.Kendo().Grid<Result>()
.Name("myGrid")
.Columns(columns =>
{
columns.Bound(c => c.ResultDateTime).Title("Date Time");
foreach (Lot Lot in (Lot[])ViewBag.Lots)
{
columns.Group(group => group
.Title(Lot.LotNumber)
.Columns(info =>
{
info.Bound(Lot.Result.Count.ToString());
info.Bound(Lot.Result.Mean.ToString());
info.Bound(Lot.Result.SD.ToString());
}));
}
columns.Bound(c => c.StandardComment.Description).Title("Comment");
columns.Bound(c => c.ReviewComment.Description).Title("Review Comment");
columns.Command(command => { command.Destroy(); });
})
.Editable(editable => editable
.Mode(GridEditMode.InLine)
.DisplayDeleteConfirmation(true))
.Pageable()
.Navigatable()
.Sortable()
.Groupable()
.Scrollable()
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.PageSize(20)
.ServerOperation(false)
.Events(events => events.Error("error_handler"))
.Read("ResultsAsync_Read", "ResultEntry")
.Destroy("ResultsAsync_Destroy", "ResultEntry")
)
)
As you can see I am trying to build the columns dynamically based on an array which has been setup and passed using ViewBag
.
Controller async function which sets up ViewBag.Lots
extracting the array from the top level IEnumerable<Result>
object:
public async Task<ActionResult> ResultsAsync_Read([DataSourceRequest]DataSourceRequest request)
{
IEnumerable<Result> controlSets = await _manager.ReadAsync(test);
ViewBag.Lots = controlSets.Select(x => x.LotResults);
return Json(controlSets.ToDataSourceResult(request));
}
When I run this, I get the following error when trying to access the ViewBag.Lots
attribute in the foreach
.
NullReferenceException: Object reference not set to an instance of an object.
Does someone know why I am getting this error and also if there is a more efficient way to achieve my goal?
EDIT:
Instead of using the ViewBag to hold the list of Lot
objects, I am using it to hold the maximum number of lots available in the entire List<Result>
s.
I have taken @ken2k's advice onboard and done this within the Index()
function of the Controller:
public async Task<IActionResult> Index()
{
QCTest test = new Models.Acusera.QCTest();
test.TestID = 3;
IEnumerable<Result> controlSets = await _manager.ReadAsync(test);
ViewBag.MaxLots = controlSets.Max(x => x.LotResults.Count);
return View("~/Views/Acusera/DataEntry/ResultEntry.cshtml");
}
Then I loop over the maximum number of lots available and create the columns required:
.Columns(columns =>
{
columns.Bound(c => c.ResultDateTime).Title("Date Time");
for (int i = 0; i < ViewBag.MaxLots; ++i)
{
columns.Group(group => group
.Title("Test")
.Columns(info =>
{
info.Bound(x => x.LotResults[i].Result.Count);
info.Bound(x => x.LotResults[i].Result.Mean);
info.Bound(x => x.LotResults[i].Result.SD);
}));
}
columns.Bound(c => c.StandardComment.Description).Title("Comment");
columns.Bound(c => c.ReviewComment.Description).Title("Review Comment");
columns.Command(command => { command.Destroy(); });
})
This results in a grid displaying like so:
So I have managed to create the number of multi-header columns required to display the data. However, I am now getting an error:
Uncaught TypeError: Cannot read property 'Result' of undefined
回答1:
Your ResultsAsync_Read
method is an async method that will be called by the Kendo framework from a javascript AJAX call, i.e. after your page has been loaded and rendered.
This means that when your page is rendered, ViewBag.Lots
is actually null, which throw the exception.
What you need is to initialize this value when you load the page, not inside your ResultsAsync_Read
method. Basically:
public async Task<ActionResult> Index()
{
// Gets the values BEFORE rendering the view
IEnumerable<Result> controlSets = await _manager.ReadAsync(test);
// The ViewBag property will be available from the Razor view
ViewBag.Lots = controlSets.Select(x => x.LotResults);
// Returns the view that display the grid
return this.View();
}
It's important to remember how the MVC stuff actually works. Basically the steps are:
- the server receives a request on the .../Index route
- the server executes the Index() action
- as the action returns this.View() (which would be equivalent to this.View("Index")), it renders the Index.cshtml razor view (that means the ViewBag must be not null here)
- if later you do AJAX calls such as
ResultsAsync_Read
, changing the ViewBag won't have any effect as the page is already rendered. The only thing you could do to modify your page, is to return some JSON, and change the DOM based on the JSON result from inside your AJAX callback (i.e. using jQuery/javascript).
来源:https://stackoverflow.com/questions/38631743/kendo-grid-multi-column-group-based-on-object-array