I'm utilizing this repo to create a custom kanban. I'm able to generate a list of tasks in my system but the trouble I'm having is drawing a line to connect two tasks together. The idea is to show continuity of sub-task and super-task.
I've been fiddling with the js controller in function doInit. The data loads and its failing on the line attempting to getBoundingClientRect() of undefined. I understand undefined means it was never initialized but neither option of component.find() or document.getElementById is working. Those are the Ids generated in the HTML by the task records generating.
Every post like this one explains it should be possible with document.getElementById() but so far hasn't. I'm confused why I can't get the list item element reference.
App/Component-parent:
<aura:component implements="flexipage:availableForAllPageTypes" access="global" > <c:EnhancedKanban objName="Task" objFields="['WhatId', 'WhoId', 'Subject', 'Status', 'Description', 'ActivityDate', 'Priority']" kanbanPicklistField="Status"/> </aura:component> component:
<!-- https://github.com/sfdcbox/Kanban-Lightning-component --> <aura:component controller="kanbanController" implements="flexipage:availableForAllPageTypes"> <aura:attribute name="objName" type="String"/> <aura:attribute name="objFields" type="String[]"/> <aura:attribute name="kanbanPicklistField" type="String"/> <aura:handler name="init" action="{!c.doInit}" value="{!this}"/> <aura:attribute name="kanbanData" type="kanbanController.kanbanWrap"/> <div style="padding:0.5rem;"> <aura:iteration var="pickVal" items="{!v.kanbanData.pickVals}"> <div class="stageContainer" style="{!'width:calc(100vw/'+(v.kanbanData.pickVals.length+0.5)+')'}"> <div class="slds-grid slds-grid_vertical"> <div> <div class="slds-media slds-no-space slds-has-divider_bottom-space slds-media_center" style="{!'width:calc(100vw/'+(v.kanbanData.pickVals.length+1)+')'}"> <div class="slds-media__body"> <h1 class="slds-page-header__title slds-align-middle slds-truncate" title="{!pickVal}">{!pickVal}</h1> </div> </div> </div> </div> <ul ondrop="{!c.drop}" ondragover="{!c.allowDrop}" class="slds-has-dividers_around-space dropZone" data-Pick-Val="{!pickVal}" style="height:70vh;overflow-y:auto;"> <aura:iteration var="objRecord" items="{!v.kanbanData.records}"> <aura:if isTrue="{!pickVal == objRecord.Status}"> <li class="slds-item slds-m-around_small" draggable="true" ondragstart="{!c.drag}" id="{!objRecord.Id}"> <article class="slds-tile slds-tile_board"> <h3 class="slds-truncate" title="{!objRecord.Name}"> <a href="javascript:void(0);" onclick="{!c.doView}"> <span class="slds-truncate slds-text-heading_medium" id="{!objRecord.Id}">{!objRecord.Subject}</span> </a> </h3> <div class="slds-tile__detail slds-text-body_small"> <p class="slds-text-heading_medium">{!objRecord.Description}</p> <p class="slds-text-heading_medium">{!objRecord.ActivityDate}</p> <p class="slds-text-heading_medium">{!objRecord.Priority}</p> <!-- <p class="slds-truncate" title="{!objRecord.Account.Name}"> <a href="javascript:void(0);" onclick="{!c.doView}"> <span class="slds-truncate" id="{!objRecord.AccountId}">{!objRecord.Account.Name}</span> </a> </p> --> <!-- <p class="slds-truncate" title="{!'Closing ' +objRecord.CloseDate}">Closing {!'Closing ' +objRecord.CloseDate}</p> --> </div> </article> </li> </aura:if> </aura:iteration> </ul> </div> </aura:iteration> </div> </aura:component> controller
({ doInit: function(component, event, helper) { var action = component.get("c.getKanbanWrap"); action.setParams({ "objName":component.get("v.objName"), "objFields":component.get("v.objFields"), "kanbanField":component.get("v.kanbanPicklistField") }); action.setCallback(this, function(response){ var state = response.getState(); if (state === "SUCCESS") { console.dir(response.getReturnValue()); component.set("v.kanbanData", response.getReturnValue()); // ATTEMPT TO CONNECT LINE var div1 = document.getElementById('00Tg000000SJbRiEAL'); //component.find('00Tg000000SJbRiEAL'); var div2 = document.getElementById('00Tg000000SJbTLEA1'); //component.find('00Tg000000SJbTLEA1');// var rect1 = div1.getBoundingClientRect(); // Fails here: Uncaught Error in $A.getCallback() [Cannot read property 'getBoundingClientRect' of undefined] Callback failed: apex://kanbanController/ACTION$getKanbanWrap var rect2 = div2.getBoundingClientRect(); // bottom right var x1 = rect1.left + window.pageXOffset + (rect1.width || div1.offsetWidth); var y1 = rect1.top + window.pageYOffset + (rect1.height || div1.offsetHeight); // top right var x2 = rect2.left + window.pageXOffset + (rect2.width || div2.offsetWidth); var y2 = rect2.top + window.pageYOffset + (rect2.height || div2.offsetHeight); // distance var length = Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1))); // center var cx = ((x1 + x2) / 2) - (length / 2); var cy = ((y1 + y2) / 2) - (5 / 2); // angle var angle = Math.atan2((y1-y2),(x1-x2))*(180/Math.PI); // make hr var htmlLine = "<div style='padding:0px; margin:0px; height:" + 5 + "px; background-color:" + "#0F0" + "; line-height:1px; position:absolute; left:" + cx + "px; top:" + cy + "px; width:" + length + "px; -moz-transform:rotate(" + angle + "deg); -webkit-transform:rotate(" + angle + "deg); -o-transform:rotate(" + angle + "deg); -ms-transform:rotate(" + angle + "deg); transform:rotate(" + angle + "deg);' />"; // document.body.innerHTML += htmlLine; // ATTEMPT TO CONNECT LINE } }); $A.enqueueAction(action); }, doView: function(component, event, helper) { var editRecordEvent = $A.get("e.force:navigateToSObject"); editRecordEvent.setParams({ "recordId": event.target.id }); editRecordEvent.fire(); }, allowDrop: function(component, event, helper) { event.preventDefault(); }, drag: function (component, event, helper) { event.dataTransfer.setData("text", event.target.id); }, drop: function (component, event, helper) { event.preventDefault(); var data = event.dataTransfer.getData("text"); var tar = event.target; while(tar.tagName != 'ul' && tar.tagName != 'UL') tar = tar.parentElement; tar.appendChild(document.getElementById(data)); console.log('aaaaaaaaaaaaa : ' + tar.getAttribute('data-Pick-Val')); document.getElementById(data).style.backgroundColor = "#ffb75d"; helper.updatePickVal(component,data,component.get("v.kanbanPicklistField"),tar.getAttribute('data-Pick-Val')); } }) helper
({ updatePickVal : function(component, recId, pField, pVal) { //Id recId, String kanbanField, String kanbanNewValue var action = component.get("c.getUpdateStage"); action.setParams({ "recId":recId, "kanbanField":pField, "kanbanNewValue":pVal }); action.setCallback(this, function(response){ var state = response.getState(); if (state === "SUCCESS") { console.log(response.getReturnValue()); document.getElementById(recId).style.backgroundColor = "#04844b"; setTimeout(function(){ document.getElementById(recId).style.backgroundColor = ""; }, 300); } }); $A.enqueueAction(action); } }) kanbanController
public class kanbanController { @AuraEnabled public static kanbanWrap getKanbanWrap(String objName, String[] objFields, String kanbanField) { List<String> lstPickvals=new List<String>(); Schema.DescribeFieldResult fieldResult = Schema.getGlobalDescribe().get(objName).getDescribe().fields.getMap().get(kanbanField).getDescribe(); List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues(); for (Schema.PicklistEntry a : ple) { lstPickvals.add(a.getValue()); } System.debug(lstPickvals); String query = 'SELECT Id, '; for(String s:objFields){ query += s+' ,'; } query = query.removeEnd(','); query += ' FROM ' + objName; System.debug('qq ' + query); return new kanbanWrap(Database.query(query), lstPickvals); } @AuraEnabled public static String getUpdateStage(Id recId, String kanbanField, String kanbanNewValue) { SObject o1 = recId.getSObjectType().newSObject(recId); o1.put(kanbanField,kanbanNewValue); update o1; return 'Success'; } public class kanbanWrap{ @AuraEnabled List<sObject> records {get;set;} @AuraEnabled List<String> pickVals {get;set;} public kanbanWrap(List<sObject> recs, List<String> pVals){ this.records = recs; this.pickVals = pVals; } } } 