PDA

View Full Version : [CLOSED] How can I filter a GridPanel that has a mapped editor field?



Caleb
Apr 21, 2017, 11:41 PM
I'm trying to setup a GridPanel like the Ext.net example (https://examples.ext.net/#/GridPanel/Data_Presentation/Editor_Field_Mapping/) for "Editor Field Mapping".

Basically, the situation requires a dictionary obj similar to the StoreCombo in the example code.
Whenever an editable field is constrained by a DB foreign key:

we want this dictionary to hold all valid choices, so anyone making an edit can simply use a dropdown box to pick from them.
And we also want to provide our JS code with a convenient mapping between the value's DB ID and a more user friendly description.

Similar to this:
24918

However, our users also need to be able to filter the grid on these mapped editor-fields, like this other ext.net example.
24919

My understanding is that although we've configured our rendering to display the user-description, the mapped editor field really links to the underlying DB ID. This means the filter actually tries to check the DB ID, which is not helpful to any users.
Is there a solution to reconfigure our filter to work against the user-descriptions? Client side filtering is our primary concern at the moment.

fabricio.murta
Apr 22, 2017, 6:45 PM
Hello @Caleb, and welcome to Ext.NET forums!

Well, in your case what you have on your store for that column is the ID only. So no, there's no way you can filter by the custom-rendered value using the column dropdown's filter. It would require it to be customized in order to somehow build the list of companies (which is probably not on the grid model at all) and try and filter it by applying filters on the store allowing those matching IDs to be displayed.

What could be just possible would be using the filter header instead and customizing the filter behavior (https://examples4.ext.net/#/GridPanel/FilterHeader/Custom_Behaviour/), such as:
1. the grid model will also include a field to store the chosen department name
2. it would be also filled when the renderer updates the contents of the cell (so they both have the same value, always)
3. the filter custom logic applied to the column would instead match that record

Unfortunately the filter at the header pop-up is not as customizable as the filter header functionality. It can be, yes, but that would be pretty much fiddling with a new functionality and goes beyond the scope here (as a quick and supported solution, at least).

If you try the described approach above, please start on a simplified code sample so, if you get in trouble trying to develop it, you can provide the code here so we can help you out.

When/if providing test cases, please follow the guidelines posted here: Tips for creating simplified code samples (http://forums.ext.net/showthread.php?61176-Tips-for-creating-simplified-code-samples)

Hope this helps!

Caleb
Apr 24, 2017, 2:40 PM
@fabricio.murta, hey thanks for the quick reply over the weekend.
Will look into the FilterHeader suggestion and see if that's something we can make work. I'll follow up soon.

fabricio.murta
Apr 24, 2017, 6:46 PM
Hello @caleb!

Looking forward to your follow-up. Hope you can make it work the way you need it to. I believe changing the behavior of the filter in the dropdowns will be quite troublesome.

Caleb
Apr 24, 2017, 11:06 PM
Ok, so here's what we came up with, which seems to do a pretty good job of satisfying our needs:
We have a gridpanel, which has a store for whatever business data it has to display
Because any FK constrained data can get a new value assigned from the user, we decided to keep things safe and simple by tracking only their DB ID on the gridpanel's store.

Thus the dropdown editor we add to these fields becomes responsible for masking the DB id by using it to figure out the display-description from its own store of valid values defined for that datatype.

The gridfilter plugin is a better visual and functional fit for us than the filterheader, so we coded a customization for its filter handler (filterFn). This new logic checks a user's filter criteria against the display-description, by translating the DB ID against the lookup store which our dropdown editor originally used.
However, we couldn't find any obvious property that exposed this filterFn directly. So we got to it by using the more generic CustomConfig property. Is this intentional?

It'd also be really nice to have our filter dynamically switch between localized vs remote server-side filtering, according to how large our dataset becomes. Is there a way to set this or to otherwise customize the logic? Thanks!

fabricio.murta
Apr 25, 2017, 4:43 AM
Hello @caleb!

I'm not sure what you have done in the page to attain what you want just by your description, if you provide a test case using the custom filter you did, I believe it will be easier to elaborate on an answer, but the CustomConfig is there to allow the user to have flexibility of scenarios that the server-side controls and C# mapping didn't (or couldn't) cover.

Usually this is not needed unless a customization is intended to a component. Or to overcome a bug when it happens.

About dynamically switching between local and remote filtering, you could do that if you could determine the amount of data at load time i.e. on Page_Load(). If you want to get deeper on this specific matter I'd ask you to open a new thread so we keep one subject per thread (making it easier to browse for future reference).

Hope this helps!

Caleb
Apr 25, 2017, 11:24 PM
Hi Fabricio,

Here's the simplified markup code for what we were considering. Please note the question in our in-line comment at line 19 of this:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="GridPanelTest.aspx.cs" Inherits="_GridPanelTest" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>MultiFeatureGridPanel</title>
<script>
var lookupDisplayVal = function (IDValue) {
var mappingEntry = App.MappingStore.getById(IDValue);

if (Ext.isEmpty(mappingEntry)) {
return IDValue;
}
return mappingEntry.data.DisplayVal;
};

var filterFn_DisplayVal = function (gridrow, filterVal) {
//in a non-hardcoded situation with undetermined number of columns, is there a good way for this filterFn handler to identify which gridview column links to the filterVal?
var lookupVal = lookupDisplayVal(gridrow.data.FK_ID);

if (Ext.isEmpty(lookupVal)) {
return false;
}

return (lookupVal.toLowerCase().indexOf(filterVal.toLower Case()) > -1);
};
</script>
</head>
<body>
<form runat="server">
<ext:ResourceManager runat="server" />

<ext:Store ID="MappingStore" runat="server">
<Model>
<ext:Model runat="server" IDProperty="IDVal">
<Fields>
<ext:ModelField Name="IDVal" Type="Int" />
<ext:ModelField Name="DisplayVal" Type="String" />
</Fields>
</ext:Model>
</Model>
</ext:Store>


<ext:GridPanel Title="MultiFeatureGridPanel" ID="MultiFeatureGridPanel" runat="server" Width="650" Height="325">
<Store>
<ext:Store ID="BusinessDataStore" runat="server">
<Model>
<ext:Model runat="server">
<Fields>
<ext:ModelField Name="businessVal" Type="String" />
<ext:ModelField Name="FK_ID" Type="Int" />
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<ColumnModel runat="server">
<Columns>
<ext:Column runat="server" Text="ExampleVals" DataIndex="businessVal">
<Filter> <ext:StringFilter /> </Filter>
</ext:Column>

<ext:Column runat="server" Text="Masked FK Editor" DataIndex="FK_ID">
<Filter>
<ext:StringFilter>
<CustomConfig>
<ext:ConfigItem Name="filterFn" Value="filterFn_DisplayVal" Mode="Raw" />
</CustomConfig>
</ext:StringFilter>
</Filter>
<Renderer Fn="lookupDisplayVal" />
<Editor>
<ext:ComboBox
runat="server"
QueryMode="Local"
Editable="true"
StoreID="MappingStore"
DisplayField="DisplayVal"
ValueField="IDVal"
/>
</Editor>
</ext:Column>
</Columns>
</ColumnModel>
<Plugins>
<ext:CellEditing runat="server" />
<ext:GridFilters runat="server" />
</Plugins>
</ext:GridPanel>

</form>
</body>
</html>

And here is the codebehind .cs for it. Please note the observation about exposing filter customization properties on line 37 and 40. Would be really convenient to have something so we don't risk messing things up by using the customConfig, but maybe we missed something? Your input is much appreciated!


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Ext.Net;

public partial class _GridPanelTest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
MappingStore.DataSource = new object[]
{
new object[] {0, "Some"},
new object[] {1, "Something"},
new object[] {2, "Somehow"},
new object[] {3, "Someway"},
new object[] {4, "Someone"},
};
MappingStore.DataBind();

BusinessDataStore.DataSource = new object[]
{
new object[] {"Record A: Some", 0},
new object[] {"Record B: Something", 1},
new object[] {"Record C: Some", 0},
new object[] {"Record D: Something", 1},
new object[] {"Record E: Somehow", 2},
new object[] {"Record F: Some", 0}
};
BusinessDataStore.DataBind();

//access the 2nd column's filter(s)
GridFilterCollection maskedFilters = MultiFeatureGridPanel.ColumnModel.Columns[1].Filter;

//convert first one into an expicit StringFilter. Doesn't seem to expose any properties to have it filter against our drop down editor's rendered val
StringFilter convertedSF = (StringFilter)maskedFilters[0];

//as an abstract GridFilter, doesn't seem to expose such a property either
GridFilter abstractFilter = maskedFilters[0];
}

}

fabricio.murta
Apr 26, 2017, 12:13 AM
Hello @Caleb!

Impressive! Thanks for the simple case, we're looking after the issue to advice you about the filter customization. Just wanted to leave you an useful tip for providing complete test cases with code behind, yet all in just one code block. Easier for both test after change -- no rebuild necessary -- and also easier to copy+paste+run if we exchange versions of the reviewed test case.



<%@ Page Language="C#" %>

<!DOCTYPE html>

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
MappingStore.DataSource = new object[]
{
new object[] {0, "Some"},
new object[] {1, "Something"},
new object[] {2, "Somehow"},
new object[] {3, "Someway"},
new object[] {4, "Someone"},
};
MappingStore.DataBind();

BusinessDataStore.DataSource = new object[]
{
new object[] {"Record A: Some", 0},
new object[] {"Record B: Something", 1},
new object[] {"Record C: Some", 0},
new object[] {"Record D: Something", 1},
new object[] {"Record E: Somehow", 2},
new object[] {"Record F: Some", 0}
};
BusinessDataStore.DataBind();

//access the 2nd column's filter(s)
GridFilterCollection maskedFilters = MultiFeatureGridPanel.ColumnModel.Columns[1].Filter;

//convert first one into an expicit StringFilter. Doesn't seem to expose any properties to have it filter against our drop down editor's rendered val
StringFilter convertedSF = (StringFilter)maskedFilters[0];

//as an abstract GridFilter, doesn't seem to expose such a property either
GridFilter abstractFilter = maskedFilters[0];
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>61872 - Custom header dropdown filter (user provided)</title>
<script type="text/javascript">
var lookupDisplayVal = function (IDValue) {
var mappingEntry = App.MappingStore.getById(IDValue);

if (Ext.isEmpty(mappingEntry)) {
return IDValue;
}
return mappingEntry.data.DisplayVal;
};

var filterFn_DisplayVal = function (gridrow, filterVal) {
//in a non-hardcoded situation with undetermined number of columns, is there a good way for this filterFn handler to identify which gridview column links to the filterVal?
var lookupVal = lookupDisplayVal(gridrow.data.FK_ID);

if (Ext.isEmpty(lookupVal)) {
return false;
}

return (lookupVal.toLowerCase().indexOf(filterVal.toLower Case()) > -1);
};
</script>
</head>
<body>
<form runat="server">
<ext:ResourceManager runat="server" />

<ext:Store ID="MappingStore" runat="server">
<Model>
<ext:Model runat="server" IDProperty="IDVal">
<Fields>
<ext:ModelField Name="IDVal" Type="Int" />
<ext:ModelField Name="DisplayVal" Type="String" />
</Fields>
</ext:Model>
</Model>
</ext:Store>


<ext:GridPanel Title="MultiFeatureGridPanel" ID="MultiFeatureGridPanel" runat="server" Width="650" Height="325">
<Store>
<ext:Store ID="BusinessDataStore" runat="server">
<Model>
<ext:Model runat="server">
<Fields>
<ext:ModelField Name="businessVal" Type="String" />
<ext:ModelField Name="FK_ID" Type="Int" />
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<ColumnModel runat="server">
<Columns>
<ext:Column runat="server" Text="ExampleVals" DataIndex="businessVal">
<Filter> <ext:StringFilter /> </Filter>
</ext:Column>

<ext:Column runat="server" Text="Masked FK Editor" DataIndex="FK_ID">
<Filter>
<ext:StringFilter>
<CustomConfig>
<ext:ConfigItem Name="filterFn" Value="filterFn_DisplayVal" Mode="Raw" />
</CustomConfig>
</ext:StringFilter>
</Filter>
<Renderer Fn="lookupDisplayVal" />
<Editor>
<ext:ComboBox
runat="server"
QueryMode="Local"
Editable="true"
StoreID="MappingStore"
DisplayField="DisplayVal"
ValueField="IDVal"
/>
</Editor>
</ext:Column>
</Columns>
</ColumnModel>
<Plugins>
<ext:CellEditing runat="server" />
<ext:GridFilters runat="server" />
</Plugins>
</ext:GridPanel>

</form>
</body>
</html>

fabricio.murta
Apr 26, 2017, 12:56 AM
Hello again!

You are using a feature that is not documented even as a protected/private feature in ExtJS. I dare say this "works by accident" but it really looks like a nice way to customize a filter.

So, according to ExtJS documentation on the gridfilters' string filter (http://docs.sencha.com/extjs/6.2.1/classic/Ext.grid.filters.filter.String.html), we can't really support this on Ext.NET, simply cause it is not guaranteed nor supported through versions of ExtJS.

May be confusing to understand the claim, as the feature is there, but here's an example of ExtJS claim on private classes: Documentation on Ext.grid.CellEditor for ExtJS 6.2.1 (http://docs.sencha.com/extjs/6.2.1/classic/Ext.grid.CellEditor.html).

That said, I'm afraid you should rely on the approach you taken. This you have done could be seen as extending the grid String filter class, as using a "custom filter" for a "string filter" is pretty much not using the "string filter" itself.

At the same time, contradicting myself, you shouldn't rely on that to be working over different versions of Ext.NET. As a non-documented feature, it is bound to be changed without prior notice nor any breaking change document (which we make considerable efforts to maintain and avoid -- when documented and supported feature).

Bottomline, I'd say, use the alternative you found. It works fine, but you'll probably want to leave some comments to review it whenever a version upgrade is made. There's no such "feature" of specifying the filter function unless you extend both Ext.NET and ExtJS (extending basically the Ext.grid.filters.filter's Base (http://docs.sencha.com/extjs/6.2.1/classic/Ext.grid.filters.filter.Base.html) or SingleFilter (http://docs.sencha.com/extjs/6.2.1/classic/Ext.grid.filters.filter.SingleFilter.html) abstract classes from ExtJS). The alternative you are using is the shortest path to what you need.

Hope this helps!

fabricio.murta
May 12, 2017, 11:15 PM
Hello @Caleb!

Been some time since we last replied you here and still no feedback from you. Do you still need help on this matter?

We may mark this as closed if you don't reply in 7+ days. But don't worry, this will not prevent you from posting more follow-ups when you feel convenient.

Caleb
May 17, 2017, 2:02 PM
Hi Fabricio, thank you for checking in on this. Yes please go ahead and close out for now. This solution seems to work!

fabricio.murta
May 17, 2017, 9:16 PM
Hello @caleb! Thanks for your feedback!