FlexGrid AJAX Data Binding in ASP.NET MVC
FlexGrid for ASP.NET MVC provides multiple options for data binding. You can bind the grid to a Model object or to JSON data via a remote action. Sometimes, because of application needs, users have to bind the grid at client side with data returned by an AJAX call.
Create ASP.NET MVC5 Project for AJAX Data Binding
Inside Visual Studio, use the "C1 ASP.NET MVC5 Web Application" template to create a new project. This adds all the relevant dlls, resources, namespaces and license information to the project automatically.
Create the Model
Add a model class to the project. In this example we'll consider the Sale class:
public class Sale
{
public int ID { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public string Country { get; set; }
public string Product { get; set; }
public string Color { get; set; }
public double Amount { get; set; }
public double Amount2 { get; set; }
public double Discount { get; set; }
public bool Active { get; set; }
public MonthData[] Trends { get; set; }
public int Rank { get; set; }
private static List<string> COUNTRIES = new List<string> { "US", "UK", "Canada", "Japan", "China", "France", "German", "Italy", "Korea", "Australia" };
private static List<string> PRODUCTS = new List<string> { "Widget", "Gadget", "Doohickey" };
/// <summary>
/// Get the data.
/// </summary>
/// <param name="total"></param>
/// <returns></returns>
public static IEnumerable<Sale> GetData(int total)
{
var colors = new[] { "Black", "White", "Red", "Green", "Blue" };
var rand = new Random(0);
var dt = DateTime.Now;
var list = Enumerable.Range(0, total).Select(i =>
{
var country = COUNTRIES[rand.Next(0, COUNTRIES.Count - 1)];
var product = PRODUCTS[rand.Next(0, PRODUCTS.Count - 1)];
var color = colors[rand.Next(0, colors.Length - 1)];
var startDate = new DateTime(dt.Year, i % 12 + 1, 25);
var endDate = new DateTime(dt.Year, i % 12 + 1, 25, i % 24, i % 60, i % 60);
return new Sale
{
ID = i + 1,
Start = startDate,
End = endDate,
Country = country,
Product = product,
Color = color,
Amount = Math.Round(rand.NextDouble() * 10000 - 5000, 2),
Amount2 = Math.Round(rand.NextDouble() * 10000 - 5000, 2),
Discount = Math.Round(rand.NextDouble() / 4, 2),
Active = (i % 4 == 0),
Trends = Enumerable.Range(0, 12).Select(x => new MonthData { Month = x + 1, Data = rand.Next(0, 100) }).ToArray(),
Rank = rand.Next(1, 6)
};
});
return list;
}
public static List<string> GetCountries()
{
var countries = new List<string>();
countries.AddRange(COUNTRIES);
return countries;
}
public static List<string> GetProducts()
{
List<string> products = new List<string>();
products.AddRange(PRODUCTS);
return products;
}
}
public class MonthData
{
public int Month { get; set; }
public double Data { get; set; }
}
Create Controller Action
Open the HomeController.cs from the Controllers folder. Add a method to return JSON data.
[HttpPost]
public JsonResult GetData()
{
List<Sale> saleList = Sale.GetData(10).ToList<Sale>();
return Json(saleList);
}
Add FlexGrid to View
Open the Index.cshtml file inside the Views/Home folder. Delete the content of this file. Add the following code to declare a FlexGrid control.
@(Html.C1().FlexGrid()
.Id("fg")
.AutoGenerateColumns(false)
.IsReadOnly(false)
.AutoClipboard(true)
.AllowSorting(true)
.AllowAddNew(false)
.SelectionMode(C1.Web.Mvc.Grid.SelectionMode.Row)
.Height(500)
.CssClass("grid")
.Columns(bl =>
{
bl.Add(cb => cb.Binding("ID").Width("0.4*").IsReadOnly(true));
bl.Add(cb => cb.Binding("Country").Header("Country").Width("*").Name("Country"));
bl.Add(cb => cb.Binding("Amount").Header("Amount").Width("*").Name("Amount"));
bl.Add(cb => cb.Binding("Product").Header("Product").Width("*").Name("Product"));
})
)
Note that the above code declares different columns and properties of the FlexGrid, but does not data bind it. We'll data bind the control on the client side using JavaScript by making an ajax call to action created in the Controller.
<script type="text/javascript">
function Load() {
$.ajax({
type: "POST",
url: "/Home/GetData",
dataType: "json",
success: function (result) {
var flex = wijmo.Control.getControl("#fg");
flex.itemsSource = result;
},
error: function (err) {
}
});
}
</script>
Call the above script on a button click:
<input type="button" id="btload" value="Get Data" onclick="Load()" />
Run the application and click on the "Get Data" button. The FlexGrid should load and display the data. You've successfully bound the grid to data returned via an AJAX call! So far, so good: We have a working FlexGrid with all necessary properties. We know that FlexGrid for ASP.NET MVC is a feature-rich control that offers number of out-of-the-box features with minimal configuration. The FlexGrid can commit data to server automatically per row or in batch. Let's use the BatchEdit feature of the grid to update all changes at once. We'll write the BatchEdit action in the controller and accordingly update the FlexGrid declaration code in the view.
public ActionResult Save([C1JSONRequest]CollectionViewBatchEditRequest<Sale> requestData)
{
return this.C1JSON(CollectionViewHelper.BatchEdit<Sale>(requestData, req =>
{
var itemresults = new List<CollectionViewItemResult<Sale>>();
return new CollectionViewResponse<Sale>
{
Error = null,
Success = true,
OperatedItemResults = itemresults
};
}, () => Sale.GetData(10).ToList<Sale>()));
}
Configure BatchEdit for FlexGrid inside the view:
<div>
<div>
<input type="button" id="btnload" value="Get Data" onclick="Load()" />
<input type="button" id="btnsave" value="Save Data" onclick="Save()" />
</div>
<h4>FlexGrid - Image in Cells</h4>
@(Html.C1().FlexGrid<Sale>()
.Id("fg")
.AutoGenerateColumns(false)
.Bind(bi => bi.BatchEdit(Url.Action(Url.Action("../Save"))).DisableServerRead(true))
.IsReadOnly(false)
.AutoClipboard(true)
.AllowSorting(true)
.AllowAddNew(false)
.SelectionMode(C1.Web.Mvc.Grid.SelectionMode.Row)
.Height(500)
.CssClass("grid")
.Columns(bl =>
{
bl.Add(cb => cb.Binding("ID").Width("0.4*").IsReadOnly(true));
bl.Add(cb => cb.Binding("Country").Header("Country").Width("*").Name("Country"));
bl.Add(cb => cb.Binding("Amount").Header("Amount").Width("*").Name("Amount"));
bl.Add(cb => cb.Binding("Product").Header("Product").Width("*").Name("Product"));
})
)
</div>
BatchEdit commit script that updates all changes to the server:
function Save() {
var flex = wijmo.Control.getControl("#fg");
cv = flex.collectionView;
cv.commit();
}
Pitfalls
One pitfall of using MVC FlexGrid with client side-binding is that when we bind the grid at client as shown in the Load function above, the server side collectionView instance is overwritten. It loses the server-side features of collectionView, and a pure client-side Wijmo collection view is now the data source of FlexGrid. Hence, if we call commit() method of FlexGrid's collectionView, it throws an error. The solution to this is easy. In the Load function, instead of directly assigning the result data of AJAX call to FlexGrid's itemsSource, we should update the sourceCollection of FlexGrid's collectionView at client-side. This way, the server-side features of CollectionView is not lost. Here's the updated Load function script. We also added a special check for any errors in date values of JSON data.
function parseDate(strDate) {
var date = strDate.match(/\\d+/g);
return new Date(parseInt(date));
}
function Load() {
$.ajax({
type: "POST",
url: "/Home/GetData",
dataType: "json",
success: function (result) {
var flex = wijmo.Control.getControl("#fg"),
cv = flex.collectionView;
//flex.itemsSource = result;
try {
cv._isFillingData = true;
cv.deferUpdate(function () {
cv.sourceCollection.clear();
result.forEach(function (item) {
item.Start = parseDate(item.Start);
item.End = parseDate(item.End);
cv.sourceCollection.push(item);
});
})
} finally {
cv._isFillingData = false;
}
},
error: function (err) {
}
});
}
Now, when we run the application and click the Save button, all changes should get updated to the server.