Create a Lightning Component and Add It to a Lightning Page

You set up Einstein Vision and installed the library. Great! We’re almost ready to upload your first dataset. But first we need a user interface so we can upload files. Let’s create the UI now.

Create a Custom Apex Class

  1. Click the gear ( Setup Gear), then select Developer Console.
  2. Click File > Open > Classes.
  3. Locate the class named EinsteinVision_Admin and double-click to open.
  4. Replace the contents of the class with this code.
    public class EinsteinVision_Admin {
        @AuraEnabled
        public static void createDatasetFromUrl(String zipUrl) {
            EinsteinVision_PredictionService service = new EinsteinVision_PredictionService();
            service.createDatasetFromUrlAsync(zipUrl);
        }
        @AuraEnabled
        public static List<EinsteinVision_Dataset> getDatasets() {
            EinsteinVision_PredictionService service = new EinsteinVision_PredictionService();
            EinsteinVision_Dataset[] datasets = service.getDatasets();
            return datasets;
        }
        @AuraEnabled
        public static String trainDataset(Decimal datasetId) {
            EinsteinVision_PredictionService service = new EinsteinVision_PredictionService();
            EinsteinVision_Model model = service.trainDataset(Long.valueOf(String.valueOf(datasetId)), 'Training', 0, 0, '');
            return model.modelId;
        }
        @AuraEnabled
        public static void deleteDataset(Long datasetId) {
            EinsteinVision_PredictionService service = new EinsteinVision_PredictionService();
            service.deleteDataset(datasetId);
        }
        @AuraEnabled
        public static List<EinsteinVision_Model> getModels(Long datasetId) {
            EinsteinVision_PredictionService service = new EinsteinVision_PredictionService();
            EinsteinVision_Model[] models = service.getModels(datasetId);
            return models;
        }
        @AuraEnabled
        public static void getCatPrediction(Id catId, String fileName, String base64) {
            Blob fileBlob = EncodingUtil.base64Decode(base64);
            EinsteinVision_PredictionService service = new EinsteinVision_PredictionService();
            EinsteinVision_Dataset[] datasets = service.getDatasets();
            for (EinsteinVision_Dataset dataset : datasets) {
                if (dataset.Name.equals('Cats')) {
                    EinsteinVision_Model[] models = service.getModels(dataset);
                    EinsteinVision_Model model = models.get(0);
                    EinsteinVision_PredictionResult result = service.predictBlob(model.modelId, fileBlob, '');
                    EinsteinVision_Probability probability = result.probabilities.get(0);
                    Cat__c cat = [SELECT Id FROM Cat__C WHERE Id=:catId];
                    cat.Cat_Breed__c = probability.label;
                    update cat;
                    Attachment[] attsOld = [SELECT Id FROM Attachment WHERE ParentId=:catId];
                    delete attsOld;
                    Attachment att = new Attachment();
                    att.Body = fileBlob;
                    att.ParentId = cat.Id;
                    att.Name = fileName;
                    insert att;
                }
            }
        }
        @AuraEnabled
        public static List<EinsteinVision_Label> getCatLabels() {
            EinsteinVision_PredictionService service = new EinsteinVision_PredictionService();
            EinsteinVision_Dataset[] datasets = service.getDatasets();
            for (EinsteinVision_Dataset dataset : datasets) {
                if (dataset.Name.equals('Cats')) {
                    return dataset.labelSummary.labels;
                }
            }
            return null;
        }
        @AuraEnabled
        public static String getImageUrlFromAttachment(Id catId) {
            List<Attachment> atts = [SELECT Id FROM Attachment WHERE ParentId=:catId];
            if (atts.size()>0) {
                return atts.get(0).Id;
            }
            return '';
        }
    }
  5. Click File > Save.

My Domain Is Already On in Your Trailhead Playground

Do not attempt to turn on My Domain, or change its settings, in your Trailhead Playground. By default, My Domain is already active in every Trailhead Playground. 

My Domain name highlighted in a Trailhead Playground URL

In your production org, My Domain lets you create a subdomain unique to your organization. With My Domain, you replace the instance URL that Salesforce assigns you, such as https://na17.lightning.force.com, with your chosen subdomain, for example, https://mydomainname.lightning.force.com. 

My Domain is required to create custom Lightning components and set up single sign-on (SSO) in an org. To learn more about My Domain, check out this knowledge article. To learn how to activate it in your production org, see the User Authentication module.

Create a Lightning Component

  1. Click File > New > Lightning Component.
  2. Name the component EinsteinVision_Admin_UI.
  3. Click Submit.
  4. Replace the contents of the component with this code.
    <aura:component implements="flexipage:availableForAllPageTypes" access="global" controller="EinsteinVision_Admin">
        <aura:attribute name="datasets" type="EinsteinVision_Dataset[]"></aura:attribute>
        <aura:attribute name="models" type="EinsteinVision_Model[]"></aura:attribute>
        <aura:attribute name="spinnerWaiting" type="Boolean" default="false"/>
        <aura:handler name="init" value="{!this}" action="{!c.onLoadDatasets}" />
        <div class="slds-card">
            <div class="slds-p-left_medium slds-p-right_medium">
                <lightning:layout verticalAlign="end" class="slds-m-around--small">
                    <lightning:layoutitem flexibility="grow">
                        <lightning:input type="URL" label="ZIP file url:" aura:id="zipUrl" value="" />
                    </lightning:layoutitem>
                    <lightning:layoutitem >
                        <lightning:button onclick="{!c.onCreateDataset}">Create Dataset</lightning:button>
                        <lightning:button onclick="{!c.onLoadDatasets}">Refresh Datasets</lightning:button>
                    </lightning:layoutitem>
                </lightning:layout>
                <table class="slds-table slds-table--bordered slds-table--cell-buffer">
                    <thead>
                        <tr class="slds-text-title--caps">
                            <th scope="col">
                                <div class="slds-truncate" title="Dataset Name">Dataset Name</div>
                            </th>
                            <th scope="col">
                                <div class="slds-truncate" title="Dataset Labels">Dataset Labels</div>
                            </th>
                            <th scope="col">
                                <div class="slds-truncate" title="Dataset Models">Dataset Models</div>
                            </th>
                            <th scoe="col">
                                <div class="slds-truncate" title="Actions">Actions</div>
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        <aura:iteration items="{!v.datasets}" var="dataset">
                            <tr>
                                <td scope="row" data-label="Dataset Name">
                                    <div class="slds-truncate" title="{!dataset.name}">{!dataset.name}</div>
                                </td>
                                <td scope="row" data-label="Dataset Labels">
                                    <aura:iteration items="{!dataset.labelSummary.labels}" var="label">
                                        {!label.name} ({!label.numExamples})<br></br>
                                    </aura:iteration>
                                </td>
                                <td scope="row" data-label="Dataset Models">
                                    <aura:iteration items="{!v.models}" var="model">
                                        {!model.modelId} ({!model.status} - {!model.progress*100}%)<br></br>
                                    </aura:iteration>
                                </td>
                                <td scope="row" data-label="Actions">
                                    <div class="slds-truncate">
                                        <lightning:button onclick="{!c.onTrainDataset}" value="{!dataset.id}" variant="brand">Train</lightning:button>
                                        <lightning:button onclick="{!c.onDeleteDataset}" value="{!dataset.id}" variant="destructive">Delete</lightning:button>
                                    </div>
                                </td>
                            </tr>
                        </aura:iteration>
                    </tbody>
                </table>
                <aura:if isTrue="{!v.spinnerWaiting}">
                    <lightning:spinner size="medium" alternativeText="Loading data..." />
                </aura:if>
            </div>
        </div>
    </aura:component>
  5. Click the Controllerbutton in the component palette on the right and replace the contents with this code.
    ({
        onCreateDataset : function(component, event, helper) {
    		helper.onCreateDataset(component);
        },
        onLoadDatasets : function(component, event, helper) {
    		helper.onLoadDatasets(component);
        },
        onTrainDataset : function(component, event, helper) {
    		helper.onTrainDataset(component, event);
        },
        onDeleteDataset : function(component, event, helper) {
    		helper.onDeleteDataset(component, event);
        }
    })
  6. Now click the Helperbutton in the palette and replace the contents with this code.
    ({
        onCreateDataset: function(component) {
            var action = component.get("c.createDatasetFromUrl");
            var zipUrl = component.find("zipUrl").get("v.value");
            var self = this;
            action.setParams({
                zipUrl: zipUrl
            });
            action.setCallback(this, function(response) {
                component.set("v.waiting", false);
                var state = response.getState();
                if (state === 'ERROR') {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            return alert(errors[0].message);
                        }
                    } else {
                        return console.log("Unknown error");
                    }
                }
                var result = response.getReturnValue();
                self.onLoadDatasets(component);
            });
            component.set("v.waiting", true);
            $A.enqueueAction(action);
        },
        onLoadDatasets : function(component) {
            var self = this;
            var action = component.get("c.getDatasets");
            action.setCallback(this, function(response) {
                var state = response.getState();
                if (state === 'ERROR') {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            return alert(errors[0].message);
                        }
                    } else {
                        return console.log("Unknown error");
                    }
                }
                component.set("v.datasets", response.getReturnValue());
                var dataset = response.getReturnValue();
                if (dataset && dataset.length>0) {
    	            self.onModelStatus(component, dataset);
                } else {
    		        component.set("v.spinnerWaiting", false);
                }
            });
            component.set("v.spinnerWaiting", true);
            $A.enqueueAction(action);
        },
        onModelStatus : function(component, datasets) {
            var action = component.get("c.getModels");
            action.setParams({
                datasetId: datasets[0].id
            });
            action.setCallback(this, function(response) {
                component.set("v.spinnerWaiting", false);
                var state = response.getState();
                if (state === 'ERROR') {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            return alert(errors[0].message);
                        }
                    } else {
                        return console.log("Unknown error");
                    }
                } else {
                    component.set("v.models", response.getReturnValue());
                }
            });
            component.set("v.spinnerWaiting", true);
            $A.enqueueAction(action);
        },
        onDeleteDataset : function(component, event) {
            var action = component.get("c.deleteDataset");
            var datasetId = event.getSource().get("v.value");
            var self = this;
            action.setParams({
                datasetId: datasetId
            });
            action.setCallback(this, function(response) {
                component.set("v.spinnerWaiting", false);
                var state = response.getState();
                if (state === 'ERROR') {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            return alert(errors[0].message);
                        }
                    } else {
                        return console.log("Unknown error");
                    }
                }
                self.onLoadDatasets(component);
            });
            component.set("v.spinnerWaiting", true);
            $A.enqueueAction(action);
        },
        onTrainDataset : function(component, event) {
            var action = component.get("c.trainDataset");
            var datasetId = event.getSource().get("v.value");
            var self = this;
            action.setParams({
                datasetId: datasetId
            });
            action.setCallback(this, function(response) {
                component.set("v.spinnerWaiting", false);
                var state = response.getState();
                if (state === 'ERROR') {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            return alert(errors[0].message);
                        }
                    } else {
                        return console.log("Unknown error");
                    }
                } else {
                    var toastEvent = $A.get("e.force:showToast");
                    toastEvent.setParams({
                        "title": "Success!",
                        "type": "success",
                        "message": "The model id for the training is " + response.getReturnValue() + ". Refresh the dataset for seeing the training progress."
                    });
                    toastEvent.fire();
                }
            });
            component.set("v.spinnerWaiting", true);
            $A.enqueueAction(action);
        }
    })
  7. Click File > Save All.
  8. Close the Developer Console.

Create a Lightning App

  1. In the Home tab in Setup, enter Lightning App in the Quick Find box and select Lightning App Builder.
  2. Click New.
  3. Leave App Page selected and click Next.
  4. For Label, enter Einstein Vision and click Next.
  5. Select One Region, then click Finish.
  6. Drag the EinsteinVision_Admin_UI Lightning component onto the Lightning page.
  7. Click Save.
  8. Click Activate.
  9. On Page Settings, select Activate for System Administrators only.
  10. Select the Lightning Experience tab.
  11. Select Cat Rescue, then click Add page to app.
  12. Click Save.
  13. Click Back Back buttonto return to Setup.
Keep learning for
free!
Sign up for an account to continue.
What’s in it for you?
  • Get personalized recommendations for your career goals
  • Practice your skills with hands-on challenges and quizzes
  • Track and share your progress with employers
  • Connect to mentorship and career opportunities