Receiving error “Component has an invalid value for property.” on App Builder

app-builderbuilderlightning-app-builderlightning-web-componentsslds

I have build a custom IconProvider class to display list of SLDS icons for selection on Lightning App Builder.

public with sharing class IconProvider extends VisualEditor.DynamicPickList {
    public override VisualEditor.DataRow getDefaultValue(){
        VisualEditor.DataRow defaultValue = new VisualEditor.DataRow('', '');
        return defaultValue;
    }
    public override VisualEditor.DynamicPickListRows getValues() {
        List<Icon> icons = (List<Icon>) JSON.deserialize(PageReference.forResource('All', 'sldsIcons.json').getContent().toString(), List<Icon>.class);
        System.debug(icons);

        VisualEditor.DynamicPickListRows  options = new VisualEditor.DynamicPickListRows();
        for (Icon ic: icons) {
            options.addRow(new VisualEditor.DataRow(ic.id + ' (' + ic.route + ')', ic.fullName));
        }
        return options;
    }
    public class Icon{
        public String id;
        public String route;
        public String fullName;
    }
}

I have a static resource zip file All which has a cached list of SLDS icons in a sldsIcons.json text JSON file.

I have a custom LWC which is available for Lightning Record Pages

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>53.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <property label="Header" name="header" type="String"/>
            <property label="Header Icon" name="icon" type="String" datasource="apex://IconProvider"/>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

I can see the list of icon names and I can select them, but when I try to save the Lightning App Builder page definition, I receive the following error:

"Component has an invalid value for property."

Best Answer

Looks like fetching StaticResource content is considered as an Callout and saving Flexi Page definition is counted as DML operation and an Exception is displayed in Apex Logs:

System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

So looks like I need to change my implementation to have a single JSON static resource icons (since I can't unzip file in Apex, existing libraries like Zippex fail with Maximum CPU time: 15020 out of 10000, LimitException: Apex CPU time limit exceeded, FATAL_ERROR Class.Puff.codes: line 416, column 1 when I try to use it in Apex to extract 86.7 KB JSON out of 11 KB Zip file) and change my implementation to use a simple SOQL instead of pageReference getContent method

public with sharing class IconProvider extends VisualEditor.DynamicPickList {
    public override VisualEditor.DataRow getDefaultValue(){
        VisualEditor.DataRow defaultValue = new VisualEditor.DataRow('', '');
        return defaultValue;
    }
    public override VisualEditor.DynamicPickListRows getValues() {
        StaticResource s = [select Id, Body FROM StaticResource where name = 'icons'];
        System.debug(s.Body.toString());
        List<Icon> icons = (List<Icon>) JSON.deserialize(s.Body.toString(), List<Icon>.class);

        // The following approach doesn't work because of System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out
        // List<Icon> icons = (List<Icon>) JSON.deserialize(PageReference.forResource('All', 'sldsIcons.json').getContent().toString(), List<Icon>.class);
        System.debug(icons);

        VisualEditor.DynamicPickListRows  options = new VisualEditor.DynamicPickListRows();
        for (Icon ic: icons) {
            options.addRow(new VisualEditor.DataRow(ic.id + ' (' + ic.route + ')', ic.fullName));
        }
        return options;
    }
    public class Icon{
        public String id;
        public String route;
        public String fullName;
    }
}