17

Is there a way to get an SObject (Tab) icon (name) to be referenced from the design system resource?

I'm building a custom visualforce page that will be displayed using Lightning, so I need to style it accordingly. My problem is that I really don't know what is the best practice to get the icon or at least it's name so I can reference the design system resource, here is an example:

I have a visualforce page with an icon as:

<span class="slds-icon_container slds-icon_container--circle slds-icon-action-description">
    <svg aria-hidden="true" class="slds-icon slds-icon--small">
        <use xlink:href="{!URLFOR($Resource.SLDS100,'assets/icons/custom-sprite/svg/symbols.svg#custom44')}"></use>
   </svg>
</span>

And I need to do something like this:

<span class="slds-icon_container slds-icon_container--circle slds-icon-action-description">
    <svg aria-hidden="true" class="slds-icon slds-icon--small">
        <use xlink:href="{!URLFOR($Resource.SLDS100,iconName)}"></use>
   </svg>
</span>

Where iconName is the URL of the svg icon of the current standardController object, right now I'm getting it from the extension like (not exactly):

public String getIconName(){
    List<Schema.DescribeTabSetResult> tabSetDesc = Schema.describeTabs();
    List<Schema.DescribeTabResult> tabDesc = new List<Schema.DescribeTabResult>();
    List<Schema.DescribeIconResult> iconDesc = new List<Schema.DescribeIconResult>();

    for(Schema.DescribeTabSetResult tsr : tabSetDesc) {
        tabDesc.addAll(tsr.getTabs());
    }

    for(Schema.DescribeTabResult tr : tabDesc) {
        if (tr.getSobjectName() == 'Custom__c') {
            iconDesc.addAll(tr.getIcons());
        }
    }

    String u;
    for (Schema.DescribeIconResult ir : iconDesc) {
        if (ir.getContentType() == 'image/svg+xml'){
            u = ir.getUrl();
            break;
        }
    }
    Integer s = u.indexOf('/custom/') + '/custom/'.length(),
            e = u.length() - 4;
    return 'assets/icons/custom-sprite/svg/symbols.svg#' + u.substring(s, e);
}

But with lightning in mind, it really looks like there should be a better way to achieve this (without all those loops), and this workaround doesn't work for a custom icon.

Notes:

  1. Similar question
  2. This needs to work in a managed package, where the icon changes from org to org.
1

3 Answers 3

6

For any of those who may be looking for an even more generic solution, or possibly a use case where you are simply using the SLDS styles in a VF page, I have a method which returns the lightning style class for the wrapper of the tab icon, in addition to its Lightning png icon to fill it. This is supported in a managed package, and will also pull custom icons that are defined by customers/packages installed. If an object is passed in that does not have a tab definition, it simply returns the SLDS Custom icon as a default. My use case required dynamic loading of the icon depending on a configuration for the object, so a dynamic IMG binding was much more realistic to switch up on the fly than repainting an SVG.

Method that takes in sObject Name and returns a map of properties (iconStyle, iconURL)

/***
 * Explores the schema of the soject passed in and finds lightning icon style and image url for sObject
 * If nothing is found, defaults to the custom icon
 *
 * @param   sObjectTypeName       the sObject to find the icon for
 * @return  Map<String,String>    Map of properties about the icon to be consumed by the front end.
 *                                Right now, returns values for keys: iconStyle,iconURL
 */
global static Map<String,String> retrieveIconForObject(String sObjectName) {    
    String iconStyle;
    String iconURL;
    String iconTag;
    Map<String,String>                iconPropertyMap = new Map<String,String>();
    List<Schema.DescribeTabSetResult> tabSetDesc      = Schema.describeTabs();
    List<Schema.DescribeTabResult>    tabDesc         = new List<Schema.DescribeTabResult>();
    List<Schema.DescribeIconResult>   iconDesc        = new List<Schema.DescribeIconResult>();

    for(Schema.DescribeTabSetResult tsr : tabSetDesc){
       tabDesc.addAll(tsr.getTabs()); 
    }
    for(Schema.DescribeTabResult tr : tabDesc) {
        if( sObjectName == tr.getSobjectName() ) {
            system.debug(tr.getIcons());
            if(!tr.getIcons().isEmpty()){
                iconDesc.addAll(tr.getIcons());
            }else{
                if( tr.isCustom() == true ) {
                    iconStyle = 'slds-icon-standard-custom';
                    iconURL   = '/img/icon/t4v35/standard/custom_120.png';
                } else {
                    iconStyle = 'slds-icon-standard-' + sObjectName.toLowerCase();
                    iconURL   = '/img/icon/t4v35/standard/' + sObjectName.toLowerCase() + '_120.png';
                }
            }
        }
    }
    for (Schema.DescribeIconResult ir : iconDesc) {
        if(ir.getTheme() =='custom' && !ir.getContentType().startsWith('image/svg')){//custom icon thats not an svg
            iconTag   = 'custom';
            iconURL   = ir.getURL();
            iconStyle = 'slds-icon-standard-custom';
            break;
        }else if(ir.getWidth() == 120 && ir.getTheme() =='theme4' && ir.getContentType() == 'image/png'){//large lightning icon image
            if(ir.getURL().contains('/custom/')){ //Icon is from the Custom Sprite
                iconTag    = ir.getURL().substringBetween('custom/','.png').substringBefore('_');
                iconURL    = '/img/icon/t4v35/custom/' + iconTag + '_120.png';
                iconStyle  = 'slds-icon-custom-'+ iconTag;
                break;
            }else if(ir.getURL().contains('/standard/')){//Icon is from the Standard Sprite
                iconTag    = ir.getURL().substringBetween('standard/','.png').substringBefore('_');
                iconURL    = '/img/icon/t4v35/standard/' + iconTag + '_120.png';
                iconStyle  = 'slds-icon-standard-'+ iconTag;
                break;
            }
        }
    }
    //if no icons are found, just default to the Custom lightning icon
    if(iconStyle == null){
        iconStyle = 'slds-icon-standard-custom';
        iconURL   = '/img/icon/t4v35/standard/custom_120.png';
        iconTag   = 'No Lightning Icon Found';
    }
    //return a map with our properties for the front end
    iconPropertyMap.put('iconStyle',iconStyle);
    iconPropertyMap.put('iconURL',  iconURL);

    return iconPropertyMap;
}

Example usage for binding:

<span data-bind="css: selectedMapObjectIconClass" class="slds-icon_container slds-avatar">
  <img data-bind="attr:{src: selectedMapObjectIconUrl}"></img>
</span>
2
  • This is great! I used it and I'm having trouble seeing where the first "else" statement is reached. How can a tab describe where there is an sObjectName have an empty tr.getIcons()? That would mean that an sObject tab was created without an icon, and I don't see how that is possible. Did I miss something? @cricketlang Apr 4, 2021 at 19:38
  • 1
    @DavidSchach the getIcons() call only reads tabs that are in an app, so that catches those and just returns the default custom icon. Apr 7, 2021 at 0:41
5

I know it is very late, but below solution worked for me for Standard and custom object.

Apex Class

    @AuraEnabled
    public static String getIconName(String sObjectName){
        String u;
        List<Schema.DescribeTabSetResult> tabSetDesc = Schema.describeTabs();
        List<Schema.DescribeTabResult> tabDesc = new List<Schema.DescribeTabResult>();
        List<Schema.DescribeIconResult> iconDesc = new List<Schema.DescribeIconResult>();

        for(Schema.DescribeTabSetResult tsr : tabSetDesc) { tabDesc.addAll(tsr.getTabs()); }

        for(Schema.DescribeTabResult tr : tabDesc) {
            if( sObjectName == tr.getSobjectName() ) {
                if( tr.isCustom() == true ) {
                    iconDesc.addAll(tr.getIcons());
                } else {
                    u = 'standard:' + sObjectName.toLowerCase();
                }
            }
        }
        for (Schema.DescribeIconResult ir : iconDesc) {
            if (ir.getContentType() == 'image/svg+xml'){
                u = 'custom:' + ir.getUrl().substringBetween('custom/','.svg').substringBefore('_');
                break;
            }
        }
        return u;
    }

Lightning Component

<aura:component controller="yourApexController" implements="flexiPage:availableForAllPageTypes,force:hasSObjectName">
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:attribute name="sObjectName" type="String" />
    <aura:attribute name="iconName" type="String" />
    <lightning:icon iconName="{!v.iconName}" size="large" alternativeText="Indicates approval"/>
</aura:component>

Lightning JS controller

({
    doInit : function(component, event, helper) {
        var action = component.get("c.getIconName");
        action.setParams({ "sObjectName" : component.get("v.sObjectName") });
        action.setCallback(this, function(response) {
           component.set("v.iconName", response.getReturnValue() );
        });
        $A.enqueueAction(action);
    }
})

Let me know if it works for you.

1
  • This works thanks for the Update
    – SFDC
    Mar 13, 2020 at 7:44
0

I have a super-simple VF page, about 50 lines, standard controller. Don't even need any Apex code. I really didn't want to add a controller just to get a tab icon. I found you can do it in the VF page in a few lines of code. The caveat is that you have to know the icon name in SLDS and the color and put that in your page. But, most pages are only going to have one or a few and the object types aren't ever going to change. So a non-dynamic solution may be better than writing a controller to look through a whole bunch of metadata.

I selected the icon with two people for my tab which is the same as SLDS custom icon 15.

The HTML for the icon on a display layout is --

<div class="highlights-icon-container slds-avatar slds-m-right_small icon" style="background-color: #f77e75">
  <img src="https://myorgname-dev-ed.my.salesforce.com/img/icon/t4v35/custom/custom15_120.png" title="My Object Name">
</div>

From that I was able to code the following in my VF page--


<div class="slds-media__figure highlights-icon-container slds-avatar slds-m-right_small icon">
  <svg class="slds-icon" aria-hidden="true"  style="background-color: #f77e75">
   <use xlink:href="{!URLFOR($Asset.SLDS, 'assets/icons/custom-sprite/svg/symbols.svg#custom15')}"/> 
  </svg>
</div>

Way better that writing an Apex controller when I do not need one.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .