EjsGrid - How to add custom filter for a custom column template

Hello,
Is there a way to add custom filter component for grid column (complex data) which has template? Please find attached pictures and GridCustomFilter.razor file for more info. I have followed Custom component in filter menu document but it doesn't work with complex data.



--------------



Thanks,
Bishan M.



Attachment: GridCustomFilter_350db5be.zip

19 Replies

BM Bishan Moteria February 19, 2020 08:30 PM UTC

Please note that, I'd like to filter Tags column with multiple values. For instance, filter by "issue" and "area-code".


RS Renjith Singh Rajendran Syncfusion Team February 20, 2020 09:53 AM UTC

Hi Bishan, 

Thanks for contacting Syncfusion support. 

From the query we understood that you are looking for filtering Tags collection but grid works with one to one relationship in your case Tags column will be like one to multiple values hence we suggest you to use a computed/additional property(TagsName) and assign the field name(TagsName) to Grid column to achieve filtering for the List value(Tags) in Grid. We have prepared a sample based on the shared codes, please download the sample from the link below, 
 
In the above sample, we have defined a new property(TagsName) in the Order class. And we have assigned the value for TagsName based on the Tags property value. Please refer the code below, 

 
<EjsGrid @ref="@Grid" DataSource="@Orders" AllowFiltering="true" AllowPaging="true" Height="315"> 
    <GridFilterSettings Type="Syncfusion.EJ2.Blazor.Grids.FilterType.CheckBox"></GridFilterSettings> 
    <GridColumns> 
        ... 
       <GridColumn Field=@nameof(Order.TagsName) HeaderText="Tags" TextAlign="TextAlign.Right" Width="120"> 
            <Template> 
                @{ 
                    var tags = (context as Order).Tags; 
                    foreach (var tag in tags) 
                    { 
                        <span class="badge badge-secondary">@tag.Name</span> 
                    } 
                } 
            </Template> 
        </GridColumn> 
    </GridColumns> 
</EjsGrid> 
 
@code{ 
    EjsGrid<Order> Grid; 
    ... 
    public class Order 
    { 
        ... 
       public double? Freight { get; set; } 
        public List<Tag> Tags { get; set; } = new List<Tag>(); 
        public string TagsName { get { 
                var newitem = ""; 
                foreach (Tag item in Tags) 
                { 
                    if (newitem == "") 
                    { 
                        newitem = item.Name; 
                    } 
                    else 
                    { 
                        newitem = newitem + "," + item.Name; 
                    } 
                }; 
                return newitem;       //Assign the value for TagsName based on the value from Tags property. 
            } set { } 
        } 
    } 
    ... 
} 


Please get back to us if you need further assistance. 

Regards, 
Renjith Singh Rajendran. 



BM Bishan Moteria February 20, 2020 12:44 PM UTC

Hi Renjith,

Thank you for providing sample.

Understood, but is there a way we could filter by individual tag name? Like filter by tag name "new" and "area-ui" rather than by whole "issue,area-ui". The product we are designing is required to have filter by individual tag name rather than converting List of something to concatenated string. These tags data are abtract but each of these tag would mean something. So we can't filter by concatenated string.



Thank you,
Bishan M.


RS Renjith Singh Rajendran Syncfusion Team February 21, 2020 06:58 AM UTC

Hi Bishan, 

We suggest you to assign the value for the “TagsName” based on your requirement. As for display the value in template we will be using the values from the original “Tags” property. And, the filtering will only be performed based on the provided Field name property value(TagsName).  

So the data available for TagsName will only be displayed in the Filter choices checkbox. Providing value as area-ui instead of issue,area-ui for the TagsName property will make the filterchoices display only the area-ui and not the issue,area-ui

So we suggest you to assign the value for TagsName based on your requirement. We have prepared a sample based on this, please download the sample from the link below, 
 
       
       public string TagsName { get { 
                var newitem = ""; 
                foreach (Tag item in Tags) 
                { 
                        newitem = item.Name;    //Assign only the single value for TagsName instead of concatenated string. 
                }; 
                return newitem;   
            } set { } 
        } 
 

If we have misunderstood your query, kindly get back to us with a detailed explanation or a pictorial representation shoing your complete requirement. 
  
Please get back to us if you need further assistance. 

Regards, 
Renjith Singh Rajendran. 



BM Bishan Moteria February 21, 2020 01:47 PM UTC

Hi Renjith,

So basically, the requirement is to have multiple columns with one to multiple values relationship and provide ability to filter these columns with multiple filter conditions. Here are more details:

Grid Column Definitions:
1. OrderID - int
2. CustomerID - int
3. OrderDate - DateTime?
4. Freight - double?
5. Tags - List of Tag
6. Classes - List of Class
7. Shops - List of Shop

Example of how grid would look like after render:


Now that the data is displayed to the user, user wants find entries with multiple criterias in following scenarios:

Scenario #1: User A wants to filter data which has tag "issue", has class "Class Two" and has shop "Amazon".
Results: As per user filter criteria, following entries should be diplsayed:


Scenario #2: User B wants to filter data which has tag "area-ui" and has shop "Walmart".
Results: As per user filter criteria, following entries should be diplsayed:


Scenario #3: User C wants to filter data which has tag "new" and "area-ui", has class "Class Three".
Result: As per user filter criteria, following entries should be displayed:


I have modified the sample by adding two more columns (Classes and Shops) and is attached with this response.

Please let me know if I can provide further more information.

Thank you,
Bishan M.

Attachment: BlazorApp1_6399b42e.zip


RS Renjith Singh Rajendran Syncfusion Team February 27, 2020 02:52 PM UTC

Hi Bishan, 

Based on your requirement, we suggest you to use the “CustomAdaptor feature of DataManager”. Please refer the documentation link below, 
 
With the CustomAdaptor in Grid you can customize the display of checkbox items in the filter dialog and also customize the filtering action performed in Grid. We suggest you to refer the below code example and the detailed explanation of the codes. 
Note : We have explained only for the column with List<Tag>. Please use the same way for the columns with List<Class> and List<Shop>. 

Explanation for showing individual tags in filter checkbox items :  
In the below code, we have initially fetched the Name property values from List<Tag> and created a new List<TagsNameClass> (CheckItemData) in the OnInitialized method. Now when the filter icon in the column header is clicked the OnActionBegin event will be triggered. In that event handler, we have fetched the clicked header column name and stored it in TagsColumnCheck. And then the Read method of CustomAdaptor will also be triggered, so now based on this TagsColumnCheck(Tags) we will return the CheckItemData(list generated from List<Tag>) in the Read method of CustomAdaptor class. So now the individual tags will be shown as checkbox items in filter dialog. 

Explanation for filtering the List<Tag> column :  
And when the Filter button in the filter dialog is clicked, the Read method of CustomAdaptor will be triggered. In the DataManagerRequest, you will get the dm.Where. This will contain the filter predicates. With the filter predicate details from dm.Where you can generate your own customized filter method. Please use the predicates from dm.Where to write your own filter method to filter the values from List<Tag>. 

 
<EjsGrid @ref="@Grid" TValue="Order" AllowFiltering="true" AllowPaging="true" Height="315"> 
    <GridEvents OnActionBegin="OnActionBegin" TValue="Order"></GridEvents>    @*Bind the OnActionBegin event, to get the details of clicked column filter icon*@ 
    <EjsDataManager AdaptorInstance="@typeof(CustomAdaptor)" Adaptor="Adaptors.CustomAdaptor"></EjsDataManager> 
    <GridFilterSettings Type="Syncfusion.EJ2.Blazor.Grids.FilterType.CheckBox"></GridFilterSettings> 
    <GridColumns> 
        ... 
        <GridColumn Field=@nameof(Order.Tags) HeaderText="Tags" TextAlign="TextAlign.Right" Width="120">    @*No need for new property as TagsName*@ 
            <Template> 
                @{ 
                    var tags = (context as Order).Tags; 
                    foreach (var tag in tags) 
                    { 
                        <span class="badge badge-secondary">@tag.Name</span> 
                    } 
                } 
            </Template> 
        </GridColumn> 
        ... 
    </GridColumns> 
</EjsGrid> 

 
@code{ 
    EjsGrid<Order> Grid; 
    public static List<Order> Orders { get; set; } 
    public static string TagsColumnCheck {get;set;} 
    public void OnActionBegin(ActionEventArgs<Order> args)      //In OnActionBegin event handler, store the clicked column name(TagsName). This is to assign the datasource for checkbox items list in filter dialog based on the clicked column(Tag or Class or Shop). 
    { 
        if(args.RequestType.ToString() == "FilterBeforeOpen") 
            TagsColumnCheck = args.ColumnName; 
        if(args.RequestType.ToString() == "Filtering") 
            TagsColumnCheck = null; 
        if(args.RequestType.ToString() == "Refresh") 
            TagsColumnCheck = null; 
    } 
    public static List<TagsNameClass> CheckItemData = new List<TagsNameClass>(); 
    protected override void OnInitialized() 
    { 
        ... 
        foreach (var a in Orders)                             //Prepare a list of individual values from the List<Tag> and store them in CheckItemData. Do like the same for List<Class> and List<Shop> also. 
        { 
            foreach (Tag item in a.Tags) 
            { 
                TagsNameClass o = new TagsNameClass { 
                    Tags = item.Name 
                }; 
                CheckItemData.Add(o);                                   //Fetch the values from the Tags and store it in a List 
            }; 
        } 
    } 
    public class TagsNameClass 
    { 
        public string Tags { get; set; }                    //Use same name as like the Order Field name(Tags) 
    } 
    public class CustomAdaptor : DataAdaptor 
    { 
        public override object Read(DataManagerRequest dm, string key = null) 
        { 
            IEnumerable<Order> DataSource = Orders; 
            if (dm.Where != null && dm.Where.Count > 0)      //Filtering 
            { 
                //Use your own customized filter instead of our PerformFiltering method. Use the predicates you receive in dm.Where to generate your own filter. 
                DataSource = DataSource.Where(...).ToList();     //Filter the DataSource based on the predicates you receive in dm.Where  
            } 
            ... 
            if(TagsColumnCheck == "Tags")   //Based on the clicked column(Tags) filter icon(i.e.) based on the stored variable value from TagsColumnCheck, return the CheckItemData which you have prepared in OnInitialized method 
            { 
                return CheckItemData;     //Return CheckItemData alone 
            } 
            else 
            { 
                return dm.RequiresCounts ? new DataResult() { Result = DataSource, Count = count } : (object)DataSource;  //Return the Data to display in Grid. 
            } 
        } 
    } 
    ... 
    public class Order                   //No need for new property as TagsName you can use the below model itself 
    { 
        public int? OrderID { get; set; } 
        public string CustomerID { get; set; } 
        public DateTime? OrderDate { get; set; } 
        public double? Freight { get; set; } 
        public List<Tag> Tags { get; set; } = new List<Tag>(); 
        public List<Class> Classes { get; set; } = new List<Class>(); 
        public List<Shop> Shops { get; set; } = new List<Shop>(); 
    } 
    ... 
} 


Please use the above suggestion and kindly get back to us if you need any further assistance. 

Regards, 
Renjith Singh Rajendran. 



EC Edward Carandang March 2, 2020 05:39 AM UTC

Dear Renjith,

Thank you for your example. However, I am having some issue when I have other filtered column. If I filter another column first, the list of tags in the filter becomes empty. Is this a bug on the DataManagerRequest or something?

Best,
Ed


EC Edward Carandang March 3, 2020 06:50 PM UTC

Up. Any response from Syncfusion?


RS Renjith Singh Rajendran Syncfusion Team March 4, 2020 01:36 PM UTC

Hi Edward, 

Thanks for your update. 

We understood that, you are facing issues when filtering multiple columns in Grid. We would like to inform you that, when a Grid is bind with CustomAdaptor, then the filtering action in Grid will be handled in the application level by using the custom logic generated for filtering based on the corresponding application. And the DataOperations(like filtering, sorting etc.) won’t be handled by Grid. The DataOperations will be performed completely based on the customized filter handling code defined in the CustomAdaptor. 

So could you please share with us the following details, so that we can proceed further with the codes which you have tried from your side. 
  1. Share the customized filtering codes which you have defined in CustomAdaptor
  2. Share the complete CustomAdaptor class codes.

The provided information will help us analyze the problem, and provide you a solution as early as possible. 

Regards, 
Renjith Singh Rajendran. 



EC Edward Carandang March 5, 2020 04:29 PM UTC

Dear Renjith,

Thank you for your response. I am having the problem in an application currently being used, but I can't send it. I will try to create a skeleton application, and see if it also encounters the problem I am having. Will post again as soon as possible. Will keep you posted.

Best,
Edward


EC Edward Carandang March 5, 2020 05:45 PM UTC

Dear Renjith,

Please find attached sample project trying to simulate what you suggested. If you try to open the filter for tags column initially, all the tags will be listed as expected. However, if you filter first by Name, then try to filter by Tags, no tags will be displayed in the popup.

Best,
Edward

Attachment: BlazorApp1_d8dc8c93.zip


EC Edward Carandang March 7, 2020 05:56 AM UTC

Dear Renjith,

Were you able to replicate what I wanted to show? Is this a bug or is it's just a configuration that I missed?

Best,
Edward


RS Renjith Singh Rajendran Syncfusion Team March 9, 2020 05:59 PM UTC

Hi Edward, 

Thanks for the sample. 

We are currently working on the provided sample. We will update you further details on March 11,2020. 

Until then we appreciate your patience. 

Regards, 
Renjith Singh Rajendran. 



RS Renjith Singh Rajendran Syncfusion Team March 11, 2020 12:16 PM UTC

Hi Edward, 

By default, we don't have support to perform any DataOperation in a column with one to many relationship data column in Grid. So, our default PerformFiltering method cannot be used to filter the TagName column. We have prepared a customized sample to achieve filtering for the TagName column. We have fetched the data manger request query from the DataManagerRequest argument of Read method, and processed the query to make it usable to filter the TagName column in Grid, and finally return the customized filtered data to display in Grid. We have prepared a sample based on this requirement, please download the sample from the link below, 

Please refer the codes below, 

 
<EjsGrid @ref="GridInstance" ID="Filterid" TValue="ListItem" ...> 
    <EjsDataManager AdaptorInstance="@typeof(CustomAdaptor)" Adaptor="Adaptors.CustomAdaptor"></EjsDataManager> 
   <GridEvents OnActionBegin="OnActionBegin" TValue="ListItem"></GridEvents> 
    ... 
</EjsGrid> 
 
 
@code { 
    EjsDataManager DataManagerInstance; 
    EjsGrid<ListItem> GridInstance; 
    ... 
    public void OnActionBegin(Syncfusion.EJ2.Blazor.Grids.ActionEventArgs<ListItem> args) 
    { 
        if (args.RequestType.ToString() == "FilterBeforeOpen") 
        { 
            ColumnFilter = args.ColumnName; 
            JsRunTime.InvokeAsync<object>("filtercolfn");               //Invoke the Js function 
        } 
        ... 
    } 
    public class CustomAdaptor : DataAdaptor 
    { 
        public List<ListItem> asr = new List<ListItem>(); 
        public override object Read(DataManagerRequest dm, string key = null) 
        { 
            var OperatorForFilter = ""; 
            var FieldForFilter = ""; 
            var ds = DataSource.AsQueryable(); 
            var hs = DataSource.AsQueryable(); 
            if (ColumnFilter == "TagName") 
            { 
                return Tags; 
            } 
            else 
            { 
                if (dm.Search != null && dm.Search.Count > 0) ds = DataOperations.PerformSearching(ds, dm.Search); 
                if (dm.Sorted != null && dm.Sorted.Count > 0) ds = DataOperations.PerformSorting(ds, dm.Sorted); 
 
                if (dm.Where != null && dm.Where.Count > 0)     //Create and use your own customized filter to filter the TagName column instead of using Grid's default PerformFiltering method. 
                { 
                    WhereFilter tag = null; 
                    List<string> tagfilter = new List<string>(); 
                //In the below two if and else if statements we have fetched the predicates to be used to filter the TagName column from the DataManagerRequest argument 
                    if (dm?.Where?.LastOrDefault()?.predicates?.LastOrDefault()?.predicates == null) 
                    { 
                        tagfilter.Add(dm?.Where?.LastOrDefault()?.predicates?.LastOrDefault().value.ToString()); 
                        OperatorForFilter = dm?.Where?.LastOrDefault()?.predicates?.LastOrDefault().Operator; 
                        FieldForFilter = dm?.Where?.LastOrDefault()?.predicates?.LastOrDefault().Field; 
                    } 
                    else if (dm?.Where?.LastOrDefault()?.predicates?.LastOrDefault()?.predicates.Count > 1) 
                    { 
                        foreach (var a in dm?.Where?.LastOrDefault()?.predicates?.LastOrDefault()?.predicates) 
                        { 
                            tagfilter.Add(a.value.ToString()); 
                        } 
                        OperatorForFilter = dm?.Where?.LastOrDefault()?.predicates?.LastOrDefault().predicates.LastOrDefault().Operator; 
                        FieldForFilter = dm?.Where?.LastOrDefault()?.predicates?.LastOrDefault().predicates.LastOrDefault().Field; 
                    } 
                    //Use the predicates fetched from the above statements to filter the TagName column. 
                    if (FieldForFilter == "TagName") 
                    { 
                        if (OperatorForFilter == "notequal") 
                        { 
                            foreach (var str in tagfilter) 
                            { 
                                ds = ds.Where(o => !o.TagName.Contains(str)); 
                                asr.AddRange(ds); 
                            } 
                        } 
                        else 
                        { 
                            foreach (var str in tagfilter) 
                            { 
                                ds = hs.Where(o => o.TagName.Contains(str)); 
                                asr.AddRange(ds); 
                            } 
                        } 
                        asr = asr.Distinct<ListItem>().ToList(); 
                        ds = asr.AsQueryable(); 
                    } 
                    //Use PerformFiltering to filter other columns except TagName 
                    else 
                    { 
                        ds = DataOperations.PerformFiltering(ds, dm.Where, dm.Where[0].Operator);        
                    } 
                } 
                ... 
                return dm.RequiresCounts ? new DataResult 
                { 
                    Result = result, 
                    Count = count 
                } : (object)result; 
            } 
        } 
    } 
} 

[filtercol.js] 

function filtercolfn() {              //As by default the filter operator for TagName column will be equal, this will affect the filterchoices for other columns. So we have updated the filter operator for TagName column as contains. As contains is what to be used to filter the List of tags. 
    var gObj = document.getElementById("Filterid").ej2_instances[0]; 
    if (gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns.length) { 
        for (var i = 0; i < gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns.length; i++) { 
            if (gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns[i].field === "TagName") { 
                gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns[i].operator = "contains"; 
            } 
        } 
    } 
} 

 
Note : As we don’t have default support for handling one to many relationship data in Grid. You need to handle the filtering or any other dataoperation actions manually as like what we have done in above code. 

Please get back to us if you need further assistance. 

Regards, 
Renjith Singh Rajendran. 



EC Edward Carandang March 12, 2020 05:15 AM UTC

Dear Renjith,

I don't think we are talking about the same issue. The example you provided still contains the issue that I am raising. Using the example project you provided, upon opening, please try to just open the filter for tag name column. Your filter should look like this

 

This is the expected behavior, where all the tags from the datasource is listed. Now, click on cancel and try to filter the name column, select "Item 1" and "Item 2"and click "Ok" as shown in the screenshot below:



As expected, the rows in the grid are filtered:


Now, try to open the filter for the Tag Name column after filtering the Name column. There are no tag names listed in the filter as shown in the screenshot below:

This is the issue that I am trying to raise. I do have a custom filtering mechanism for 1 to many relationships (albeit not how you are suggesting to do it) in the custom adaptor, but I need to select the items first.

Hope this provided a clearer view of the issue.

Best,
Edward


EC Edward Carandang March 12, 2020 05:27 AM UTC

And also, the feature to perform filtering in one to many columns would be a really cool feature. =)


RS Renjith Singh Rajendran Syncfusion Team March 13, 2020 01:34 PM UTC

Hi Edward, 

Thanks for your update. 

We have modified the sample from our previous update. Please download the sample from the link below, 
 
In the above sample we have added the below highlighted code for the JavaScript function(filtercolfn). And also removed the (CustomFilter = null) lines in OnActionBegin handler and added it in Read method. And invoked a JavaScript method(filtercolfnafter) in OnActionComplete handler to restore the changed operator value. Please refer the code below, 

 
[js] 
 
var flag = true; 
function filtercolfn(colname) { 
    var gObj = document.getElementById("Filterid").ej2_instances[0]; 
    if (gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns.length) { 
        for (var i = 0; i < gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns.length; i++) { 
            if (gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns[i].field === "TagName") { 
                gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns[i].operator = "contains"; 
            } 
            if (colname === "TagName" && gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns[i].operator === "equal") { 
                gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns[i].operator = "notequal"; 
                flag = false; 
            } 
 
        } 
    } 
} 
function filtercolfnafter(colname) {                         //This method will restore the changed operator value in the above highlighted code. 
    var gObj = document.getElementById("Filterid").ej2_instances[0]; 
    if (gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns.length) { 
        for (var i = 0; i < gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns.length; i++) { 
            if (!flag && colname === "filterafteropen") { 
                gObj.filterModule.filterModule.excelFilterBase.options.filteredColumns[i].operator = "equal"; 
            } 
        } 
        flag = true; 
    } 
} 

[razor] 

    public void OnActionBegin(Syncfusion.EJ2.Blazor.Grids.ActionEventArgs<ListItem> args) 
    { 
        if (args.RequestType.ToString() == "FilterBeforeOpen") 
        { 
            ColumnFilter = args.ColumnName; 
            JsRunTime.InvokeAsync<object>("filtercolfn",args.ColumnName);       //Invoke the Js function  
        } 
    } 
    public void OnActionComplete(Syncfusion.EJ2.Blazor.Grids.ActionEventArgs<ListItem> args) 
    { 
        if (args.RequestType.ToString() == "FilterAfterOpen") 
        { 
            JsRunTime.InvokeAsync<object>("filtercolfnafter","filterafteropen");       //Invoke the Js function  
        } 
    } 
    public class CustomAdaptor : DataAdaptor 
    { 
        public List<ListItem> asr = new List<ListItem>(); 
        public override object Read(DataManagerRequest dm, string key = null) 
        { 
            ... 
           if (ColumnFilter == "TagName") 
            { 
                ColumnFilter = null; 
                return Tags; 
            } 
            ... 
       } 
    } 


Query : the feature to perform filtering in one to many columns would be a really cool feature. =) 
We have considered this feature request, and added this to our feature request list. At the planning stage for every release cycle, we review all open features and identify features for implementation based on specific parameters including product vision and technological feasibility. This will be available in any of our upcoming releases.  

You can now track the current status of your request, review the proposed resolution timeline, and contact us for any further inquiries through this link.  
Please get back to us if you need further assistance. 

Regards, 
Renjith Singh Rajendran. 



EC Edward Carandang March 14, 2020 12:34 PM UTC

Dear Renjith,

Thank you for the example you have given. Although to be honest, I'm a bit skeptical of the workaround that you are suggesting. It is very column specific and I'm really not sure why you need to change the operator from "equal" to "notequal" in line 10 of the javascript. Also, it only works if you just have one filter. Will your suggestion work if there are multiple columns where the filter should be customized (like the original post where there are columns for Tags, Class and Shops)?

I really think that it should be as simple as specifying a FilterDataSource in the GridColumn, since this is a pretty common requirement.

Best,
Edward


RS Renjith Singh Rajendran Syncfusion Team March 16, 2020 09:33 AM UTC

Hi Edward, 

Thanks for your update. 

As already informed in our previous updates, we don't have support to perform any DataOperation in a column with one to many relationship data column in Grid. The solution/suggestion provided in our previous updates is a workaround to achieve this requirement by customizing our default Filtering feature. 

And we have already considered to implement this feature request, and added this to our feature request list. Based on specific parameters including product vision and technological feasibility we will implement this feature. This will be available in any of our upcoming releases.   

Until then we appreciate your patience. 

Regards, 
Renjith Singh Rajendran. 


Loader.
Up arrow icon