From 2848fa2da03101c455436718043b6de6ca23a691 Mon Sep 17 00:00:00 2001 From: ALMMa Date: Wed, 19 Mar 2014 23:07:41 -0300 Subject: [PATCH] Helper methods on column collection. Added helper methods and a ColumnCollection object to help you when getting both filtered and sorted columns. Improved sample code. --- DataTables.Mvc/ColumnCollection.cs | 90 ++++++++++++++++++++++++++++ DataTables.Mvc/DataTables.Mvc.csproj | 1 + DataTables.Mvc/DataTablesBinder.cs | 19 ++---- DataTables.Mvc/IDataTablesRequest.cs | 34 +++++++++-- README.md | 36 +++++------ 5 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 DataTables.Mvc/ColumnCollection.cs diff --git a/DataTables.Mvc/ColumnCollection.cs b/DataTables.Mvc/ColumnCollection.cs new file mode 100644 index 0000000..2cdc669 --- /dev/null +++ b/DataTables.Mvc/ColumnCollection.cs @@ -0,0 +1,90 @@ +#region Copyright +/* The MIT License (MIT) + +Copyright (c) 2014 Anderson Luiz Mendes Matos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#endregion Copyright +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataTables.Mvc +{ + /// + /// Represents a read-only DataTables column collection. + /// + public class ColumnCollection : IEnumerable + { + /// + /// For internal use only. + /// Stores data. + /// + private IReadOnlyList Data; + /// + /// Created a new ReadOnlyColumnCollection with predefined data. + /// + /// The column collection from DataTables. + public ColumnCollection(IEnumerable columns) + { + if (columns == null) throw new ArgumentNullException("The provided column collection cannot be null", "columns"); + Data = columns.ToList().AsReadOnly(); + } + /// + /// Get sorted columns on client-side already on the same order as the client requested. + /// The method checks if the column is bound and if it's ordered on client-side. + /// + /// The ordered enumeration of sorted columns. + public IOrderedEnumerable GetSortedColumns() + { + return Data + .Where(_column => !String.IsNullOrWhiteSpace(_column.Data) && _column.IsOrdered) + .OrderBy(_c => _c.OrderNumber); + } + /// + /// Get filtered columns on client-side. + /// The method checks if the column is bound and if the search has a value. + /// + /// The enumeration of filtered columns. + public IEnumerable GetFilteredColumns() + { + return Data + .Where(_column => !String.IsNullOrWhiteSpace(_column.Data) && _column.Searchable && !String.IsNullOrWhiteSpace(_column.Search.Value)); + } + /// + /// Returns the enumerable element as defined on IEnumerable. + /// + /// The enumerable elemento to iterate through data. + public IEnumerator GetEnumerator() + { + return Data.GetEnumerator(); + } + /// + /// Returns the enumerable element as defined on IEnumerable. + /// + /// The enumerable element to iterate through data. + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return ((System.Collections.IEnumerable)Data).GetEnumerator(); + } + } +} diff --git a/DataTables.Mvc/DataTables.Mvc.csproj b/DataTables.Mvc/DataTables.Mvc.csproj index cf598f3..1e2d497 100644 --- a/DataTables.Mvc/DataTables.Mvc.csproj +++ b/DataTables.Mvc/DataTables.Mvc.csproj @@ -82,6 +82,7 @@ + diff --git a/DataTables.Mvc/DataTablesBinder.cs b/DataTables.Mvc/DataTablesBinder.cs index 5a30ad9..9009bc4 100644 --- a/DataTables.Mvc/DataTablesBinder.cs +++ b/DataTables.Mvc/DataTablesBinder.cs @@ -106,10 +106,10 @@ public object BindModel(ControllerContext controllerContext, ModelBindingContext ParseColumnOrdering(requestParameters, columns); // Attach columns into the model. - model.AddColumns(columns); + model.SetColumns(columns); // Returns the filled model. - return model; + return (IDataTablesRequest)model; } /// /// Resolves the NameValueCollection from the request. @@ -148,7 +148,7 @@ protected virtual List GetColumns(NameValueCollection collection) { try { - var returnCollection = new List(); + var columns = new List(); // Loop through every request parameter to avoid missing any DataTable column. for (int i = 0; i < collection.Count; i++) @@ -163,23 +163,16 @@ protected virtual List GetColumns(NameValueCollection collection) var columnSearchValue = Get(collection, String.Format(COLUMN_SEARCH_VALUE_FORMATTING, i)); var columnSearchRegex = Get(collection, String.Format(COLUMN_SEARCH_REGEX_FORMATTING, i)); - var column = new Column( - columnData, - columnName, - columnSearchable, - columnOrderable, - columnSearchValue, - columnSearchRegex); - - returnCollection.Add(column); + columns.Add(new Column(columnData, columnName, columnSearchable, columnOrderable, columnSearchValue, columnSearchRegex)); } else break; // Stops iterating because there's no more columns. } - return returnCollection; + return columns; } catch { + // Returns an empty column collection to avoid null exceptions. return new List(); } } diff --git a/DataTables.Mvc/IDataTablesRequest.cs b/DataTables.Mvc/IDataTablesRequest.cs index b8e43e8..63ccce6 100644 --- a/DataTables.Mvc/IDataTablesRequest.cs +++ b/DataTables.Mvc/IDataTablesRequest.cs @@ -57,7 +57,7 @@ public interface IDataTablesRequest /// /// Gets the read-only collection of client-side columns with their options and configs. /// - IReadOnlyCollection Columns { get; } + ColumnCollection Columns { get; } } /// /// For internal use only. @@ -65,14 +65,36 @@ public interface IDataTablesRequest /// class DataTablesRequest : IDataTablesRequest { + /// + /// For internal use only. + /// Gets/Sets the draw counter from DataTables. + /// public int Draw { get; set; } + /// + /// For internal use only. + /// Gets/Sets the start record number (jump) for paging. + /// public int Start { get; set; } + /// + /// For internal use only. + /// Gets/Sets the length of the page (paging). + /// public int Length { get; set; } + /// + /// For internal use only. + /// Gets/Sets the global search term. + /// public Search Search { get; set; } - private List _Columns { get; set; } - public IReadOnlyCollection Columns { get { return _Columns.AsReadOnly(); } } - public DataTablesRequest() { _Columns = new List(); } - public void AddColumn(Column column) { _Columns.Add(column); } - public void AddColumns(IEnumerable columns) { _Columns.AddRange(columns); } + /// + /// For internal use only. + /// Gets/Sets the column collection. + /// + public ColumnCollection Columns { get; private set; } + /// + /// For internal use only. + /// Set the new columns on the mechanism. + /// + /// The columns to be set. + public void SetColumns(IEnumerable columns) { Columns = new ColumnCollection(columns); } } } \ No newline at end of file diff --git a/README.md b/README.md index 520493d..86f0472 100644 --- a/README.md +++ b/README.md @@ -37,36 +37,30 @@ public JsonResult MyActionResult([ModelBinder(typeof(DataTablesBinder)] IDataTab return Json(new DataTablesResponse(requestModel.Draw, paged, myFilteredData.Count(), myOriginalDataSet.Count())); } ``` -

Any gotchas?

+

What about ordering?

- There is one. Simple but tricky. + It's a no brainer too.

- When sorting on client-side, DataTables send a request to your server to order by the selected columns. It defaults to the first column but you can change that.
+ Filter/sort info from each column is, well, included on each column.

- The problem comes on the server-side when dealing with IQueryable, IEnumerable and IList elements. - DataTables provides you with the sorting column indexes and directions but we don't have a method to order an IQueryable, IEnumerable or IList using the index of the field. + To help you out, get only the columns which were ordered on client-side with IDataTablesRequest.GetSortedColumns(). + Than, iterate through then and use Column.SortDirection to sort your dataset.

- Also, consider that you have an unbound column (model: null). You won't find a way to order that column on server-side (but you might want to order through other columns). -

-

- So, you'll have work with 3 elements: Column.IsOrdered, Column.OrderNumber and Column.SortDirection. -

-

- Tips: + Sample:

```C# -var columns = requestParameters.Columns.Where(_column => _column.IsOrdered && !String.IsNullOrWhiteSpace(_column.Data)); -if (columns.Any()) +var filteredColumns = requestParameters.Columns.GetFilteredColumns(); +foreach(var column in filteredColumns) + Filter(column.Data, column.Search.Value, column.Search.IsRegexValue); + +var sortedColumns = requestParameters.Columns.GetSortedColumns(); +var isSorted = false; +foreach(var column in sortedColumns) { - var sortedColumns = columns.OrderBy(_column => _column.OrderNumber); - var isSorted = false; - foreach(var column in sortedColumns) - { - if (!isSorted) { Sort(column.Data, column.SortDirection); isSorted = true; } - else { SortAgain(column.Data, column.SortDirection); } - } + if (!isSorted) { Sort(column.Data, column.SortDirection); isSorted = true; } + else { SortAgain(column.Data, column.SortDirection); } } ``` \ No newline at end of file