We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date

IEnumerable<ExpandoObject> as a Model for Grid

Hi,
I want to create dynamic grid with variable columns - I do not know exact column names and istead construct ExpandoObject at runtime, adding columns as needed.
If I pass list of ExpandoObject to the Grid component, it wont work - shows empty grid with correct number of rows, but no data.

Here is my simplified testing controller:

public ActionResult AppGrid()
{
IList<ExpandoObject> datasource = new List<ExpandoObject>();

var row = new ExpandoObject() as IDictionary<string, Object>;
row.Add("id", 1);
row.Add("name", "Name 1");
datasource.Add(row as ExpandoObject);
var row2 = new ExpandoObject() as IDictionary<string, Object>;
row2.Add("id", 2);
row2.Add("name", "Name 2");
datasource.Add(row2 as ExpandoObject);

ViewBag.datasource = datasource;

return View("DynamicAppGrid");
}

private IEnumerable<ExpandoObject> Add(IEnumerable<ExpandoObject> e, ExpandoObject value)
{
foreach (var cur in e)
{
yield return cur;
}
yield return value;
}

And my view:

@using System.Dynamic

@(Html.EJ().Grid<dynamic>("DynamicAppGrid").Datasource((IEnumerable<dynamic>)ViewBag.datasource)
.AllowPaging()
.PageSettings(settings => settings.PageSize(10))
.AllowFiltering()
.AllowSorting()
.FilterSettings(d => d.FilterType(FilterType.Menu))
.EnablePersistence(false)
.Columns(col =>
{
col.Field("id").HeaderText("ID").TextAlign(TextAlign.Right).Width(40).Add();
col.Field("name").HeaderText("Name").TextAlign(TextAlign.Right).Width(40).Add();
}).ClientSideEvents(eve => { eve.RecordDoubleClick("recordDblClick"); eve.RowSelected("recordSelected"); })
)

Any other working solution is welcome. I have list of column names stored in database and from this I dynamicaly build my query to load data and then loop through list of columns to build my ExpandoObject at runtime. I can easily build key-value pairs for each column and then push List of pairs (as one row) into another List representing whole table. But I do not know how to consume this type of data by Grid. Maybe using JSON datasource?

10 Replies

SS Seeni Sakthi Kumar Seeni Raj Syncfusion Team October 11, 2016 12:07 PM UTC

Hi Tom, 
 
We are able to reproduce the problem at our end. The cause of the problem is datasource bound to the Grid is in keyvaluepair structure. Since Grid handles the list of objects or JSON data or Data Table, it will not recognize the keyvalue pair objects of array. So we suggest to bind JSON/Data Table/List of objects to the Grid. Refer to the following code example with the Data Table. 
 
        public ActionResult Index(){ 
            List<ExpandoObject> datasource = new List<ExpandoObject>(); 
            dynamic row = new ExpandoObject(); 
            row.id = 1; 
            row.name = "Name 1"; 
            dynamic row1 = new ExpandoObject(); 
            row1.id = 2; 
            row1.name = "Name 2"; 
            var dt = new DataTable(); 
            foreach (var val2 in ((IDictionary<string, object>)datasource[0]).Keys) 
            { 
                dt.Columns.Add(val2); 
            } 
            foreach (dynamic val in datasource) 
            { 
                IDictionary<string, object> items = val; 
                dt.Rows.Add(items.Values.ToArray()); 
            } 
            ViewBag.datasource = dt; 
            return View(); 
        } 
@(Html.EJ().Grid<object>("Grid") 
        .Datasource((IEnumerable<object>)ViewBag.datasource) 
            .AllowPaging() 
    .PageSettings(settings => settings.PageSize(10)) 
    .AllowFiltering() 
    .AllowSorting() 
    .FilterSettings(d => d.FilterType(FilterType.Menu)) 
    .EnablePersistence(false) 
    //columns not declared 
) 
 
Grid also provides the Auto Generation concept for columns only if the Columns were not declared in it. Refer to the following Help Document. 
 
  
We have prepared a sample that can be downloaded from the following location. 
 
 
Regards, 
Seeni Sakthi Kumar S. 



TF Tom Frajer October 11, 2016 12:54 PM UTC

Thank you very much! - I got the idea, but have to sligtly modify your source snippet as it was throwing some cast exceptions. Here is final piece for other people who might find this useful:


        public ActionResult AppGrid(int id)
        {

            List<ExpandoObject> datasource = new List<ExpandoObject>();

            dynamic row = new ExpandoObject();
            row.id = 1;
            row.name = "Name 1";
            datasource.Add(row as ExpandoObject);
            dynamic row1 = new ExpandoObject();
            row1.id = 2;
            row1.name = "Name 2";
            datasource.Add(row1 as ExpandoObject);

            var dt = new System.Data.DataTable();

            foreach (var val2 in ((IDictionary<string, object>)datasource[0]).Keys)
            {
                dt.Columns.Add(val2);
            }

            foreach (dynamic val in datasource)
            {
                IDictionary<string, object> items = val;
                dt.Rows.Add(items.Values.ToArray());
            }

            ViewBag.datasource = dt;

           return View("DynamicAppGrid");
        }

        private IEnumerable<ExpandoObject> Add(IEnumerable<ExpandoObject> e, ExpandoObject value)
        {
            foreach (var cur in e)
            {
                yield return cur;
            }
            yield return value;
        }


@(Html.EJ().Grid<object>("Grid")

        .Datasource((System.Data.DataTable)ViewBag.datasource)

            .AllowPaging()

    .PageSettings(settings => settings.PageSize(10))

    .AllowFiltering()

    .AllowSorting()

    .FilterSettings(d => d.FilterType(FilterType.Menu))

    .EnablePersistence(false)

//columns not declared

}


 



TF Tom Frajer October 11, 2016 01:05 PM UTC

One more question. I need to specify column properties and I do not know how do to it if there are no columns definitions in the view.
How can I set for example HeaderText, align, width and other properties of Syncfusion table?

col.Field(t => t.nl_product_year).HeaderText("Rok").TextAlign(TextAlign.Right).Width(50).Add();


SS Seeni Sakthi Kumar Seeni Raj Syncfusion Team October 12, 2016 10:57 AM UTC

Hi Tom, 
 
While using Auto-Generation concept in Grid, you cannot assign other properties of the Columns in Grid. However, you can assign values to the Column properties in the Load event of the Grid. Refer to the following code example. 
 
 
@(Html.EJ().Grid<object>("BatchEditing") 
        .Datasource((System.Data.DataTable)ViewBag.datasource) 
            .AllowPaging() 
    .PageSettings(settings => settings.PageSize(10)) 
    .AllowFiltering() 
    .AllowSorting() 
    .FilterSettings(d => d.FilterType(FilterType.Menu)) 
    .EnablePersistence(false) 
    .ClientSideEvents(events => events.Load("onLoad")) 
        //columns not declared 
) 
 
<script> 
 
    function onLoad(args) { 
        var jsonObj = this.model.dataSource[0]; 
        if (this.model.columns.length == 0 && jsonObj) { 
            for (var field in jsonObj) { 
                var value = jsonObj[field]; 
                this.model.columns.push({ 
                    field: field, 
                    width: 50, 
                    headerText: field, 
                    textAlign: ej.TextAlign.Right 
                }); 
            } 
        } 
    } 
</script> 
 
Regards, 
Seeni Sakthi Kumar S. 



TF Tom Frajer October 13, 2016 09:19 AM UTC

Thank you, this seems OK, I will yet have to try it, but there should be no problem doing this. Another question is how to set datatype of the column? How can I set column in Load event to display data as checkboxes?


SS Seeni Sakthi Kumar Seeni Raj Syncfusion Team October 14, 2016 12:56 PM UTC

Hi Tom, 

Using the “type” property of Grid columns, you can specify the DataType of the columns and you can mention the type of the Column in the load event of the Grid. Refer to the following code example and API Reference.  

@(Html.EJ().Grid<object>("BatchEditing") 
        .Datasource((System.Data.DataTable)ViewBag.datasource) 
            .AllowPaging() 
        . . . 
             . . 
    .ClientSideEvents(events => events.Load("onLoad")) 
    //columns not declared 
) 
 
<script> 
 
    function onLoad(args) { 
        var jsonObj = this.model.dataSource[0]; 
        if (this.model.columns.length == 0 && jsonObj) { 
            for (var field in jsonObj) { 
                if (jsonObj.hasOwnProperty(field)) { 
                    var value = jsonObj[field]; 
                    this.model.columns.push({ 
                        field: field, 
                        width: 50, 
                        type: value != null ? (value.getDay ? (value.getHours() > 0 || value.getMinutes() > 0 || value.getSeconds() > 0 || value.getMilliseconds() > 0 ? "datetime" : "date") : typeof (value)) : null, 
                        headerText: field, 
                        textAlign: ej.TextAlign.Right 
                    }); 
                } 
            } 
        } 
    } 
</script> 
 
        public ActionResult Index(){ 
            List<ExpandoObject> datasource = new List<ExpandoObject>(); 
            dynamic row = new ExpandoObject(); 
            row.id = 1; 
            row.verified = true; 
            row.name = "Name 1"; 
            datasource.Add(row); 
            dynamic row1 = new ExpandoObject(); 
            row1.id = 2; 
            row1.verified = false; 
            row1.name = "Name 2"; 
            datasource.Add(row1); 
            var dt = new DataTable(); 
            foreach (var col in ((IDictionary<string, object>)datasource[0])) 
            { 
                dt.Columns.Add(col.Key, col.Value.GetType());//Columns and type 
            } 
            foreach (dynamic val in datasource) 
            { 
                IDictionary<string, object> items = val; 
                dt.Rows.Add(items.Values.ToArray()); 
            } 
            ViewBag.datasource = dt; 
            return View(); 
        } 


Note: The Grid automatically render the Boolean column as checkbox which is the default behavior.  

Regards, 
Seeni Sakthi Kumar S. 



ME Megatron October 15, 2016 09:19 PM UTC

Hi,

can you please post the sample code, with the auto-detection of the column type, and showing the Edit and Save to work as well.

Will this also work with dynamic and generic objects or should we stick with expando objects

thanks



SS Seeni Sakthi Kumar Seeni Raj Syncfusion Team October 17, 2016 09:07 AM UTC

Hi Tom, 

Query #1: The auto-detection of the column type 

From the first object of the dataSource, you can detect the column types and define the same to the Grid columns. Refer to the following code example. 

@(Html.EJ().Grid<object>("BatchEditing") 
        .Datasource((System.Data.DataTable)ViewBag.datasource) 
            .AllowPaging() 
            .ClientSideEvents(events => events.Load("onLoad").DataBound("dataBound")) 
        //columns not declared 
) 
 
<script> 
    function onLoad(args) { 
        var jsonObj = this.model.dataSource[0]; 
        if (this.model.columns.length == 0 && jsonObj) { 
            for (var field in jsonObj) { 
                var value = jsonObj[field]; 
                //from the object values type has been defined 
                //so you can dynamically assign them to the columns 
                type = value != null ? (value.getDay ? (value.getHours() > 0 || value.getMinutes() > 0 || value.getSeconds() > 0 || value.getMilliseconds() > 0 ? "datetime" : "date") : typeof (value)) : null; 
                    this.model.columns.push({ 
                        field: field, 
                        width: 50, 
                        type: type, 
                        headerText: field, 
                        textAlign: ej.TextAlign.Right 
                    }); 
            } 
        } 
    } 
</script> 
 
Query #2: showing the Edit and Save to work as well 

To perform the editing action in the Grid, you have to mention the primaryKey (read-only) column in the Grid. Because, based on this read-only column alone, save/delete of any records takes place in the Grid. You can define this property “isPrimaryKey” in anyone of the read-only column within the dataBound event of the Grid. We have already discussed about in the following Help Document. 


@(Html.EJ().Grid<object>("BatchEditing") 
        .Datasource((System.Data.DataTable)ViewBag.datasource) 
            .AllowPaging() 
            .EditSettings(edit => edit.AllowAdding().AllowDeleting().AllowEditing()) 
            .ToolbarSettings(tools => tools 
                .ShowToolbar() 
                    .ToolbarItems(items => 
                    { 
                        items.AddTool(ToolBarItems.Add); 
                        items.AddTool(ToolBarItems.Edit); 
                        items.AddTool(ToolBarItems.Update); 
                        items.AddTool(ToolBarItems.Delete); 
                        items.AddTool(ToolBarItems.Cancel); 
                    } 
            )) 
            .ClientSideEvents(events => events.Load("onLoad").DataBound("dataBound")) 
        //columns not declared 
) 
 
<script> 
    function dataBound(args) { 
        //for editing actions, primaryKey column is must  
        //we have defined them in the dataBound event 
        //always recommended to placed the primarykey column as first column 
        var column = args.model.columns[0]; 
        column.isPrimaryKey = true; 
        //Here columns method used to update the particular column 
        this.columns(column, "update"); 
    } 
</script> 
 
Grid also provides, various editing types such as boolean, numberic, date or datetime. These edit types were defined in Grid using editType property of the columns. Refer to the following code example and Help Document. 

    function onLoad(args) { 
        var jsonObj = this.model.dataSource[0]; 
        if (this.model.columns.length == 0 && jsonObj) { 
            for (var field in jsonObj) { 
                var value = jsonObj[field]; 
                //from the object values type has been defined 
                //so you can dynamically assign them to the columns 
                type = value != null ? (value.getDay ? (value.getHours() > 0 || value.getMinutes() > 0 || value.getSeconds() > 0 || value.getMilliseconds() > 0 ? "datetime" : "date") : typeof (value)) : null; 
                if (type == "boolean") 
                    editType = "booleanedit"; 
                else if (type == "date") 
                    editType = "datepicker"; 
                else if (type == "datetimepicker") 
                    editType = "datetimepicker"; 
                else if (type == "number") 
                    editType = "numericedit"; 
                else 
                    editType = "string"; 
                    this.model.columns.push({ 
                        field: field, 
                        width: 50, 
                        type: type, 
                        editType: editType, 
                        headerText: field, 
                        textAlign: ej.TextAlign.Right 
                    }); 
            } 
        } 
    } 
 
 

We have also prepared a sample that can be downloaded from the following location. 


Query #3: Will this also work with dynamic and generic objects or should we stick with expando objects 

Yes, the solution provided for rendering the auto-generated columns will work with the dynamic as well as generic objects.   

Regards, 
Seeni Sakthi Kumar S. 



ME Megatron October 19, 2016 07:18 PM UTC

Hi is this dynamic to anydata type on the column, including user defined objects, for e.g. custom enums etc


SS Seeni Sakthi Kumar Seeni Raj Syncfusion Team October 20, 2016 10:47 AM UTC

Hi Megatron, 

We suspect that you are looking for a complex column in a Grid. To generate the complex column in Grid, you have to check for the object in the records. Refer to the  following code example. 

@(Html.EJ().Grid<object>("BatchEditing") 
        .Datasource((System.Data.DataTable)ViewBag.datasource) 
            .AllowPaging() 
           .. .  
            .ClientSideEvents(events => events.Load("onLoad").DataBound("dataBound")) 
        //columns not declared 
) 
 
<script> 
    function onLoad(args) { 
        var jsonObj = this.model.dataSource[0]; 
        if (this.model.columns.length == 0 && jsonObj) { 
            for (var field in jsonObj) { 
                var value = jsonObj[field]; 
                //from the object values type has been defined 
                //so you can dynamically assign them to the columns 
                if (typeof (jsonObj[field]) != "object") { 
                    type = value != null ? (value.getDay ? (value.getHours() > 0 || value.getMinutes() > 0 || value.getSeconds() > 0 || value.getMilliseconds() > 0 ? "datetime" : "date") : typeof (value)) : null; 
                    editType = typeBuilder(type); 
                    this.model.columns.push({ 
                        field: field, 
                        width: 50, 
                        type: type, 
                        editType: editType, 
                        headerText: field, 
                        textAlign: ej.TextAlign.Right 
                    }); 
                } 
                else { 
                    //for complex columns 
                    complexColumn(jsonObj[field], field, this.model.columns); 
                } 
            } 
        } 
    } 
    function complexColumn(obj, field, cols, cxField) { 
        var complexField = cxField || field; 
        for (var field1 in obj) { 
 
            if (typeof obj[field1] == "object" && !ej.isNullOrUndefined(obj[field1])) { 
                complexField = complexField.concat(".").concat(field1); 
                complexColumn(obj[field1], field1, cols, complexField); 
            } 
            else { 
                var cxFieldName = (complexField).concat(".").concat(field1), value = obj[field1]; 
                var editType = typeBuilder(type); 
                cols.push({ 
                    field: cxFieldName, 
                    editType: editType, 
                    type: value != null ? (value.getDay ? (value.getHours() > 0 || value.getMinutes() > 0 || value.getSeconds() > 0 || value.getMilliseconds() > 0 ? "datetime" : "date") : typeof (value)) : null 
                }); 
            } 
        } 
    } 
    function typeBuilder(type) { 
        var editType = ""; 
        if (type == "boolean") 
            editType = "booleanedit"; 
            . . 
        else 
            editType = "string"; 
        return editType; 
    } 


Note: complexColumn method will handle the objects and generate the columns with complex name (“Employee.FirstName.Name”). 

We have modified the sample that can be downloaded from the following location. 


Regards, 
Seeni Sakthi Kumar S. 


Loader.
Up arrow icon