1

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.

enter image description here

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; } } } 

2 Answers 2

0

The reason you can not access this div is Rendering Lifecycle.

init => render => afterRender => ... (Create a Custom Renderer)

You are trying to get elements in time when they haven't been rendered yet.

  1. Move doInit into helper.
  2. Create Renderer.js file in the folder of your component bundle.
({ afterRender : function(component, helper) { this.superAfterRender(); helper.doInit(component, helper); } }) 

Working example

test.cmp

<aura:component> <canvas id="{!globalId + 'canvas'}"></canvas> </aura:component> 

test.cmp-meta.xml

<?xml version="1.0" encoding="UTF-8"?> <AuraDefinitionBundle xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>45.0</apiVersion> <description>test</description> </AuraDefinitionBundle> 

testController.js

({ }) 

testHelper.js

({ doInit : function(component) { let globalId = component.getGlobalId(); let canvas = document.getElementById(globalId + 'canvas'); let ctx = canvas.getContext('2d'); } }) 

testRenderer.js

({ afterRender : function(component, helper) { this.superAfterRender(); helper.doInit(component); }, rerender : function(component, helper){ this.superRerender(); helper.doInit(component); } }) 
2
  • it didn't seem to work. Do you have a working example of the questions code above? Commented Jul 31, 2019 at 13:24
  • @AlexMiller, I've updated my answer. Commented Jul 31, 2019 at 14:26
0

You can't use document.getElementById or Element.innerHTML. The former won't work because ID values are "mangled" by the framework, and the latter shouldn't be used because it violates Locker Service restrictions. You'll need to use $A.createComponents to create the inner elements, an Aura.Component[] attribute to display the elements, and component.getReference to link controller actions to those components.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.