Display custom sections aside videos

As we know it until now, the VideoList component is just an out of the box component that allow us to render one or more videos associated to a given Salesforce record. In this section we will learn how to combine the video playbacks with some contextual information or actions that the running user can take.

If we consider again the video interviewing example introduced here, the hiring manager that is watching video interviews will want to take a decision on each candidate, for example typing a short comment and approving or rejecting the applicant. Alternatively, the hiring manager could mention one or more of her colleagues via Chatter, asking for an extra point of view before deciding how to progress.

All of these small visual integrations can be achieved natively using the standard VideoList component, which accepts up to three VisualForce pages that can be displayed on the right side of each video. The embedded page is also provided with some context information, useful to get a reference to the Salesforce records involved in the presentation of the videos.

The steps to include a VisualForce page aside of a video are the following:

  1. Create the VisualForce page, developing the user interface and the logic that is required in your use case. Leverage the contextual information injected by NativeVideo to identify the records you are dealing with. More info later on this page.
  2. Considering a VideoList component embedded in your page via the Lightning App Builder, fill in the Side Page Label and Side Page Link in order to display your VisualForce page. If you look closely, there are three option for each of the two parameters just mentioned, because as briefly anticipated, we host up to three VisualForce pages on the right hand side of each video. If more than a VisualForce page is specified, a tab view will be rendered, using the Side Page Label as tab label. The Side Page Link should be filled just with the name of the VisualForce page, nothing else.

Let's go back to the hiring manager use case and walk you through these steps in practice.

1. Create a VisualForce page to cover your specific requirements

The first functionality that we will be offering to our hiring managers will be to be able to quickly decide who has been accepted for a F2F interview, and who instead has been rejected. In both cases, the hiring manager is invited to add a personal comment, motivating his or her decision.

Now, in order to achieve this, we will have to create a simple VisualForce page and an associated Apex class. Let's start with the Apex controller:

global with sharing class VideoListApplicationAction {

    public Id parentObjectID {get; set;}
    public Application__c application {get; set;}

    public VideoListApplicationAction() {
        parentObjectID = (Id) ApexPages.currentPage().getParameters().get('parentObjectID');
        application = findApplication(parentObjectID);
    }

    @RemoteAction
    global static Boolean updateApplication(Id parentRecord, String comments, Boolean approved) {
        Application__c application = findApplication(parentRecord);

        if(application != null) {
            application.Hiring_Manager_Comments__c = comments != null 
                ? comments.substring(0, comments.length() < 255 ? comments.length() : 255) : '';
            application.Hiring_Manager_Video_Decision__c = approved;
            application.Hiring_Manager_Video_Decision_Date__c = Datetime.now();
            application.Status__c = approved ? 'Accepted' : 'Rejected';

            update application;
            return true;
        }

        return false;
    }

    private static Application__c findApplication(Id applicationID) {
        Application__c application = null;
        try {
            application = [SELECT Hiring_Manager_Comments__c, Hiring_Manager_Video_Decision__c, 
                           Hiring_Manager_Video_Decision_Date__c, Status__c
				            FROM Application__c WHERE Id = :applicationID LIMIT 1][0];
        } catch (Exception e) {
            System.debug('Error in getting the Application ' + applicationID + ' - ' + e.getMessage());
        }

        return application;
    }

}


This class is reading the parentObjectID from the GET parameters and loads the application record. Also, it exposes a Javascript remoting function that accepts the comments and the decision and updates the decision fields.  The information that are available via GET parameters are:

  • "videoID" with the Salesforce ID of the video
  • "parentObjectID", populated with the Salesforce ID of either the parent record (One to one scenario) or the junction record (One to many scenario)


Once the Apex controller is in place, the VisualForce page can take care of displaying the user interface and expose the logic. An example could be the following:

<apex:page showHeader="false" sidebar="false" controller="VideoListApplicationAction">
    <apex:slds />
    <apex:includeLightning />
	<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script type="text/javascript">
        function submitHMDecision(decisionButton) {
            var selectedButton = $(decisionButton)[0];
            var comments = $(selectedButton).closest('.commentsCnt').find('textarea')[0];
            var parentRecord = '{!parentObjectID}';
            updateApplication(selectedButton, parentRecord, $(comments).val(), selectedButton.name === 'approved');
            return false;
        }

        function updateApplication(buttonElement, parentRecord, comments, approved) {
            Visualforce.remoting.Manager.invokeAction(
                '{!$RemoteAction.VideoListApplicationAction.updateApplication}', 
                parentRecord, comments, approved, 
                function(result, event){
                    if(event.status) {
                        $(buttonElement).closest('.commentsCnt').find('.submitComments').hide();
                        $(buttonElement).closest('.commentsCnt').find('.thankyou').show();
                    } else {
                        $(buttonElement).closest('.commentsCnt').find('.submitComments').hide();
                        $(buttonElement).closest('.commentsCnt').find('.error').show();
                    }
                }, 
                {escape: true}
            );
        }

    </script>

    <apex:outputPanel layout="none" rendered="{! application.Hiring_Manager_Video_Decision_Date__c == null }" >
    <div class="commentsCnt">
        <div class="submitComments"> 
            <br/>
            <apex:form >
                <div class="slds-form-element">
                    <label class="slds-form-element__label" for="textarea-id-01">Your Comment</label>
                    <div class="slds-form-element__control">
                        <textarea name="comments" class="slds-textarea" 
                        placeholder="Your thoughts on the video interview..."></textarea>
                    </div>
                </div>
                <br/>
                <div class="slds-form-element">
                    <label class="slds-form-element__label" for="textarea-id-01">Progress with a face-to-face interview?</label>
                    <div class="slds-form-element__control">
                        <input class="slds-button slds-button_stateful slds-button_neutral slds-not-selected" 
                            type="submit" name="approved" value="Approve Candidate" onclick="return submitHMDecision(this);"/>
                        <input class="slds-button slds-button_stateful slds-button_neutral slds-not-selected" 
                            type="submit" name="rejected" value="Reject Candidate" onclick="return submitHMDecision(this);"/>
                    </div>
                </div>
            </apex:form>
        </div>

        <div class="thankyou" style="display: none;">
            <br/>
            <p>Thanks for submitting your comments, we will be in touch soon!</p>
        </div>

        <div class="error" style="display: none;">
            <br/>
            <p>Apologies - try again later.</p>
        </div>
    </div>
    </apex:outputPanel>

    <apex:outputPanel layout="none" rendered="{!application.Hiring_Manager_Video_Decision_Date__c != null }" >
        <br/>
        <p>
            On&nbsp;<apex:outputText value="{0,date,dd.MM.yyyy 'at' HH:mm}"><apex:param value="{!application.Hiring_Manager_Video_Decision_Date__c}" /></apex:outputText> this candidate has been <b>
        <apex:outputPanel layout="none" rendered="{!application.Hiring_Manager_Video_Decision__c}"> approved</apex:outputPanel>
        <apex:outputPanel layout="none" rendered="{!!application.Hiring_Manager_Video_Decision__c}"> rejected</apex:outputPanel></b>, with the following comment:<br/>
        <i>{!application.Hiring_Manager_Comments__c}</i>
        </p>
    </apex:outputPanel>

</apex:page>


This VisualForce page is either showing a form, where the hiring manager is typing a comment and deciding what's next, or it's showing what has been the decision on this candidate. 


2. Reference the VisualForce page in the VideoList component

Once the VisualForce page and the Apex controller have been created, the coding required is completed, so that we can configure them aside the VideoList component. Open the Lightning App or Community Builder and click on your VideoList component, so that the list of configurations appears on the right.

Then, locate the "Side Page 1 Label" and populate it with "Approve / Reject", and "Side Page 2 Link" with "VideoListApplicationAction". Save the page, navigate to a Vacancy to see something like the following:

VideoList with a Side Page


As you might have noticed, if only one VisualForce page is configured to be shown on the side of the video, no label is displayed above. Let's now add a second page, so that the tab view will be apperaing above the embedded VisualForce pages.


Let's build an integration with Chatter, so that we can display the feed of the record hosting the reference of the video.

First of all, create a new Lightning Component, called for example "VideoChatterFeed". Open the component and add this code:

<aura:component implements="force:appHostable" >
	<aura:attribute name="recordID" type="String" />
	<forceChatter:fullFeed type="Record" subjectId="{!v.recordID}"/>
</aura:component>


This component is simply reading the recordID attribute and displaying the Chatter feed & posting functionalities.

Next step, create a Lightning Application, called for example "VideoChatterFeedApp". In the Application file, add the following code:

<aura:application access="GLOBAL" extends="ltng:outApp"> 
    <aura:dependency resource="c:VideoChatterFeed"/>
</aura:application>


Finally, create a new VisualForce page that invokes the Lightning Application we have just created. For example, call the VisualForce page as "VideoChatterFeed" and add this code:

<apex:page showHeader="false" sidebar="false">
    <!-- Import the lightning library -->
    <apex:includeLightning />

    <!-- Set the container for the dynamic loading -->
    <div id="lightningContainer" />

    <!-- Get from the URL the object to refer and load it -->
    <script type="text/javascript">
        var urlParams = fetchURLParameters();
        var recordIDAttrKey = 'parentObjectID';

        $Lightning.use("c:VideoChatterFeedApp", function() {
          $Lightning.createComponent("c:VideoChatterFeed",
          {recordID : urlParams[recordIDAttrKey]}, "lightningContainer", function(cmp) {});
        });

        function fetchURLParameters() {
            var urlParams = {};

            // Get the URL and filter the attributes section
            var pageURL = decodeURIComponent(window.location.href);
            var pageURLAttributes = pageURL.substring(pageURL.indexOf('?') + 1);

            // Split by couples
            var URLVariablesList = pageURLAttributes.split('&');
            
            for(var _i = 0; _i < URLVariablesList.length; _i++) {
                var variableList = URLVariablesList[_i].split('=');
                urlParams[variableList[0]] = variableList[1];
            }

            return urlParams;
        }
    </script>
</apex:page>


As you can notice, the VisualForce page is reading the parentObjectID attribute from the URL GET parameters and dynamically creates and embeds the Lightning Application we have just created.

Once this is done, go back to the Lightning App Builder and configure two further parameters on the VideoList component, the "Side Page 2 Label" with "Chatter" and "Side Page 2 Link" with "VideoChatterFeed".
Save and reload the details page of a Vacancy record... you will see something like the following screenshot:

VideoList with two Side Pages



Conclusion & Next Steps

In this section we have discussed how to leverage the capabilities of the out of the box VideoList component when the display of further information or actions is required alongside each video.

In the next sections we will finally list all parameters available in the VideoList component. Link here.