├── LICENSE ├── README.md └── src ├── aura ├── cardOutputSection │ ├── cardOutputSection.cmp │ ├── cardOutputSection.cmp-meta.xml │ └── cardOutputSectionController.js ├── fieldSetOutputSection │ ├── fieldSetOutputSection.cmp │ ├── fieldSetOutputSection.cmp-meta.xml │ ├── fieldSetOutputSection.design │ ├── fieldSetOutputSection.svg │ ├── fieldSetOutputSectionController.js │ └── fieldSetOutputSectionHelper.js ├── layoutOutputSection │ ├── layoutOutputSection.cmp │ ├── layoutOutputSection.cmp-meta.xml │ └── layoutOutputSectionController.js ├── outputDataRow │ ├── outputDataRow.cmp │ ├── outputDataRow.cmp-meta.xml │ └── outputDataRowController.js ├── outputDataTable │ ├── outputDataTable.cmp │ ├── outputDataTable.cmp-meta.xml │ ├── outputDataTableController.js │ └── outputDataTableHelper.js ├── outputField │ ├── outputField.cmp │ ├── outputField.cmp-meta.xml │ ├── outputFieldController.js │ └── outputFieldHelper.js ├── referenceField │ ├── referenceField.cmp │ ├── referenceField.cmp-meta.xml │ └── referenceFieldController.js └── relatedRecordsTable │ ├── relatedRecordsTable.cmp │ ├── relatedRecordsTable.cmp-meta.xml │ ├── relatedRecordsTable.design │ ├── relatedRecordsTable.svg │ ├── relatedRecordsTableController.js │ └── relatedRecordsTableHelper.js ├── classes ├── DataTable.cls ├── DataTable.cls-meta.xml ├── Field.cls ├── Field.cls-meta.xml ├── FieldSetSectionController.cls ├── FieldSetSectionController.cls-meta.xml ├── FieldSetSectionControllerTest.cls ├── FieldSetSectionControllerTest.cls-meta.xml ├── RelatedRecordsTableController.cls ├── RelatedRecordsTableController.cls-meta.xml ├── RelatedRecordsTableControllerTest.cls ├── RelatedRecordsTableControllerTest.cls-meta.xml ├── Row.cls └── Row.cls-meta.xml └── package.xml /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Financial Spectra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 1. Field Set Output Section 2 | This component can be used on lightning record page and displays the fields from a field set of the object. You need to set the Field Set Name (API Name - case sensitive) when adding the component to the page and set the other parameters to style the display. 3 | 4 | ## How to use Field Set Output Section 5 | Follow the under mentioned steps to use the Field Set Output Section component on your lightning record page 6 | * Deploy the source code of this repository to your Force.com project or install the latest managed package (Recommended). 7 | * After successful deployment/installation, go to the lightning record page where you want to add the field set section. 8 | * On the left side components panel of Lightning App Builder, you will see Field Set Output Section component. 9 | * Drag the component to the area on the record page where you want to display the output section. 10 | * In the right side panel of the Lightning App Builder, set the properties of the component. 11 | * Set the Field Set Name (API Name of the field set with namespace-prefix). 12 | * Set the other optional properties like Section Name, Section Style, Columns per Row, Icon and Collapsible as per your requirement. 13 | 14 | 15 | # 2. Related Records Table 16 | This component can be used on lightning record page and displays the list of records from a related object. A view-only table is created that is useful to display read only information to users. The option to configure the number of records to be displayed and show more records makes it more user-friendly. 17 | 18 | ## How to use Related Record Table 19 | Follow the under mentioned steps to use the Related Record Table component on your lightning record page 20 | * Deploy the source code of this repository to your Force.com project or install the latest managed package (Recommended). 21 | * After successful deployment/installation, go to the lightning record page where you want to add the related record table. 22 | * On the left side components panel of Lightning App Builder, you will see Related Record Table component. 23 | * Drag the component to the area on the record page where you want to display the table of related records. 24 | * In the right side panel of the Lightning App Builder, set the properties of the component. 25 | * Set the Related Object Name (API Name of the Object with namespace-prefix). 26 | * Set the Relationship Name (API Name of the Relationship - custom relationship ends with '__r'). 27 | * Set the comma-separated list of fields that you want to display in the table. 28 | * Set the other optional properties like Sort By, Max. Number of Rows, Title and Icon as per your requirement. 29 | 30 | 31 | If this component is useful for you, please leave a comment. If you have any suggestions or find any issue, then also please log an issue. 32 | 33 | Cheers! 34 | 35 | 36 | This component has been released via AppExchange 37 | 38 | https://appexchange.salesforce.com/listingDetail?listingId=a0N3A00000ErEqoUAF 39 | 40 | The AppExchange release also includes another component - Related Record Table. 41 | -------------------------------------------------------------------------------- /src/aura/cardOutputSection/cardOutputSection.cmp: -------------------------------------------------------------------------------- 1 | 10 | 11 | 13 | 15 | 17 | 19 | 21 | 22 | 23 | 24 | {!v.sectionTitle} 25 | 26 | 27 | 28 | 29 | {!$Label.c.Hide_Records_Label} 30 | {!$Label.c.Show_Records_Label} 31 | 32 | 33 | 34 | 35 |
36 |
    37 | 38 |
    39 |
    40 |
    {!field.fieldLabel}
    42 |
    43 | 46 |
    47 |
    48 |
    49 |
    50 |
51 |
52 |
53 |
54 | 55 |
-------------------------------------------------------------------------------- /src/aura/cardOutputSection/cardOutputSection.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Component to display the output section for a Lightning Record Page as a Card. 5 | 6 | -------------------------------------------------------------------------------- /src/aura/cardOutputSection/cardOutputSectionController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - cardOutputSectionController.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | toggleDisplay : function(component, event, helper) { 12 | var isCollapsible = component.get("v.collapsible"); 13 | 14 | if (isCollapsible){ 15 | var hideLink = component.find("hideLink"); 16 | $A.util.toggleClass(hideLink, "slds-hide"); 17 | 18 | var showLink = component.find("showLink"); 19 | $A.util.toggleClass(showLink, "slds-hide"); 20 | 21 | var sectionBody = component.find("sectionBody"); 22 | $A.util.toggleClass(sectionBody, "slds-hide"); 23 | } 24 | } 25 | }) -------------------------------------------------------------------------------- /src/aura/fieldSetOutputSection/fieldSetOutputSection.cmp: -------------------------------------------------------------------------------- 1 | 10 | 12 | 13 | 15 | 17 | 19 | 21 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | {!v.body} 31 | 32 | -------------------------------------------------------------------------------- /src/aura/fieldSetOutputSection/fieldSetOutputSection.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Component to display the output section for a Lightning Record Page with the fields from a field set. 5 | 6 | -------------------------------------------------------------------------------- /src/aura/fieldSetOutputSection/fieldSetOutputSection.design: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /src/aura/fieldSetOutputSection/fieldSetOutputSection.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/aura/fieldSetOutputSection/fieldSetOutputSectionController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - fieldSetOutputSectionController.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | doInit : function(component, event, helper) { 12 | helper.setSectionBody(component, event, helper); 13 | }, 14 | 15 | hideSpinner : function (component, event, helper) { 16 | var spinner = component.find('spinner'); 17 | $A.util.addClass(spinner, "slds-hide"); 18 | } 19 | }) -------------------------------------------------------------------------------- /src/aura/fieldSetOutputSection/fieldSetOutputSectionHelper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - fieldSetOutputSectionHelper.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | setSectionBody : function(component, event, helper) { 12 | var action = component.get("c.getOutputFields"); 13 | action.setParams({ 14 | "pRecordId": component.get("v.recordId"), 15 | "pFieldSetName": component.get("v.fieldSetName") 16 | }); 17 | action.setCallback(this, function(response){ 18 | var state = response.getState(); 19 | if (component.isValid() && state === "SUCCESS") { 20 | var sectionFields = response.getReturnValue(); 21 | if(!$A.util.isEmpty(sectionFields) && !$A.util.isUndefined(sectionFields)){ 22 | helper.createSectionBody(component, helper, sectionFields); 23 | } 24 | } 25 | }); 26 | $A.enqueueAction(action); 27 | }, 28 | createSectionBody : function(component, helper, sectionFields){ 29 | var sectionType = component.get("v.sectionType"); 30 | if (sectionType == "Card"){ 31 | helper.createCardSection(component, helper, sectionFields); 32 | } 33 | else { 34 | helper.createLayoutSection(component, helper, sectionFields); 35 | } 36 | }, 37 | createCardSection : function(component, helper, sectionFields) { 38 | $A.createComponent( 39 | "c:cardOutputSection",{ 40 | "sectionTitle" : component.get("v.sectionTitle"), 41 | "sectionFields" : sectionFields, 42 | "sectionIcon" : component.get("v.sectionIcon"), 43 | "collapsible" : component.get("v.collapsible"), 44 | "columnClass" : helper.getColumnClass(component.get("v.columnsPerRow")) 45 | }, 46 | function(sectionBody, status, errorMessage){ 47 | if (status === "SUCCESS") { 48 | component.set("v.body", sectionBody); 49 | } 50 | else if (status === "INCOMPLETE") { 51 | console.log("No response from server or client is offline.") 52 | } 53 | else if (status === "ERROR") { 54 | console.log("Error: " + errorMessage); 55 | } 56 | } 57 | ); 58 | }, 59 | createLayoutSection : function(component, helper, sectionFields) { 60 | $A.createComponent( 61 | "c:layoutOutputSection",{ 62 | "sectionTitle" : component.get("v.sectionTitle"), 63 | "sectionFields" : sectionFields, 64 | "collapsible" : component.get("v.collapsible"), 65 | "columnClass" : helper.getColumnClass(component.get("v.columnsPerRow")) 66 | }, 67 | function(sectionBody, status, errorMessage){ 68 | if (status === "SUCCESS") { 69 | component.set("v.body", sectionBody); 70 | } 71 | else if (status === "INCOMPLETE") { 72 | console.log("No response from server or client is offline.") 73 | } 74 | else if (status === "ERROR") { 75 | console.log("Error: " + errorMessage); 76 | } 77 | } 78 | ); 79 | }, 80 | getColumnClass : function(numberOfColumns){ 81 | var columnClass = "slds-large-size_1-of-2"; //default 2 fields per row 82 | 83 | if (numberOfColumns == "1"){ 84 | columnClass = "slds-large-size_1-of-1"; 85 | } else if (numberOfColumns == "2"){ 86 | columnClass = "slds-large-size_1-of-2"; 87 | } else if (numberOfColumns == "3"){ 88 | columnClass = "slds-large-size_1-of-3"; 89 | } else if (numberOfColumns == "4"){ 90 | columnClass = "slds-large-size_1-of-4"; 91 | } 92 | 93 | return columnClass; 94 | } 95 | 96 | }) -------------------------------------------------------------------------------- /src/aura/layoutOutputSection/layoutOutputSection.cmp: -------------------------------------------------------------------------------- 1 | 10 | 11 | 13 | 15 | 17 | 19 | 20 |
21 |
22 |

23 | 34 |

35 | 36 |
37 |
38 | 39 |
40 |
41 | {!field.fieldLabel} 42 |
43 | 47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 56 |
-------------------------------------------------------------------------------- /src/aura/layoutOutputSection/layoutOutputSection.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Lightning component to display the fields in layout section style. 5 | 6 | -------------------------------------------------------------------------------- /src/aura/layoutOutputSection/layoutOutputSectionController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - layoutOutputSectionController.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | toggleDisplay : function(component, event, helper) { 12 | var isCollapsible = component.get("v.collapsible"); 13 | 14 | if (isCollapsible){ 15 | var titleIconDown = component.find("titleIconDown"); 16 | $A.util.toggleClass(titleIconDown, "slds-hide"); 17 | 18 | var titleIconRight = component.find("titleIconRight"); 19 | $A.util.toggleClass(titleIconRight, "slds-hide"); 20 | 21 | var sectionBody = component.find("sectionBody"); 22 | $A.util.toggleClass(sectionBody, "slds-hide"); 23 | } 24 | } 25 | }) -------------------------------------------------------------------------------- /src/aura/outputDataRow/outputDataRow.cmp: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {!field.fieldLabel} 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/aura/outputDataRow/outputDataRow.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Lightning component to display the data row of a table. 5 | 6 | -------------------------------------------------------------------------------- /src/aura/outputDataRow/outputDataRowController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - outputDataRowController.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | navigateToRecord : function(component, event, helper) { 12 | var selectedRecord = event.currentTarget; 13 | var targetRecordId = selectedRecord.dataset.record; 14 | var navEvt = $A.get("e.force:navigateToSObject"); 15 | navEvt.setParams({ 16 | "recordId": targetRecordId 17 | }); 18 | navEvt.fire(); 19 | } 20 | }) -------------------------------------------------------------------------------- /src/aura/outputDataTable/outputDataTable.cmp: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | {!v.dataRows} 38 | 39 |
31 |
{!column.fieldLabel}
32 |
40 | 41 |
42 | -------------------------------------------------------------------------------- /src/aura/outputDataTable/outputDataTable.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Lightning component to display the output (read-only) data table. 5 | 6 | -------------------------------------------------------------------------------- /src/aura/outputDataTable/outputDataTableController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - outputDataTableController.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | doInit : function(component, event, helper) { 12 | helper.createTableRows(component, event, helper); 13 | } 14 | }) -------------------------------------------------------------------------------- /src/aura/outputDataTable/outputDataTableHelper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - outputDataTableHelper.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | createTableRows : function(component, event, helper) { 12 | var tableRows = component.get("v.rows"); 13 | var maxRows = component.get("v.maxRowsDisplayed"); 14 | 15 | var totalRows = tableRows.length; 16 | console.log("Total Rows : " + totalRows); 17 | 18 | if (maxRows == null || maxRows == undefined){ 19 | maxRows = totalRows; 20 | } 21 | if (maxRows > totalRows){ 22 | maxRows = totalRows; 23 | } 24 | console.log("Rows to be dispalyed : " + maxRows); 25 | 26 | var dataRows = component.get("v.dataRows"); 27 | for(var i = 0; i < maxRows; i++){ 28 | $A.createComponent( 29 | "c:outputDataRow",{ 30 | "fields" : tableRows[i].fields, 31 | }, 32 | function(newRow, status, errorMessage){ 33 | if (status === "SUCCESS") { 34 | dataRows.push(newRow); 35 | component.set("v.dataRows", dataRows); 36 | } 37 | } 38 | ); 39 | }; 40 | } 41 | }) -------------------------------------------------------------------------------- /src/aura/outputField/outputField.cmp: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 14 | 16 | 18 | 20 | 21 | 22 | {!v.body} 23 | 24 | -------------------------------------------------------------------------------- /src/aura/outputField/outputField.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Component to display the output field as per the field data type. 5 | 6 | -------------------------------------------------------------------------------- /src/aura/outputField/outputFieldController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - outputFieldController.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | doInit : function(component, event, helper) { 12 | //get the appropriate ui component for the field type 13 | var uiComponentType = helper.getUIComponentType(component.get("v.type")); 14 | //console.log("Component Type : " + uiComponentType); 15 | 16 | if (uiComponentType == "ui:outputURL"){ 17 | helper.createURLField(component, event, helper); 18 | } 19 | else if (uiComponentType == "c:referenceField"){ 20 | helper.createReferenceField(component, event, helper); 21 | } 22 | else { 23 | helper.createField(component, event, helper, uiComponentType); 24 | } 25 | } 26 | 27 | }) -------------------------------------------------------------------------------- /src/aura/outputField/outputFieldHelper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - outputFieldHelper.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | getUIComponentType : function(fieldType) { 12 | var uiComponentType = "ui:outputText"; 13 | 14 | if (fieldType == "BOOLEAN"){ 15 | uiComponentType = "ui:outputCheckbox"; 16 | } 17 | else if (fieldType == "CURRENCY"){ 18 | uiComponentType = "ui:outputCurrency"; 19 | } 20 | else if (fieldType == "DATE"){ 21 | uiComponentType = "ui:outputDate"; 22 | } 23 | else if (fieldType == "DATETIME"){ 24 | uiComponentType = "ui:outputDateTime"; 25 | } 26 | else if (fieldType == "DOUBLE"){ 27 | uiComponentType = "ui:outputNumber"; 28 | } 29 | else if (fieldType == "EMAIL"){ 30 | uiComponentType = "ui:outputEmail"; 31 | } 32 | else if (fieldType == "INTEGER"){ 33 | uiComponentType = "ui:outputNumber"; 34 | } 35 | else if (fieldType == "PERCENT"){ 36 | uiComponentType = "ui:outputNumber"; 37 | } 38 | else if (fieldType == "PHONE"){ 39 | uiComponentType = "ui:outputPhone"; 40 | } 41 | else if (fieldType == "TEXTAREA"){ 42 | uiComponentType = "ui:outputTextArea"; 43 | } 44 | else if (fieldType == "URL"){ 45 | uiComponentType = "ui:outputURL"; 46 | } 47 | else if (fieldType == "REFERENCE"){ 48 | uiComponentType = "c:referenceField"; 49 | } 50 | else { 51 | uiComponentType = "ui:outputText"; 52 | } 53 | 54 | return uiComponentType; 55 | }, 56 | 57 | createField : function(component, event, helper, uiComponentType){ 58 | $A.createComponent( 59 | uiComponentType, 60 | { 61 | "value": component.get("v.value"), 62 | "class": component.get("v.class") 63 | }, 64 | function(uiComponent, status, errorMessage){ 65 | if (status === "SUCCESS") { 66 | helper.setFieldBody(component, uiComponent); 67 | } 68 | else if (status === "INCOMPLETE") { 69 | console.log("No response from server or client is offline.") 70 | } 71 | else if (status === "ERROR") { 72 | console.log("Error: " + errorMessage); 73 | } 74 | } 75 | ); 76 | }, 77 | 78 | createURLField : function(component, event, helper){ 79 | var urlLabel = component.get("v.label"); 80 | if (urlLabel == null || urlLabel == undefined || urlLabel == ""){ 81 | urlLabel = component.get("v.value"); 82 | } 83 | 84 | $A.createComponent( 85 | "ui:outputURL", 86 | { 87 | "value": component.get("v.value"), 88 | "label": urlLabel, 89 | "class": component.get("v.class") 90 | }, 91 | function(urlField, status, errorMessage){ 92 | if (status === "SUCCESS") { 93 | helper.setFieldBody(component, urlField); 94 | } 95 | else if (status === "INCOMPLETE") { 96 | console.log("No response from server or client is offline.") 97 | } 98 | else if (status === "ERROR") { 99 | console.log("Error: " + errorMessage); 100 | } 101 | } 102 | ); 103 | }, 104 | 105 | createReferenceField : function(component, event, helper){ 106 | var fieldLabel = component.get("v.label"); 107 | if (fieldLabel == null || fieldLabel == undefined || fieldLabel == ""){ 108 | fieldLabel = component.get("v.value"); 109 | } 110 | 111 | $A.createComponent( 112 | "c:referenceField", 113 | { 114 | "referenceRecordId": component.get("v.value"), 115 | "referenceRecordName": fieldLabel, 116 | "class": component.get("v.class") 117 | }, 118 | function(refField, status, errorMessage){ 119 | if (status === "SUCCESS") { 120 | helper.setFieldBody(component, refField); 121 | } 122 | else if (status === "INCOMPLETE") { 123 | console.log("No response from server or client is offline.") 124 | } 125 | else if (status === "ERROR") { 126 | console.log("Error: " + errorMessage); 127 | } 128 | } 129 | ); 130 | }, 131 | 132 | setFieldBody : function(component, createdField){ 133 | var componentBody = component.get("v.body"); 134 | componentBody.push(createdField); 135 | component.set("v.body", componentBody); 136 | } 137 | }) -------------------------------------------------------------------------------- /src/aura/referenceField/referenceField.cmp: -------------------------------------------------------------------------------- 1 | 10 | 11 | 13 | 15 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /src/aura/referenceField/referenceField.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Lightning Component to display the reference field on the layout. 5 | 6 | -------------------------------------------------------------------------------- /src/aura/referenceField/referenceFieldController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - referenceFieldController.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | navigateToReferenceRecord : function(component, event, helper) { 12 | var selectedRecord = event.currentTarget; 13 | var referenceRecordId = selectedRecord.dataset.record; 14 | var navEvt = $A.get("e.force:navigateToSObject"); 15 | navEvt.setParams({ 16 | "recordId": referenceRecordId 17 | }); 18 | navEvt.fire(); 19 | } 20 | }) -------------------------------------------------------------------------------- /src/aura/relatedRecordsTable/relatedRecordsTable.cmp: -------------------------------------------------------------------------------- 1 | 10 | 12 | 13 | 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {!v.tableTitle} 46 | 47 | 48 | 49 | 50 | {!$Label.c.Hide_Records_Label} 51 | {!$Label.c.Show_Records_Label} 52 | 53 | 54 | 55 | 56 |
57 | {!v.relatedRecords} 58 |
59 |
60 | 61 | 64 | 65 |
66 | 67 |
-------------------------------------------------------------------------------- /src/aura/relatedRecordsTable/relatedRecordsTable.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Lightning component to display the list of records in a table format from a Related object. 5 | 6 | -------------------------------------------------------------------------------- /src/aura/relatedRecordsTable/relatedRecordsTable.design: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 22 | -------------------------------------------------------------------------------- /src/aura/relatedRecordsTable/relatedRecordsTable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/aura/relatedRecordsTable/relatedRecordsTableController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - relatedRecordsTableController.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | doInit : function(component, event, helper) { 12 | helper.populateDataTable(component, event, helper); 13 | }, 14 | 15 | addRows : function(component, event, helper) { 16 | helper.createDataTable(component, event, helper); 17 | }, 18 | 19 | hideSpinner : function (component, event, helper) { 20 | var spinner = component.find('spinner'); 21 | $A.util.addClass(spinner, "slds-hide"); 22 | }, 23 | 24 | toggleDisplay : function(component, event, helper) { 25 | var isCollapsible = component.get("v.collapsible"); 26 | 27 | if (isCollapsible){ 28 | var hideLink = component.find("hideLink"); 29 | $A.util.toggleClass(hideLink, "slds-hide"); 30 | 31 | var showLink = component.find("showLink"); 32 | $A.util.toggleClass(showLink, "slds-hide"); 33 | 34 | var tableBody = component.find("tableBody"); 35 | $A.util.toggleClass(tableBody, "slds-hide"); 36 | 37 | var tablefooter = component.find("tablefooter"); 38 | $A.util.toggleClass(tablefooter, "slds-hide"); 39 | } 40 | } 41 | }) -------------------------------------------------------------------------------- /src/aura/relatedRecordsTable/relatedRecordsTableHelper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * File Name - relatedRecordsTableHelper.js 6 | * 7 | * Developer(s) - SSingh 8 | * 9 | */ 10 | ({ 11 | populateDataTable : function(component, event, helper) { 12 | console.log('Fetching data from server ...'); 13 | var action = component.get("c.getRecords"); 14 | action.setParams( 15 | { 16 | "pRecordId" : component.get("v.recordId"), 17 | "pRelatedObjectName" : component.get("v.relatedObjectName"), 18 | "pRelationshipName" : component.get("v.relationshipName"), 19 | "pFieldsString" : component.get("v.fieldsString"), 20 | "pSortByFieldsString" : component.get("v.sortByfields"), 21 | "pIsAscending" : component.get("v.ascending") 22 | } 23 | ); 24 | 25 | action.setCallback(this, function(response){ 26 | var state = response.getState(); 27 | if (component.isValid() && state === "SUCCESS") { 28 | var dataTable = response.getReturnValue(); 29 | if(!$A.util.isEmpty(dataTable) && !$A.util.isUndefined(dataTable)){ 30 | component.set("v.dataTable", dataTable); 31 | var totalRows = dataTable.rows.length; 32 | component.set("v.totalRows", totalRows); 33 | 34 | if (totalRows > 0){ 35 | helper.createDataTable(component, event, helper); 36 | } 37 | else { 38 | helper.setNoRecordsFound(component, helper); 39 | } 40 | } else { 41 | helper.setNoRecordsFound(component, helper); 42 | } 43 | } 44 | else if (state === "INCOMPLETE") { 45 | helper.setTextMessage(component, $A.get("$Label.fsLtng.No_response_from_server")); 46 | } 47 | else if (state === "ERROR") { 48 | var errors = response.getError(); 49 | if (errors) { 50 | if (errors[0] && errors[0].message) { 51 | console.log(errors[0].message); 52 | helper.setTextMessage(component, $A.get("$Label.fsLtng.Unexpected_Server_Side_Error")); 53 | } 54 | } 55 | } 56 | }); 57 | $A.enqueueAction(action); 58 | }, 59 | 60 | createDataTable : function(component, event, helper) { 61 | console.log('Creating data table ...'); 62 | var dataTable = component.get("v.dataTable"); 63 | var totalRows = parseInt(component.get("v.totalRows")); 64 | var rowsToBeDisplayed = parseInt(component.get("v.maxNumberOfRows")); 65 | var maxRowsDisplayed = (totalRows > rowsToBeDisplayed ? rowsToBeDisplayed : totalRows); 66 | 67 | $A.createComponent( 68 | "c:outputDataTable",{ 69 | "columns" : dataTable.columns, 70 | "rows" : dataTable.rows, 71 | "maxRowsDisplayed" : rowsToBeDisplayed 72 | }, 73 | function(relatedRecTable, status, errorMessage){ 74 | if (status === "SUCCESS") { 75 | component.set("v.relatedRecords", relatedRecTable); 76 | helper.setRowAttributes(component); 77 | 78 | var showHideAction = component.find('tableShowHideAction'); 79 | $A.util.removeClass(showHideAction, "slds-hide"); 80 | } 81 | } 82 | ); 83 | }, 84 | 85 | setRowAttributes : function(component){ 86 | var totalRows = parseInt(component.get("v.totalRows")); 87 | var rowsToBeDisplayed = parseInt(component.get("v.maxNumberOfRows")); 88 | 89 | var rowsNotDisplayed = totalRows - rowsToBeDisplayed; 90 | if (rowsNotDisplayed < 0){ 91 | rowsNotDisplayed = 0; 92 | } 93 | component.set("v.rowsNotDisplayed", rowsNotDisplayed); 94 | 95 | var showMore = component.find('showMoreRowsLink'); //This returns the anchor tag 96 | var showMoreLabel = ''; 97 | 98 | if (rowsNotDisplayed == 0){ 99 | $A.util.addClass(showMore, "slds-hide"); 100 | } 101 | else { 102 | $A.util.removeClass(showMore, "slds-hide"); 103 | if (rowsNotDisplayed > 0 && rowsNotDisplayed < rowsToBeDisplayed){ 104 | rowsToBeDisplayed = rowsToBeDisplayed + rowsNotDisplayed; 105 | showMoreLabel = $A.get("$Label.fsLtng.Show_Records_Label") + ' ' + rowsNotDisplayed.toString() + ' ' + $A.get("$Label.fsLtng.More_Records_Label"); 106 | } 107 | else if (rowsNotDisplayed > 0 && rowsNotDisplayed >= rowsToBeDisplayed){ 108 | showMoreLabel = $A.get("$Label.fsLtng.Show_Records_Label") + ' ' + rowsToBeDisplayed.toString() + ' ' + $A.get("$Label.fsLtng.More_Records_Label"); 109 | rowsToBeDisplayed = rowsToBeDisplayed * 2; 110 | } 111 | } 112 | 113 | //records to be displayed on next show more click 114 | component.set("v.maxNumberOfRows", rowsToBeDisplayed); 115 | 116 | //set the text for the show more rows text 117 | $A.createComponent( 118 | "ui:outputText",{ 119 | "value" : showMoreLabel 120 | }, 121 | function(showMoreLink, status, errorMessage){ 122 | if (status === "SUCCESS") { 123 | component.set("v.showMoreRowsText", showMoreLink); 124 | } 125 | } 126 | ); 127 | 128 | }, 129 | 130 | setNoRecordsFound : function(component, helper){ 131 | helper.setTextMessage(component, $A.get("$Label.fsLtng.No_records_to_display") ); 132 | 133 | var showHideAction = component.find('tableShowHideAction'); 134 | $A.util.addClass(showHideAction, "slds-hide"); 135 | }, 136 | 137 | setTextMessage : function(component, text){ 138 | $A.createComponent( 139 | "ui:outputText",{ 140 | "value" : text, 141 | "class" : "slds-align_absolute-center" 142 | }, 143 | function(defaultText, status, errorMessage){ 144 | if (status === "SUCCESS") { 145 | component.set("v.relatedRecords", defaultText); 146 | } 147 | } 148 | ); 149 | } 150 | }) -------------------------------------------------------------------------------- /src/classes/DataTable.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * Class Name - DataTable 6 | * 7 | * Description - Structure to store columns and rows of a data table 8 | * 9 | * Developer(s) - SSingh 10 | */ 11 | global class DataTable { 12 | @AuraEnabled global Field[] columns; 13 | @AuraEnabled global Row[] rows; 14 | } -------------------------------------------------------------------------------- /src/classes/DataTable.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/Field.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * Class Name - Field 6 | * 7 | * Description - Structure to keep the attributes related to 8 | * an SObject field along with the value 9 | * 10 | * Developer(s) - SSingh 11 | * 12 | * Modification(s) 13 | * 10-Jul-2017 Added attribute fieldDisplayValue 14 | * This attribute is populated for reference fields and 15 | * keeps the name of the record whose Id is stored in the 16 | * reference or lookup field 17 | * 18 | * 19 | */ 20 | global class Field { 21 | 22 | @AuraEnabled global String fieldName {get; set;} 23 | @AuraEnabled global String fieldLabel {get; set;} 24 | @AuraEnabled global String fieldType {get; set;} 25 | @AuraEnabled global Object fieldValue {get; set;} 26 | @AuraEnabled global Boolean isRequired {get; set;} 27 | @AuraEnabled global Integer fieldPosition {get; set;} 28 | @AuraEnabled global String fieldHelpText {get; set;} 29 | 30 | //Added for supporting reference fields 31 | @AuraEnabled global String fieldDisplayValue {get; set;} 32 | } 33 | -------------------------------------------------------------------------------- /src/classes/Field.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/FieldSetSectionController.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * Class Name - FieldSetSectionController 6 | * 7 | * Description - Apex Controller for Field Set Output Section 8 | * Lightning component 9 | * 10 | * Developer(s) - SSingh 11 | */ 12 | global with sharing class FieldSetSectionController { 13 | 14 | @AuraEnabled 15 | global static Field[] getOutputFields(Id pRecordId 16 | , String pFieldSetName){ 17 | Schema.SObjectType sObjectType = pRecordId.getSobjectType(); 18 | Map fieldSetMap = sObjectType.getDescribe().fieldSets.getMap(); 19 | Map fieldMap = sObjectType.getDescribe().fields.getMap(); 20 | 21 | if (!fieldSetMap.containsKey(pFieldSetName)){ 22 | return null; 23 | } 24 | 25 | Schema.FieldSet fieldSet = fieldSetMap.get(pFieldSetName); 26 | String sObjectName = sObjectType.getDescribe().getName(); 27 | String queryFieldString = ''; 28 | for(Schema.FieldSetMember fsMember : fieldSet.getFields()){ 29 | Schema.DescribeFieldResult fieldDesc = fieldMap.get(fsMember.getFieldPath()).getDescribe(); 30 | //skip if field is not accessible 31 | if (!fieldDesc.isAccessible()) 32 | continue; 33 | 34 | //skip if Id or Name field 35 | if (fieldDesc.getName().equalsIgnoreCase('Id')) 36 | continue; 37 | 38 | //add field to the query string 39 | if (fieldDesc.getType() == Schema.DisplayType.Picklist){ 40 | queryFieldString += 'toLabel(' + fieldDesc.getName() + '), '; 41 | } 42 | else { 43 | queryFieldString += fieldDesc.getName() + ', '; 44 | } 45 | 46 | if (fieldDesc.getType() == Schema.DisplayType.Reference){ 47 | queryFieldString += fieldDesc.getRelationshipName() + '.Id, '; 48 | queryFieldString += fieldDesc.getRelationshipName() + '.Name, '; 49 | } 50 | } 51 | queryFieldString += 'Id'; 52 | 53 | //prepare SOQL 54 | String recordSOQL = 'SELECT ' + queryFieldString + ' FROM ' + sObjectName + ' WHERE Id = :pRecordId'; 55 | 56 | //execute SOQL 57 | SObject sObjRecord = Database.query(recordSOQL); 58 | 59 | //populate fields details 60 | Field[] sectionFields = new Field[]{}; 61 | for(Schema.FieldSetMember fsMember : fieldSet.getFields()){ 62 | Schema.DescribeFieldResult fieldDesc = fieldMap.get(fsMember.getFieldPath()).getDescribe(); 63 | 64 | //add field to the section field list only if it is accessible to user 65 | if (fieldDesc.isAccessible()){ 66 | Field field = new Field(); 67 | field.fieldName = fieldDesc.getName(); 68 | field.fieldLabel = fieldDesc.getLabel(); 69 | field.isRequired = (fsMember.getDBRequired() || fsMember.getRequired()); 70 | field.fieldValue = sObjRecord.get(fieldDesc.getName()); 71 | field.fieldType = String.valueOf(fieldDesc.getType()); 72 | field.fieldHelpText = fieldDesc.getInlineHelpText(); 73 | sectionFields.add(field); 74 | 75 | //set display value for Reference field 76 | if (fieldDesc.getType() == Schema.DisplayType.Reference && field.fieldValue != null){ 77 | SObject refRecord = sObjRecord.getSObject(fieldDesc.getRelationshipName()); 78 | field.fieldDisplayValue = String.valueOf(refRecord.get('Name')); 79 | } 80 | else { 81 | field.fieldDisplayValue = String.valueOf(field.fieldValue); 82 | } 83 | } 84 | } 85 | 86 | return sectionFields; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/classes/FieldSetSectionController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/FieldSetSectionControllerTest.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * Class Name - FieldSetSectionControllerTest 6 | * 7 | * Description - Test class for (1) FieldSetSectionController (2) Field 8 | * 9 | * Developer(s) - SSingh 10 | */ 11 | @isTest(seeAllData=false) 12 | private class FieldSetSectionControllerTest { 13 | 14 | @testSetup 15 | static void createTestData(){ 16 | Account testAccount = new Account(); 17 | testAccount.Name = 'Avengers Corp'; 18 | testAccount.AnnualRevenue = 5000000; 19 | testAccount.Industry = 'Energy'; 20 | testAccount.NumberOfEmployees = 150; 21 | testAccount.Rating = 'Hot'; 22 | 23 | insert testAccount; 24 | System.assert(testAccount.Id != null); 25 | } 26 | 27 | @isTest 28 | private static void testGetOutputFields(){ 29 | Account testAccount = [SELECT Id FROM Account WHERE Name = 'Avengers Corp' LIMIT 1]; 30 | 31 | Test.startTest(); 32 | Field[] sectionFields = FieldSetSectionController.getOutputFields(testAccount.Id, 'fsLtng__Account_Additional_Information'); 33 | System.assert(sectionFields.size() > 0); 34 | Test.stopTest(); 35 | } 36 | 37 | @isTest 38 | private static void testGetOutputFields_NegativeScenario(){ 39 | Account testAccount = [SELECT Id FROM Account WHERE Name = 'Avengers Corp' LIMIT 1]; 40 | 41 | Test.startTest(); 42 | Field[] sectionFields = FieldSetSectionController.getOutputFields(testAccount.Id, 'DummyFieldSet'); 43 | System.assert(sectionFields == null); 44 | Test.stopTest(); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/classes/FieldSetSectionControllerTest.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/RelatedRecordsTableController.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * Class Name - RelatedRecordsTableController 6 | * 7 | * Description - Apex Controller for Related Records Table 8 | * Lightning Component 9 | * 10 | * Developer(s) - SSingh 11 | */ 12 | global with sharing class RelatedRecordsTableController { 13 | 14 | @AuraEnabled 15 | global static DataTable getRecords(String pRecordId 16 | , String pRelatedObjectName 17 | , String pRelationshipName 18 | , String pFieldsString 19 | , String pSortByFieldsString 20 | , Boolean pIsAscending){ 21 | 22 | if (String.isBlank(pRecordId) || String.isBlank(pRelatedObjectName) || String.isBlank(pRelationshipName)){ 23 | return null; 24 | } 25 | 26 | Id recordId = Id.valueOf(pRecordId); 27 | 28 | /* Declaring local variables to handle SOQL Injection */ 29 | String relatedObjectName = String.escapeSingleQuotes(pRelatedObjectName); 30 | String relationshipName = String.escapeSingleQuotes(pRelationshipName); 31 | String fieldsString = String.escapeSingleQuotes(pFieldsString); 32 | String sortByFieldsString = String.escapeSingleQuotes(pSortByFieldsString); 33 | 34 | /* 35 | * validate the relationship 36 | */ 37 | List childRelationships = recordId.getSObjectType().getDescribe().getChildRelationships(); 38 | Schema.DescribeSObjectResult relateObjDesc; 39 | String relationshipFieldName; 40 | 41 | for (Schema.ChildRelationship childObj : childRelationships){ 42 | if (childObj.getChildSObject().getDescribe().getName().equalsIgnoreCase(relatedObjectName) 43 | && childObj.getRelationshipName().equalsIgnoreCase(relationshipName)){ 44 | relateObjDesc = childObj.getChildSObject().getDescribe(); 45 | relationshipFieldName = childObj.getField().getDescribe().getName(); 46 | } 47 | } 48 | 49 | if (String.isBlank(relationshipFieldName)){ 50 | throw new RelatedRecordsException(Label.Invalid_SObject_Relationship); 51 | } 52 | 53 | //if the related object is not accessible to the user then return null 54 | if (!relateObjDesc.isAccessible()){ 55 | return null; 56 | } 57 | 58 | /* 59 | * eliminate invalid and duplicate fields 60 | */ 61 | Map fieldMap = relateObjDesc.fields.getMap(); 62 | Map accessibleFieldMap = new Map(); 63 | 64 | for (String fieldName : fieldMap.keySet()){ 65 | //only add accessible fields to the map 66 | if (fieldMap.get(fieldName).getDescribe().isAccessible()){ 67 | accessibleFieldMap.put(fieldName.toLowerCase(), fieldMap.get(fieldName)); 68 | } 69 | } 70 | 71 | Map positionToFieldMap = new Map(); 72 | Set uniqueFields = new Set(); 73 | Integer columnPosition = 0; 74 | 75 | //put Id field on 0 position 76 | positionToFieldMap.put(columnPosition, 'id'); 77 | uniqueFields.add('id'); 78 | columnPosition++; 79 | 80 | fieldsString = String.isBlank(fieldsString) ? '' : fieldsString; 81 | String[] fieldList = fieldsString.split(','); 82 | 83 | for (String field : fieldList){ 84 | String fieldName = field.trim().toLowerCase(); 85 | if (!uniqueFields.contains(fieldName) && accessibleFieldMap.containsKey(fieldName)){ 86 | Schema.DescribeFieldResult fieldDesc = accessibleFieldMap.get(fieldName).getDescribe(); 87 | 88 | positionToFieldMap.put(columnPosition, fieldName); 89 | columnPosition++; 90 | 91 | if (fieldDesc.getType() == Schema.DisplayType.Picklist){ 92 | uniqueFields.add('toLabel(' + fieldDesc.getName().toLowerCase() + ')'); 93 | } 94 | else if (fieldDesc.getType() == Schema.DisplayType.Reference){ 95 | uniqueFields.add(fieldDesc.getName().toLowerCase()); 96 | 97 | String refRelationName = fieldDesc.getRelationshipName().toLowerCase(); 98 | uniqueFields.add(refRelationName + '.id'); 99 | uniqueFields.add(refRelationName + '.name'); 100 | } 101 | else { 102 | uniqueFields.add(fieldDesc.getName().toLowerCase()); 103 | } 104 | } 105 | } 106 | 107 | if (positionToFieldMap.size() == 1){ //if no field added to the map then add Name field 108 | positionToFieldMap.put(columnPosition, 'name'); 109 | columnPosition++; 110 | } 111 | 112 | /* 113 | * query related records 114 | */ 115 | List queryFieldList = new List(uniqueFields); 116 | String relatedRecordsSOQL = 'SELECT ' + String.join(queryFieldList, ',') + 117 | ' FROM ' + relateObjDesc.getName() + 118 | ' WHERE ' + relationshipFieldName + ' = :recordId'; 119 | 120 | //preparing order by clause 121 | String orderByClause; 122 | String sortOrder = (pIsAscending ? 'ASC': 'DESC'); 123 | 124 | if (String.isNotBlank(sortByFieldsString)){ 125 | String[] sortByFieldList = sortByFieldsString.split(','); 126 | List orderByClauseList = new List(); 127 | 128 | for (String field : sortByFieldList){ 129 | String fieldName = field.trim().toLowerCase(); 130 | if (accessibleFieldMap.containsKey(fieldName)){ 131 | orderByClauseList.add(fieldName + ' ' + sortOrder); 132 | } 133 | } 134 | orderByClause = ' ORDER BY ' + String.join(orderByClauseList, ','); 135 | } 136 | else { 137 | orderByClause = ' ORDER BY ' + positionToFieldMap.get(1) + ' ' + sortOrder; 138 | } 139 | 140 | //prepare SOQL 141 | relatedRecordsSOQL = relatedRecordsSOQL + orderByClause; 142 | 143 | //execute SOQL 144 | List relatedRecords = Database.query(relatedRecordsSOQL); 145 | 146 | /* 147 | * prepare response 148 | */ 149 | DataTable recordTable = new DataTable(); 150 | recordTable.columns = new Field[]{}; 151 | recordTable.rows = new Row[]{}; 152 | 153 | // prepare table columns data 154 | for(Integer colPosition : positionToFieldMap.keySet()){ 155 | if (colPosition == 0) continue; //Do not include name Id column in table columns 156 | Schema.DescribeFieldResult fieldDesc = accessibleFieldMap.get(positionToFieldMap.get(colPosition)).getDescribe(); 157 | Field column = new Field(); 158 | column.fieldPosition = colPosition; 159 | column.fieldName = fieldDesc.getName(); 160 | column.fieldLabel = fieldDesc.getLabel(); 161 | column.fieldType = String.valueOf(fieldDesc.getType()); 162 | column.fieldHelpText = fieldDesc.getInlineHelpText(); 163 | recordTable.columns.add(column); 164 | } 165 | 166 | // prepare table rows data 167 | for(SObject relatedRecord : relatedRecords){ 168 | Row row = new Row(); 169 | row.fields = new Field[]{}; 170 | 171 | for (Field column : recordTable.columns){ 172 | Schema.DescribeFieldResult fieldDesc = accessibleFieldMap.get(positionToFieldMap.get(column.fieldPosition)).getDescribe(); 173 | 174 | Field field = new Field(); 175 | field.fieldPosition = column.fieldPosition; 176 | field.fieldName = column.fieldName; 177 | field.fieldType = column.fieldType; 178 | 179 | if (column.fieldName == 'Name'){ 180 | field.fieldLabel = String.valueOf(relatedRecord.get('Name')); 181 | field.fieldValue = String.valueOf(relatedRecord.get('Id')); 182 | } 183 | else{ 184 | field.fieldLabel = column.fieldLabel; 185 | field.fieldValue = relatedRecord.get(column.fieldName); 186 | if (fieldDesc.getType() == Schema.DisplayType.Reference && 187 | field.fieldValue != null){ 188 | SObject refRecord = relatedRecord.getSObject(fieldDesc.getRelationshipName()); 189 | field.fieldDisplayValue = String.valueOf(refRecord.get('Name')); 190 | } 191 | else { 192 | field.fieldDisplayValue = String.valueOf(field.fieldValue); 193 | } 194 | } 195 | 196 | row.fields.add(field); 197 | } 198 | 199 | recordTable.rows.add(row); 200 | } 201 | 202 | return recordTable; 203 | } 204 | 205 | global class RelatedRecordsException extends Exception {} 206 | 207 | } -------------------------------------------------------------------------------- /src/classes/RelatedRecordsTableController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/RelatedRecordsTableControllerTest.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * Class Name - RelatedRecordsTableControllerTest 6 | * 7 | * Description - Test class for (1) RelatedRecordsTableController 8 | * (2) DataTable 9 | * (3) Row 10 | * 11 | * Developer(s) - SSingh 12 | */ 13 | @isTest(seeAllData=false) 14 | private class RelatedRecordsTableControllerTest { 15 | 16 | @testSetup 17 | static void createTestData(){ 18 | Account testAccount = new Account(); 19 | testAccount.Name = 'Avengers Corp'; 20 | testAccount.AnnualRevenue = 5000000; 21 | testAccount.Industry = 'Energy'; 22 | testAccount.NumberOfEmployees = 150; 23 | testAccount.Rating = 'Hot'; 24 | insert testAccount; 25 | System.assert(testAccount.Id != null); 26 | 27 | Contact testContact = new Contact(); 28 | testContact.FirstName = 'Tony'; 29 | testContact.LastName = 'Stark'; 30 | testContact.Phone = '6501234567'; 31 | testContact.Email = 'tony.s@avengers.com'; 32 | testContact.AccountId = testAccount.Id; 33 | insert testContact; 34 | System.assert(testContact.Id != null); 35 | 36 | //following labels are added to automically add them to the package 37 | String label_1 = Label.Hide_Records_Label; 38 | String label_2 = Label.More_Records_Label; 39 | String label_3 = Label.Show_Records_Label; 40 | String label_4 = Label.No_records_to_display; 41 | String label_5 = Label.No_response_from_server; 42 | String label_6 = Label.Invalid_SObject_Relationship; 43 | String label_7 = Label.Unexpected_Server_Side_Error; 44 | } 45 | 46 | @isTest 47 | private static void testGetRecords(){ 48 | Account testAccount = [SELECT Id FROM Account WHERE Name = 'Avengers Corp' LIMIT 1]; 49 | 50 | Test.startTest(); 51 | DataTable relatedRecords = RelatedRecordsTableController.getRecords(testAccount.Id 52 | , 'Contact' 53 | , 'Contacts' 54 | , 'FirstName, LastName, AccountId, Email, Phone' 55 | , 'LastName, FirstName' 56 | , true); 57 | System.assert(relatedRecords != null); 58 | System.assert(relatedRecords.rows.size() == 1); 59 | Test.stopTest(); 60 | } 61 | 62 | @isTest 63 | private static void testGetRecords_NegativeScenarios(){ 64 | Account testAccount = [SELECT Id FROM Account WHERE Name = 'Avengers Corp' LIMIT 1]; 65 | 66 | Test.startTest(); 67 | DataTable relatedRecords = RelatedRecordsTableController.getRecords(null 68 | , 'Contact' 69 | , 'Contacts' 70 | , 'FirstName, LastName, Email, Phone' 71 | , 'LastName, FirstName' 72 | , true); 73 | System.assert(relatedRecords == null); 74 | 75 | Boolean hasError = false; 76 | try{ 77 | relatedRecords = RelatedRecordsTableController.getRecords(testAccount.Id 78 | , 'Contact' 79 | , 'fsLtng__Contact__r' 80 | , 'FirstName, LastName, Email, Phone' 81 | , 'LastName, FirstName' 82 | , true); 83 | } 84 | catch (Exception e){ 85 | hasError = true; 86 | } 87 | System.assert(hasError); 88 | System.assert(relatedRecords == null); 89 | 90 | Test.stopTest(); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /src/classes/RelatedRecordsTableControllerTest.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/Row.cls: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Financial Spectra 3 | * All rights reserved. 4 | * 5 | * Class Name - Row 6 | * 7 | * Description - Structure to keep the value of columns of a row 8 | * 9 | * Developer(s) - SSingh 10 | */ 11 | global class Row { 12 | @AuraEnabled global Field[] fields; 13 | } -------------------------------------------------------------------------------- /src/classes/Row.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | * 5 | ApexClass 6 | 7 | 8 | * 9 | ApexComponent 10 | 11 | 12 | * 13 | ApexPage 14 | 15 | 16 | * 17 | ApexTestSuite 18 | 19 | 20 | * 21 | ApexTrigger 22 | 23 | 24 | * 25 | AuraDefinitionBundle 26 | 27 | 28 | * 29 | StaticResource 30 | 31 | 38.0 32 | 33 | --------------------------------------------------------------------------------