Skip to Content
Technical Articles

How to enable Smart Variant Management for Custom Controls

Have you ever wondered how to enable Smart Variant Management for your custom controls?

Then you are lucky because this guide will show you how to do it!Ā šŸŽ‰

Before we start let’s have a look at the documentationĀ on how to use the Smart Variant Management:

The sap.ui.comp.smartvariants.SmartVariantManagement control provides an interface to enable a simple integration of the sap.ui.comp.variants.VariantManagement control and access to the layered repository of SAPUI5 flexibility for easy communication.

What this simply means is that you can persist the current state of your control and application in the so called Layer Repository as part of the SAPUI5 Flexibility ServicesĀ using the Smart Variant Management control.

This feature is mainly used by e.g.Ā Smart Filter BarĀ orĀ Smart TableĀ but can also be implemented by any custom control.

So let’s get started by creating a new XML Composite Control:

InputWithSmartVariantManagement.control.xml

<core:FragmentDefinition
    xmlns:core="sap.ui.core"
    xmlns="sap.m">
  <Input
      id="idInput"
      width="{$this>width}"
      value="{$this>value}"
      change="onChange"/>
</core:FragmentDefinition>

InputWithSmartVariantManagement.js

sap.ui.define([
  'sap/ui/core/XMLComposite',
  'sap/base/Log'
], (
  XMLComposite,
  Log
) => XMLComposite.extend('com.sap.example.InputWithSmartVariantManagement', {

  metadata: {

    properties: {

      width: {
        type: 'sap.ui.core.CSSSize',
        defaultValue: null,
        invalidate: true
      },

      value: {
        type: 'string',
        defaultValue: null,
        invalidate: true
      }

    },

    events: {

      change: {
        value: {
          type: 'string'
        }
      }

    }

  },

  init (...args) {
    XMLComposite.prototype.init.apply(this, args)
    this.log = Log.getLogger(this.getMetadata().getName())
    this.input = this.byId('idInput')
  },

  exit (...args) {
    XMLComposite.prototype.exit.apply(this, args)
    this.log = null
    this.input = null
  },

  onChange () {
    const value = this.getValue()
    this.fireChange({
      value
    })
  }
}))

With this in place the first step is now to get a reference to our SmartVariantManagement instance.

We can do this by adding an associationĀ smartVariantĀ to our control:

  associations: {

     smartVariant: {
       type: 'sap.ui.comp.smartvariants.SmartVariantManagement',
       multiple: false
     }

  },

Next we have to register and initialize our control with the SmartVariantManagement instance whenever the association is being set.

We can do this by overriding the generated setSmartVariant method which is just a shorthand for this.setAssociation(‘smartVariant’, smartVariant):

  setSmartVariant (smartVariant) {
    this._registerSmartVariantManagement(smartVariant)
    this._initialiseSmartVariantManagement()
    return this.setAssociation('smartVariant', smartVariant)
  },

In _registerSmartVariantManagementĀ we simply resolve the association and get the actual reference to the SmartVariantManagement instance:

_registerSmartVariantManagement (smartVariant) {
    if (!smartVariant) {
      return
    }
    if (typeof smartVariant === 'string') {
      this.smartVariantManagement = sap.ui.getCore().byId(smartVariant)
    } else if (smartVariant instanceof SmartVariantManagement) {
      this.smartVariantManagement = smartVariant
    } else {
      this.log.error('Invalid association: smartVariant', JSON.stringify(smartVariant))
    }
  },

In _initialiseSmartVariantManagement we have to create a PersonalizibleInfoĀ instance for our control and add this to the corresponding aggregation of the SmartVariantManagement instance.

Finally we have to call SmartVariantManagement.initialise.

It is important to mention here that the approach using the initialise event which is still being described in the documentationĀ has been deprecated as of SAPUI5 1.38.0.

_initialiseSmartVariantManagement () {
    if (!this.smartVariantManagement) {
      return
    }
    const oPersonalizableInfo = new PersonalizableInfo({
      type: 'control', // can be any type!? SmartTable sets 'table' here
      keyName: 'persistencyKey', // must match the control's property name
      dataSource: 'TODO' // can be any type!? e.g. SmartTable sets 'TODO' here
    })
    oPersonalizableInfo.setControl(this)
    this.smartVariantManagement.addPersonalizableControl(oPersonalizableInfo)
    this.smartVariantManagement.initialise(() => this.onInitialiseVariant(), this)
  },

  onInitialiseVariant () {
    // noop
  },

The last step is now to implement the interface methods which will be called by the SmartVariantManagement instance to fetch and apply the variant for our custom control.

  variantsInitialized () {
    // noop
  },

  fetchVariant () {
    const value = this.getValue()
    const variant = {
      value
    }
    return variant
  },

  applyVariant (variant = null) {
    this.variant = variant
    const variantValue = this.variant?.value
    const value = this.getValue()
    if (variantValue && variantValue !== value) {
      this.setValue(variantValue)
      this.onChange()
    }
  },

Additionally we need to mark theĀ SmartVariantManagement instance as modified whenever our input value changes.

The SmartVariantManagement instance will highlight this by showing a * (asterisk).

  onChange () {
    const value = this.getValue()
    const variantValue = this.variant?.value
    if (this.smartVariantManagement && variantValue !== value) {
      this.smartVariantManagement.currentVariantSetModified(true)
    }
    this.fireChange({
      value
    })
  }

All put together the final implementation for our custom control will look like this:

sap.ui.define([
  'sap/ui/core/XMLComposite',
  'sap/base/Log',
  'sap/ui/comp/smartvariants/SmartVariantManagement',
  'sap/ui/comp/smartvariants/PersonalizableInfo'
], (
  XMLComposite,
  Log,
  SmartVariantManagement,
  PersonalizableInfo
) => XMLComposite.extend('com.sap.example.InputWithSmartVariantManagement', {

  metadata: {

    properties: {

      width: {
        type: 'sap.ui.core.CSSSize',
        defaultValue: null,
        invalidate: true
      },

      value: {
        type: 'string',
        defaultValue: null,
        invalidate: true
      },

      persistencyKey: {
        type: 'string',
        defaultValue: null,
        invalidate: true
      }

    },

    associations: {

      smartVariant: {
        type: 'sap.ui.comp.smartvariants.SmartVariantManagement',
        multiple: false
      }

    },

    events: {

      change: {
        value: {
          type: 'string'
        }
      }

    }

  },

  init (...args) {
    XMLComposite.prototype.init.apply(this, args)
    this.log = Log.getLogger(this.getMetadata().getName())
    this.input = this.byId('idInput')
    this.smartVariantManagement = null
  },

  exit (...args) {
    XMLComposite.prototype.exit.apply(this, args)
    this.log = null
    this.input = null
    this.smartVariantManagement = null
  },

  setSmartVariant (smartVariant) {
    this._registerSmartVariantManagement(smartVariant)
    this._initialiseSmartVariantManagement()
    return this.setAssociation('smartVariant', smartVariant)
  },

  _registerSmartVariantManagement (smartVariant) {
    if (!smartVariant) {
      return
    }
    if (typeof smartVariant === 'string') {
      this.smartVariantManagement = sap.ui.getCore().byId(smartVariant)
    } else if (smartVariant instanceof SmartVariantManagement) {
      this.smartVariantManagement = smartVariant
    } else {
      this.log.error('Invalid association: smartVariant', JSON.stringify(smartVariant))
    }
  },

  _initialiseSmartVariantManagement () {
    if (!this.smartVariantManagement) {
      return
    }
    const personalizableInfo = new PersonalizableInfo({
      type: 'control',
      keyName: 'persistencyKey',
      dataSource: 'TODO'
    })
    personalizableInfo.setControl(this)
    this.smartVariantManagement.addPersonalizableControl(personalizableInfo)
    this.smartVariantManagement.initialise(() => this.onInitialiseVariant(), this)
  },

  onInitialiseVariant () {
    // noop
  },

  variantsInitialized () {
    // noop
  },

  fetchVariant () {
    const value = this.getValue()
    const variant = {
      value
    }
    return variant
  },

  applyVariant (variant = null) {
    this.variant = variant
    const variantValue = this.variant?.value
    const value = this.getValue()
    if (variantValue && variantValue !== value) {
      this.setValue(variantValue)
      this.onChange()
    }
  },

  onChange () {
    const value = this.getValue()
    const variantValue = this.variant?.value
    if (this.smartVariantManagement && variantValue !== value) {
      this.smartVariantManagement.currentVariantSetModified(true)
    }
    this.fireChange({
      value
    })
  }
}))

And to conclude here is an example of how the SmartVariantManagement and the custom control will work together:

<core:FragmentDefinition
    xmlns:core="sap.ui.core"
    xmlns:smartvariants="sap.ui.comp.smartvariants"
    xmlns:example="com.sap.example"
    xmlns="sap.m">
  <smartvariants:SmartVariantManagement
      id="idSmartVariantManagement"
      persistencyKey="pKeyPageVariantManagement"
      lifecycleSupport="true"
      showExecuteOnSelection="true"
      showShare="true"/>
  <example:InputWithSmartVariantManagement
      id="idInputWithSmartVariantManagement"
      width="6rem"
      smartVariant="idSmartVariantManagement"
      persistencyKey="pKeyInputWithSmartVariantManagement"
      change="onChange"/>
</core:FragmentDefinition>

šŸ‘¾ Happy Coding! šŸ‘¾

Be the first to leave a comment
You must be Logged on to comment or reply to a post.