Exercise 5Lightning Data Service, Design Parameters and Component Communication

Objectives

  • Create Design parameters for Lightning App Builder
  • Restrict the use of the component to a specific object
  • Use events for cross-component communication

Step 1 - Leverage Lightning Data Service

  1. On the Property Record page, edit the price of the property (raise it by $200000).
  2. Click Save, and notice that the component doesn’t change.
  3. In the Developer Console, open the SimilarProperties component markup.
  4. Add the following attribute:

     <aura:attribute name="property" type="Property__c" />
    
  5. Add a force:recordData object to the component after the last aura:handler:

     <force:recordData aura:id="propertyService" 
             recordId="{!v.recordId}" 
             targetRecord="{!v.property}" 
             recordUpdated="{!c.doInit}" 
             layoutType="FULL" />
    
  6. Save the file, and refresh the Property Record page.
  7. Edit the price of the property, and notice that the component performs a new search.
  8. Make an edit to a record in the Similar Properties list, increasing the price by more than $10,000. Notice that the component doesn’t refresh.

Step 2 - Using Custom Lightning Events

  1. In the Developer Console, click File > New > Lightning Event.
  2. Give the event a name of recordUpdated.
  3. Close the file.
  4. Switch to SimilarProperties.cmp.
  5. Add the following on a new line after the existing aura:handler:

     <aura:handler event="c:recordUpdated" action="{!c.doInit}" />
    
  6. Save the file.
  7. Switch to SimilarProperty.
  8. On a new line after the aura:attribute add:

     <aura:registerEvent name="recordUpdated" type="c:recordUpdated"/>
    
  9. Save the file, then switch to SimilarPropertyController.js.
  10. Add the following to the handleSuccess function, immediately after toastEvent.fire():

    var recUpdate = $A.get("e.c:recordUpdated");
    recUpdate.fire();	
    
  11. Save the file, then refresh the Property page.
  12. Make an edit to a record in the Similar Properties list.
  13. Notice that the component refreshes.

Step 3 - Using Design Parameters

  1. Add two new attributes to the SimilarProperties.cmp:

     <aura:attribute name="searchCriteria" type="String" default="Price" />
     <aura:attribute name="priceRange" type="String" default="100000" />
    
  2. Update the title of the lightning:card to:

     {! 'Similar Properties by ' + v.searchCriteria}
    
  3. Switch to the SimilarPropertiesController.
  4. Update the action to var action = component.get("c.getSimilarProperties");.
  5. Update the parameters for the action to:

     action.setParams({
         recordId: component.get("v.recordId"),
         beds: component.get("v.property.fields.Beds__c.value"),
         price: component.get("v.property.fields.Price__c.value"),
         searchCriteria: component.get("v.searchCriteria"),
         priceRange: parseInt(component.get("v.priceRange"), 10)
     });	
    
  6. In the Developer Console, choose File > Open Resource and open MyPropertyController.
  7. Remove the FindProperties method and update the getSimilarProperties method to match:

     @AuraEnabled
     public static List<Property__c> getSimilarProperties (Id recordId, String searchCriteria, Decimal beds, Decimal price, Decimal priceRange ) {
          if (searchCriteria == 'Bedrooms') {
              return [
                  SELECT Id, Name, Beds__c, Baths__c, Price__c, Broker__c, Status__c, Thumbnail__c
                  FROM Property__c WHERE Id != :recordId AND Beds__c = :beds
              ];
          } else {
              Decimal range;
              if (priceRange == null) {
                  range = 100000;
              } else {
                  range = priceRange;
              }
              return [
                  SELECT Id, Name, Beds__c, Baths__c, Price__c, Broker__c, Status__c, Thumbnail__c
                  FROM Property__c WHERE Id != :recordId AND Price__c > :price - range AND Price__c < :price + range
              ];
          }
      }
    
  8. Save the files.
  9. Refresh the Property Record page and notice that we are now retrieving the properties by passing the Id, number of bedrooms and price of the current record.
  10. In the Developer Console, switch to the SimilarProperties.cmp file.
  11. Click the Design tile to add a design file to the component bundle.
  12. Add the following to the design file:

    <design:attribute name="searchCriteria" label="Search By" datasource="Bedrooms, Price" default="Price" description="Search for similar houses based on what criteria?" />
    <design:attribute name="priceRange" label="Price Range" default="100000" description="When searching by Price, search using the price plus or minus this amount" />
    
  13. Switch to the Property Record page.
  14. Click the Setup icon and choose Edit Page.
  15. (Optional) Click on the SimilarProperties component on the page, then tap the delete key (or click the X) to remove the component from the page.
  16. (Optional) Click the Save button in Lightning App Builder.
  17. (Optional) Switch back to the Developer Console and add the following to the Design file:

    <sfdc:objects >
        <sfdc:object >Property__c</sfdc:object>
    </sfdc:objects>	
    
  18. (Optional) Save the file.
  19. Drag two instances of the Similar Properties component onto the page. Configure one instance for ‘Bedrooms’ and one for ‘Price’.
  20. Save the page and return to the Property record.

Step 4 - Add a Custom Icon, Visible in Lightning App Builder

  1. Switch back to the Developer Console and the SimilarProperties.cmp tab, click on the SVG tile on the right-hand side of the window.
  2. In a new browser tab, navigate back to the SLDS site.
  3. Navigate to the Icons section of the site.
  4. Scroll down to locate custom85 in the Custom section of icons.
  5. (Informational Only) Normally, you would click on the Downloads link in the navigation panel, and then scroll down to the Icons section and click the Download button.
  6. (Informational Only) After navigating to the downloaded zip file and unzipping it, open the folder and then open the custom folder.
  7. (Informational Only) Locate the custom85.svg file and open it in a text editor.
  8. (Informational Only) Copy the <path> tag from the SVG.
  9. (Informational Only) In the Developer Console, switch to SimilarProperties.svg.
  10. (Informational Only) Replace the second <path> tag with the one you just copied.
  11. (Informational Only) At the beginning of the <path> you just pasted, add fill=”#fff” before the “d” attribute.
  12. (Informational Only) Change width="120px" height="120px" viewBox="0 0 120 120" in the <svg> tag to:

    width="100px" height="100px" viewBox="0 0 100 100"
    
  13. (Informational Only) Change the fill of the first <path> to #F26891.
  14. Select all and replace the .svg file in the Dev Console with:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
        <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
            <path d="M120,108 C120,114.6 114.6,120 108,120 L12,120 C5.4,120 0,114.6 0,108 L0,12 C0,5.4 5.4,0 12,0 L108,0 C114.6,0 120,5.4 120,12 L120,108 L120,108 Z" id="Shape" fill="#2A739E"/>
            <path fill="#FFF" d="m78 24h-50v-2c0-1.1-0.9-2-2-2h-4c-1.1 0-2 0.9-2 2v56c0 1.1 0.9 2 2 2h4c1.1 0 2-0.9 2-2v-46h50c1.1 0 2-0.9 2-2v-4c0-1.1-0.9-2-2-2z m-4 14h-34c-3.3 0-6 2.7-6 6v22c0 3.3 2.7 6 6 6h34c3.3 0 6-2.7 6-6v-22c0-3.3-2.7-6-6-6z m-5.5 17h-2.5v10c0 0.6-0.4 1-1 1h-4c-0.6 0-1-0.4-1-1v-6c0-0.6-0.4-1-1-1h-4c-0.6 0-1 0.4-1 1v6c0 0.6-0.4 1-1 1h-4c-0.6 0-1-0.4-1-1v-10h-2.5c-0.5 0-0.7-0.6-0.3-0.9l11.2-10.9c0.4-0.3 0.9-0.3 1.3 0l11.2 10.9c0.3 0.3 0.1 0.9-0.4 0.9z"></path>
        </g>
    </svg>
    
  15. Save the file.
  16. Switch back to Lightning App Builder, and click the Refresh button at the top of the components’ list.

Step 6 - Show/Hide a Spinner

  1. In Developer Console, switch to the SimilarProperties component.
  2. Add a new line at the bottom of the markup before the closing </lightning:card> and add the following to the new line:

     <lightning:spinner aura:id="spinner" variant="brand" size="large"/>
    
  3. Save the file.

  4. Add class="slds-is-relative" to the <lightning:card> tag so that it looks like this:

     <lightning:card iconName="custom:custom85" title="{! 'Similar Properties by ' + v.searchCriteria}" class="slds-is-relative">
    
  5. Save the file.
  6. Click STYLE to add a custom CSS file to the SimilarProperties component and replace the default contents with:

     .THIS {
         min-height: 13rem;
     }
    
  7. Save the file.
  8. Reload the Property Record page. Notice anything wrong…?
  9. In the Developer Console, click CONTROLLER to open the SimilarPropertiesController.
  10. Add the following to the beginning of the doInit function before the var action declaration:

    var spinner = component.find("spinner");
    $A.util.removeClass(spinner, "slds-hide");
    
  11. Add the following at the end of the action’s callback:

    $A.util.addClass(spinner, "slds-hide");
    
  12. Save the file, and refresh the Property Record page.
  13. Modify the record and notice the spinner.

Step 7 - Conditionally Render Content

  1. In the Developer Console, switch back to the SimilarProperties component markup.
  2. Replace the contents of the <ul> with the following:

     <aura:if isTrue="{!v.similarProperties.length &gt; 0}">
         <aura:iteration items="{!v.similarProperties}" var="item">
             <li class="slds-list__item">                   
                 <c:SimilarProperty property="{!item}" />
             </li>
         </aura:iteration>
         <aura:set attribute="else">
             <li class="slds-list__item">
                 <h3 class="slds-text-small slds-text-color_error">No similar properties found.</h3>
             </li>
         </aura:set>
     </aura:if>
    
  3. Save the file and refresh the page. Change the price of the Property Record to something which has no similar properties like $200000 to see the “empty” state of the component.

Need a last second rescue? Check your code against the following:

SimilarProperties

SimilarProperties.cmp
<aura:component controller="MyPropertyController" implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" >
    <aura:attribute name="recordId" type="Id" />
    <aura:attribute name="similarProperties" type="Object[]" />
    <aura:attribute name="property" type="Property__c" />
    <aura:attribute name="searchCriteria" type="String" default="Price" />
    <aura:attribute name="priceRange" type="String" default="100000" />
    
    <aura:handler event="c:RecordUpdated" action="{!c.doInit}" />
    <aura:handler name="init" value="{! this}" action="{!c.doInit}" />
    
    <force:recordData aura:id="propertyService" 
                      recordId="{!v.recordId}" 
                      targetRecord="{!v.property}" 
                      recordUpdated="{!c.doInit}" 
                      layoutType="FULL" />
    
    <lightning:card iconName="custom:custom85" title="{! 'Similar Properties by ' + v.searchCriteria}" class="slds-is-relative">
        <div class="slds-p-left_medium slds-p-right_medium">
            <ul class="slds-list_vertical slds-has-dividers_top-space">
                <aura:if isTrue="{!v.similarProperties.length &gt; 0}">
                    <aura:iteration items="{!v.similarProperties}" var="item">
                        <li class="slds-list__item">                   
                            <c:SimilarProperty property="{!item}" />
                        </li>
                    </aura:iteration>
                    <aura:set attribute="else">
                        <li class="slds-list__item">
                            <h3 class="slds-text-small slds-text-color_error">No similar properties found.</h3>
                        </li>
                    </aura:set>
                </aura:if>
            </ul>
        </div>
        <lightning:spinner aura:id="spinner" variant="brand" size="large"/>
    </lightning:card>
</aura:component>
SimilarPropertiesController.js
({
    doInit : function(component, event, helper) {
        var spinner = component.find("spinner");
        $A.util.removeClass(spinner, "slds-hide");
        var action = component.get("c.getSimilarProperties");         
        action.setParams({
            recordId: component.get("v.recordId"),
            beds: component.get("v.property.fields.Beds__c.value"),
            price: component.get("v.property.fields.Price__c.value"),
            searchCriteria: component.get("v.searchCriteria"),
            priceRange: parseInt(component.get("v.priceRange"), 10)
        }); 
        action.setCallback(this, function(response){
            var similarProperties = response.getReturnValue();
            component.set("v.similarProperties", similarProperties);
            console.log("f: ", component.get("v.similarProperties"));
            $A.util.addClass(spinner, "slds-hide");
        });
        $A.enqueueAction(action);
    }
})
SimilarProperties.css
.THIS {
    min-height: 13rem;
}
SimilarProperties.design
<design:component >
    <sfdc:objects >
        <sfdc:object >Property__c</sfdc:object>
    </sfdc:objects>	
    <design:attribute name="searchCriteria" label="Search By" datasource="Bedrooms, Price" default="Price" description="Search for similar houses based on what criteria?" />
    <design:attribute name="priceRange" label="Price Range" default="100000" description="When searching by Price, search using the price plus or minus this amount" />
</design:component>

SimilarProperty

SimilarProperty.cmp
<aura:component >
    <aura:attribute name="property" type="Property__c" />
    <aura:registerEvent name="recordUpdated" type="c:RecordUpdated"/>
    
    <lightning:recordViewForm aura:id="viewForm" recordId="{!v.property.Id}" objectApiName="Property__c">
        <div class="slds-media">
            <div class="slds-media__figure">
                <img src="{!v.property.Thumbnail__c}" class="slds-avatar_large slds-avatar_circle" alt="{!v.targetFields.Title_c}" />
            </div>
            <div class="slds-media__body">
                <lightning:layout class="slds-hint-parent">
                    <a onclick="{!c.navToRecord}">
                        <h3 class="slds-text-heading_small slds-m-bottom_xx-small">{!v.property.Name}</h3>
                    </a>
                    <lightning:buttonIcon iconName="utility:edit" class="slds-col_bump-left" iconClass="slds-button__icon_hint" variant="bare" alternativeText="Edit Record" onclick="{!c.editRecord}" />
                </lightning:layout>        
                <lightning:layout multipleRows="true" >
                    <lightning:layoutItem size="6"  >
                        <lightning:outputField fieldName="Beds__c"/>
                    </lightning:layoutItem>
                    <lightning:layoutItem size="6"  >
                        <lightning:outputField fieldName="Baths__c" />
                    </lightning:layoutItem>
                    <lightning:layoutItem size="6"  >
                        <lightning:outputField fieldName="Price__c" />
                    </lightning:layoutItem>
                    <lightning:layoutItem size="6"  >
                        <lightning:outputField fieldName="Status__c" />
                    </lightning:layoutItem>
                </lightning:layout>
            </div>
        </div>	
    </lightning:recordViewForm>
    <lightning:recordEditForm aura:id="editForm" recordId="{!v.property.Id}" objectApiName="Property__c" class="slds-hide" onsuccess="{!c.handleSuccess}">        
        <div class="slds-media">
            <div class="slds-media__figure">
                <img src="{!v.property.Thumbnail__c}" class="slds-avatar_large slds-avatar_circle" alt="{!v.targetFields.Title_c}" />
            </div>
            <div class="slds-media__body">
                <lightning:layout >
                    <a onclick="{!c.navToRecord}">
                        <h3 class="slds-text-heading_small slds-m-bottom_xx-small">{!v.property.Name}</h3>
                    </a>
                </lightning:layout>
                <lightning:layout multipleRows="true" >
                    <lightning:layoutItem size="6"  >
                        <lightning:inputField fieldName="Beds__c"/>
                    </lightning:layoutItem>
                    <lightning:layoutItem size="6"  >
                        <lightning:inputField fieldName="Baths__c" />
                    </lightning:layoutItem>
                    <lightning:layoutItem size="6"  >
                        <lightning:inputField fieldName="Price__c" />
                    </lightning:layoutItem>
                    <lightning:layoutItem size="6"  >
                        <lightning:inputField fieldName="Status__c" />
                    </lightning:layoutItem>
                </lightning:layout>
                <lightning:layout horizontalAlign="center" class="slds-m-top_large">
                    <lightning:button variant="neutral" label="Cancel" title="Cancel" type="text" onclick="{!c.handleCancel}" />
                    <lightning:button variant="brand" label="Submit" title="Submit" type="submit" />
                </lightning:layout>
            </div>
        </div>
    </lightning:recordEditForm>
</aura:component>
SimilarPropertyController.js
({
    navToRecord : function (component, event, helper) {
        var navEvt = $A.get("e.force:navigateToSObject");
        navEvt.setParams({
            "recordId": component.get("v.property.Id")
        });
        navEvt.fire();
    },
    editRecord : function(component, event, helper) {
        helper.showHide(component);	
    },
    handleSuccess : function(component, event, helper) {
        var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            "title": "Success!",
            "message": "The property's info has been updated.",
            "type": "success"
        });
        toastEvent.fire();
        var recUpdate = $A.get("e.c:RecordUpdated");
        recUpdate.fire();	
        helper.showHide(component);
    },
    handleCancel : function(component, event, helper) {
        helper.showHide(component);
        event.preventDefault();
    }	
    
})
MyPropertyController.apxc
public with sharing class MyPropertyController {
@AuraEnabled
public static List<Property__c> getSimilarProperties (Id recordId, String searchCriteria, Decimal beds, Decimal price, Decimal priceRange ) {
      if (searchCriteria == 'Bedrooms') {
          return [
              SELECT Id, Name, Beds__c, Baths__c, Price__c, Broker__c, Status__c, Thumbnail__c
              FROM Property__c WHERE Id != :recordId AND Beds__c = :beds
          ];
      } else {
          Decimal range;
          if (priceRange == null) {
              range = 100000;
          } else {
              range = priceRange;
          }
          return [
              SELECT Id, Name, Beds__c, Baths__c, Price__c, Broker__c, Status__c, Thumbnail__c
              FROM Property__c WHERE Id != :recordId AND Price__c > :price - range AND Price__c < :price + range
          ];
      }
  }
}