Skip to content

Custom Filters

Imagine a scenario where you've created a custom Attribute that is the concatenation of two other imagine device Attributes generated automatically by Molten. For instance, a Full Name field which concatenates First Name and Last Name attributes together:

import { Attributes } from '@leverege/ui-attributes'

const options = {
  filterable : true,
  sortable : true
}

export default {
  id : 'person.fullName',
  name : 'person.fullName',
  displayName : 'Full Name',
  objectType : 'person',
  valueType : 'string',
  get : ( obj ) => {
    return `${Attributes.get( 'person.firstName', 'person', obj )} ${Attributes.get( 'person.lastName', 'person', obj )}`
  },
  getOption : ( key ) => {
    return options[key]
  }
}
We will now be able to view our new custom attribute in tables and in detail cards throughout our UI. Our first name and last name attributes are already filterable because they were generated by Molten and our api has some knowledge of how to filter those fields. However, our new Full Name attribute only exists on the UI side and has no equivelant on the server side. This does not stop us from being able to filter it though.

First, we will need to register a custom comparator against our new Attribute:

import {StringEqualsComparatorEditor} from '@leverege/ui-attributes';

const TYPE = 'person.fullName.equals'

const FullNameEqualsModel = {
  create() {
    return {
      type : TYPE,
      value1 : null
    }
  },
  TYPE,
  ...ModelUtil.createAllValue( 'value1' ),
  name : 'equals'
}

molten.addPlugin( 'AttributeConditionComparator', {
  type : FullNameEqualsModel.TYPE,
  model : FullNameEqualsModel,
  name : FullNameEqualsModel.name,
  attrName : 'person.fullName'
} )

molten.addPlugin( 'AttributeConditionComparatorEditor', { type : FullNameEqualsModel.type, editor : StringEqualsComparatorEditor } )
Registering this comparator using attrName will ensure that when editing AttributeConditions for this Attribute, only comparators registered against the attribute name (this comparator, for instance) and not the more generic built-in valueType comparators will be available for selection.

Note

Here we are using the StringEqualsComparatorEditor from the @leverege/ui-attribute library as an editor for our comparator's model since the two models are essentially identical and this saves us a bit of work.

However, this comparator won't show up in our condition editor just yet, and that is because we need to regsiter a ComparatorSearchConverter to convert this comparator model into a Data Source Specific Filter Model.

import { Attributes } from '@leverege/ui-attributes'

export default {
  id : 'custom.person.fullName.Equals.ComparatorFilterConverter',
  type : 'groupDelegate.imagine.person.fullName.equals.ComparatorFilterConverter',
  convert : ( type, cmp, attribute ) => {
    if ( cmp?.value1 == null ) {
      return null
    }

    const firstNameAttr = Attributes.getAttribute( 'person.firstName', 'person' )
    const lastNameAttr = Attributes.getAttribute( 'person.lastName', 'person' )

    const [ firstName, lastName ] = cmp.value1.split( ' ' )

    return {
      type : 'logical',
      operator : 'and',
      conditions : [ {
        type : 'equals',
        value : firstName,
        field : firstNameAttr.blueprint.field
      }, {
        type : 'equals',
        value : lastName,
        field : lastNameAttr.blueprint.field
      } ]
    }
  }
}
This ConditionFilterConverter creates a complex filter which ensures that both the first name and last name components of our custom attribute match the first and last name fields on the Imagine device.