Grid
Awesome Grid is declared using the
Html.Awe().Grid
helper, its columns can be bound to one or multiple properties and sub-properties of the model, it has sorting, grouping, header and footer aggregates, paging and client side api. It can also be bound to parents ( used for filtering ) and parameters ca be set as well.
Data binding
It needs an url specified to get data from, if not specified by default convention it will look for a controller with the same name as it + "Grid"/GetItems, example:
@Html.Awe().Grid("DinnerGrid").Columns(...) // gets data from DinnerGrid/GetItems
@Html.Awe().Grid("MyGrid")
.Url(Url.Action("GetItems", "LunchGrid")) // gets data from LunchGrid/GetItems
.Columns(...)
the url has to return a Json made from the result of
GridModelBuilder<TModel>().Build()
, example:
public class LunchGridController : Controller
{
public ActionResult GetItems(GridParams g)
{
var list = Db.Lunches.AsQueryable();
return Json(new GridModelBuilder<Lunch>(list, g)
{
Key = "Id", // needed for Entity Framework, nesting, tree, grid api (select, update)
// EF can't do paging unless it orders at least by one column so we will order by that column when there is no sorted columns
}.Build());
}
}
The model defined in
GridModelBuilder<Model>
is the one that is used for databinding, the
Column.Bind
has to be defined based on the Model's properties, like this:
.Columns(
new Column{ Bind = "Id" }, // bind to Model.Id
new Column{ Bind = "Location" }, // bind to Model.Location
new Column{ Bind = "Country.Name" } // bind to Model.Country.Name
new Column{ Bind = "Person.FName,Person.LName" } // bind to Model.Person.FName and Model.Person.LName
/* when sorting/grouping the FName will have a +1 higher rank than LName*/
new Column{ Bind = "Code1,Code2" } // bind to Model.Code1 and Model.Code2
the grid uses DynamicLinq so with EntityFramework sorting by a column that is bound to
Person.FName,Person.LName
would be like calling
.OrderBy(o => o.Person.FName).ThenBy(o => o.Person.LName)
, unless you're using custom querying, in that case you would handle this yourself.
Map
The model that is displayed and the model that the columns are bound to don't have to be the same, you can define a
Map
function that will transform the grid
querying model
(the generic type defined in
GridModelBuilder<T>
) into the a
display model
(the type returned by
Map
), this can be useful in many cases:
- to generate a model property based on other properties and parameters
- when a
Column
is bound to multiple or nested model properties
- to optimise/reduce the amount of data being sent to the client (send only what you need to display/or use in js)
- to avoid the circular reference error when serialising the model to json
here's an example:
return Json(new GridModelBuilder<Lunch>(list, g)
{
Map = o => new
{
o.Id // grid key
CountryName = o.Country.Name, // => Bind = "Country.Name" and ClientFormat = ".(CountryName)"
ChefName = o.Chef.FirstName + " " + o.Chef.LastName // here ClientFormat = ".(ChefName)" (value displayed), but Bind = "Chef.FirstName,Chef.LastName"
// so the sorting/grouping on this column is bound to Chef.FirstName and Chef.LastName
o.Person // this can be just Bind="Person"
}
}.Build());
Custom Formatting
By default the cell content will show the value of the model property defined in
Column.Bind
, but that can be changed by specifying:
ClientFormat
- format defined as a string which can include row model properties defined like this:
.(Prop1)
ClietnFormatFunc
- define a js function that will receive a the row model and
Bind
value as parameters, and must return a string which will be used a value for the cell. The result of the javascript function
will not be encoded.
examples:
// 123 USD | .(Cost) means Cost property value from the display model (usually the model returned by Map)
new Column{ Bind = "Cost", ClientFormat = ".(Cost) USD" },
// Adam spent 123
new Column{ Bind = "Person", ClientFormat = ".(Person) spent .(Cost) $" },
// will call formatPrice(model, "Price"), result will be <div style='color:red;'>123 £ </div>
new Column{ Bind = "Price", ClientFormatFunc = "formatPrice"},
...
<script type="text/javascript">
function formatPrice(lunch) {
var color = 'blue';
if (lunch.Price < 20) color = 'green';
if (lunch.Price > 50) color = 'red';
return "<div style='color:" + color + ";'>" + lunch.Price + " USD </div>";
}
</script>
Row class
A css class can be set to individual rows using
.RowClassClientFormat
, here's an example:
@Html.Grid("Grid1").RowClassClientFormat(".RowClass")...
and in the controller have this:
return Json(new GridModelBuilder<Lunch>(Db.Lunches.AsQueryable(), g)
{
Map = o => new
{
...
RowClass = o.Price > 90 ? "pinkb" : o.Price < 30 ? "greenb" : string.Empty
}
In this example the model will have the
RowClass
property which will have a different value depending on the Price property and the RowClassClientFormat will use that value to set the css class for the row.
Encoding
By default cell content is html encoded, but there are exceptions.
The result of the
ClientFormtFunc
js function is not encoded, you can also set
.Encode(false)
to disable html encoding for the whole grid.
Persistence
Enable the persistence using this extension:
.Persistence
- will save the state of the grid (columns, current page, collapsed groups).
There are 3 types of persistence:
View
- data will be lost after refresh, go to another page
Session
- data will be lost after closing the browser (uses sessionStorage)
Local
- data will be persisted even after closing the browser (uses localStorage)
When using it
make sure you don't have 2 grids with the same persistence key , persistence key is equal to grid's name by default, but you can set it using
.PersistenceKey
All keys used for saving are prefixed by the
awe.ppk
string, so when you're deploying a new version you can modify the
awe.ppk
string.
Right now the
aweUtils.init
method will remove all localStorage keys that start with "awe" and don't start with the value of
awe.ppk
(`awe.ppk` by default is set to "awe" + number), so you could do this:
awe.ppk += "myapp" + 5; // 5 is version, modify it on each release
aweUtils.init(...
You only need to do this (set awe.ppk) if you're using Local or Session persistence.
Sorting
The grid is sortable by default, this can be changed by setting the
Sortable
for the grid or for a column:
Column { Bind = "Id", Sortable = false }
@Html.Grid("MyGrid").Sortable(false) // Column.Sortable overrides this value
for single column sorting use the extension
SingleColumnSort
:
@Html.Grid("MyGrid").SingleColumnSort(true)
other sorting related properties of the column:
Column.Sort
- initial sorting for this column (None | Asc | Desc)
Column.SortRank
- initial sort rank/priority for this column
Default Key sorting
When setting
GridModelBuilder.Key
or
KeyProp
the data by default will be sorted by the
Key
, this is because the data needs to always be sorted by at least 1 column before doing paging (Take/Skip),
you can change this by setting
GridModelBuilder.DefaultKeySort
, example:
new GridModelBuilder<Meal>(...) { Key = "Id", DefaultKeySort = Sort.None }
Grouping
Just like with sorting, all the properties ( that have a Bind defined ) are groupable by default, but this can be changed by setting Column properties
Column.Group
- defines whether initially the column is grouped
Column.GroupRank
- group rank for this column
Column.GroupRemovable
- (default is true) if set to false grouped column won't have the remove group button
Aggregates
You can put aggregates in the header and footer of the groups, to do that you have to define functions for the MakeHeader and MakeFooter properties of the GridModelBuilder:
public class GroupingGridController : Controller
{
public ActionResult GetItems(GridParams g)
{
//passing 2 Functions MakeFooter and MakeHeader
return Json(new GridModelBuilder<Lunch>(Db.Lunches.AsQueryable(), g)
{
MakeFooter = MakeFooter,
MakeHeader = gr =>
{
//get first item in the group
var first = gr.Items.First();
//get the grouped column value for the first item
var val = AweUtil.GetColumnValue(gr.Column, first).Single();
return new GroupHeader
{
Content = string.Format(" {0} : {1} ( Count = {2}, Max Price = {3} )",
gr.Header, val, gr.Items.Count(), gr.Items.Max(o => o.Price)),
Collapsed = true // making groups collapsed by default
};
}
}.Build());
}
private object MakeFooter(GroupInfo<Lunch> info)
{
//will add the word Total at the grid level footer (Level == 0)
var pref = info.Level == 0 ? "Total " : "";
return new
{
Food = pref + " count = " + info.Items.Count(),
Location = info.Items.Select(o => o.Location).Distinct().Count() + " distinct locations",
Date = pref + " max: " + info.Items.Max(o => o.Date).Date.ToShortDateString(),
Price = info.Items.Sum(o => o.Price)
};
}
}
Api
Access the api by calling :
var api = $('#gridId').data('api');
api.load({
group:[columnName,..],
sort: [{Prop: columnName, Sort: direction }, ..],// direction 0 = none, 1 = asc, 2 = desc
params: {PropName:PropValue, ..},// params will persist until api with params: {} is called
oparams: {PropName:PropValue, ..} // oparams - one time params, sent only when the api is called with it
})
// all properties( group, sort, params, oparams) are optional, specifying a property will override its previous value
api.reset() // reset will bring the grid back to the initial state defined in the markup
api.getRequest() // get last request
api.getResult() // get last result
api.clearPersist() // clear grid persistence data
api.persist() // persist grid state
api.render() // render grid using last successful result
api.select(key) // select grid row(s) by key (multiple rows can be selected in case of the treeGrid), returns an array of jQuery objects
api.update(key[, params]) // update item by key, additional params can be sent
api.lay() // adjust grid layout
api.getSelection() // get selected rows models
api.nestOpen($row, nestName) // open row nest
api.nestClose($row, nestName) // close row nest
api.model($row) // get row model
api.inlineSave($row) // inline save grid row
api.batchSave($rows) // inline batch save, when $rows not specified all rows in edit mode will be saved
api.inlineCancel($row) // cancel inline edit row
api.inlineCancelAll() // cancel all inline edit rows
api.inlineEdit($row) // inline edit row
// grid configuration can also be accessed via data('o');
Events
awebeginload | begin loading, before sending request |
aweload | grid was loaded |
awecolresize | resizing grid column |
awecolresizeend | finished resizing column |
awerender | grid rendered |
awebfren | before grid render |
awereorder | column reordered |
aweupdate | grid row updated |
awenestload | grid nest loaded |
aweinlinecancel | inline edit grid row canceled |
aweinlineedit | grid row entered inline edit mode |
aweinlinesave | inline edit grid row saved |
example:
$('#Grid1').on('aweload', function (event, data, requestData) {
console.log('aweload handled', data, requestData);
}).on('awebeginload', function (event, requestData) {
console.log('awebeginload handled', data, requestData);
});
Extensions
Url | Specify the url from where to load data from |
Height | Set height of the grid, when set the grid will be scrollable. When not specified (or zero) the grid will automatically scale it's height |
Columns | Specify the columns of the Grid |
PageSize | Set the page size |
ShowGroupedColumn | Show the columns that are grouped by (default true) |
SingleColumnSort | enables sorting by one column at a time |
Persistence | makes the grid save it's state ( collapsed groups, current page, sorting, grouping), it can be None - no persistence, View - view context (will loose state on page refresh); Session - using HTML sessionStorage (will loose state on closing browser); Local - using HTML localStorage ( state will be persisted even after the browser is closed) |
Prefix | Set Prefix for the html id (use to achieve unique ids for elements with same name) |
Column properties
Bind | used to bind this column to the model, it can be bound to one or more properties and subproperties in the model, examples: "FirstName", "FirstName,LastName", "Country.Name", "Chef.FirstName, Chef.LastName" |
Group | defines whether initially the column is grouped |
GroupRank | group rank for this column |
GroupRemovable | (default is true) if set to false grouped column won't have the remove group button |
Sort | initial sorting for this column (None | Asc | Desc) |
SortRank | initial sort rank for this column |
ClientFormat | Client format for the column defined as a string using .(ModelPropertyName) for including values of the row model |
ClientFormatFunc | Defines the Name of a javascript function that will receive as a parameter the model (or mapped model) of the grid row and must return a string which will be used a value for the cell |
GridModelBuilder
It is used to build the model of the grid by sorting, paging, grouping the items and building the model.
Map | defines a function that maps the Model to a Dto, this way Column.Name will still be bound the the Model's properties but for cell values the values from the Dto will be used |
Key | defines a property on which the data will be sorted when there's no sorting, this is needed when using Entity Framework because it does not allow paging if the data is not ordered at least by one column |
MakeHeader | defines a function for creating the GroupHeader |
MakeFooter | function for creating a group footer |