1. #1
    Sencha User
    Join Date
    Apr 2010
    Location
    Bogotá Colombia
    Posts
    5
    Vote Rating
    0
    gersonjohan is on a distinguished road

      0  

    Default Custom Filter field with different value than ValueProvider path

    Custom Filter field with different value than ValueProvider path


    I have a grid column associated to a calculated bean field (original field is a collection 'roles', calculated field 'rolesString' is a comma separed values of role items).

    Code:
    @Entity
    public class User {
    ...
    private List<Role> roles;
    ...
    @Transcient public String getRolesString() {
    return StringUtil.collectionToString(roles, ", ");
    }
    ... }
    The value provider of column config and filter has "rolesString" as field path. I created a custom filter that overrides the "getFilterConfg" method, to change the filter field to "roles":

    Code:
     @Override
     public List<FilterConfig> getFilterConfig() {
      
      FilterConfig config = new EntityFilterConfig<Role>();
      
      config.setField("roles");
      config.setType(field.getValue().getClass().getName());
      config.setComparison("member of");
      config.setValue(field.getValue().toString());
      ((EntityFilterConfig<Role>) config).setTypedValue(field.getValue());  
      return Collections.singletonList(config);
     }
    I don`t know why when the query filter is built, in the "buildQuery" method of "AbstractGridFilters" class, the field name is again replaced by the path of value provider:

    Code:
      public List<FilterConfig> buildQuery(List<Filter<M, ?>> filters) {
        List<FilterConfig> configs = new ArrayList<FilterConfig>();
        for (Filter<M, ?> f : filters) {
          List<FilterConfig> temp = f.getFilterConfig();
          for (FilterConfig tempConfig : temp) {
            tempConfig.setField(f.getValueProvider().getPath());
            configs.add(tempConfig);
          }
        }
        return configs;
      }

  2. #2
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,717
    Vote Rating
    89
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    The purpose of Filter.getFilterConfig is to build out what that particular filter has been given as its criteria - the comparison and the value, plus the type that that particular subclass is built to handle. It can make more than one, so it is allowed to return a list. At present, AbstractGridFilters.buildQuery calls that and finishes off the work by setting the field that each config is acting on. This is probably something we could change in 3.1, but it is a breaking change as it might break custom Filter types that do not declare a field, since existing code all assumes that AbstractGridFilters will do this work automatically.

    In your case, I would instead use a custom ValueProvider that returns the path you actually want it to set. This could come in handy in other areas as well - that getPath is used for both filtering and sorting when talking to the server, as well as simple comparison on the client when checking of two things are talking about the same property. Being consistent in this area is a good thing, it'll prevent hidden bugs later.

    The simplest way to do this if doing it just once would be to implement ValueProvider yourself, and do the getValue/setValue methods as would normally be generated. I'd probably go so far as to delete the getRolesString() method in your object, unless it is used elsewhere - getValue can generate that automatically. The setValue method *probably* doesn't do anything, though I can't tell from the code you've pasted. getPath is where you will return your custom path string.

    If this happens pretty regularly, you could make a ValueProvider that wraps another ValueProvider, and calls into it for setValue and getValue, but returns its own path. Again, may not make sense for this use case, since all you really want is getPath and getValue to read out one property as another type.

  3. #3
    Sencha User
    Join Date
    Apr 2010
    Location
    Bogotá Colombia
    Posts
    5
    Vote Rating
    0
    gersonjohan is on a distinguished road

      0  

    Default


    Thanks Colin,

    I personally don´t lik to create many classes for value providers. My workaround was to override the builQuery method to validate if the FilterConfig already has a field setted:
    Code:
     @Override
     public List<FilterConfig> buildQuery(List<Filter<T, ?>> filters) {
      List<FilterConfig> configs = new ArrayList<FilterConfig>();
      for (Filter<T, ?> f : filters) {
       List<FilterConfig> temp = f.getFilterConfig();
       for (FilterConfig tempConfig : temp) {
        if (tempConfig.getField() == null || tempConfig.getField().isEmpty()) { //workaround
         tempConfig.setField(f.getValueProvider().getPath());
        }
        configs.add(tempConfig);
       }
      }
      return configs;
     }
    This lets me to separate the field path of column config from the filter field, if I need.

    Moreover, I think that all filters sub-classes should set then field name in the FilterConfig. By default, from the path of the value provider.
    Regards,
    Gerson Samaniego

  4. #4
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,717
    Vote Rating
    89
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    It's a reasonable idea, but like I said, this will break existing code in unexpected ways, so we'll need to be careful about this.

    Remember, you could write a ValueProvider once and reuse it, assuming you already can get a ValueProvider from some other source like PropertyAccess

    Code:
    public class CustomPathValueProvider<T,V> implements ValueProvider<T,V> {
      private final String path;
      private final ValueProvider<T,V> wrapped;
      public CustomPathValueProvider(String path, ValueProvider<T,V> vp) {
        this.path = path;
        this.wrapped = vp;
      }
      public V getValue(T object) {
        return wrapped.getValue(object);
      }
      public void setValue(T object, V value) {
        wrapped.setValue(object, value);
      }
      public String get Path() {
        return path;
      }
    }
    This then could be used to wrap an existing one:

    Code:
    StringFilter<MyData> filter = new StringFilter<MyData>(
        new CustomPathValueProvider<MyData, String>("custom-path-name", props.name())
        );