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

}
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);
    }
})
|improve this answer|||||
  • it didn't seem to work. Do you have a working example of the questions code above? – Alex Miller Jul 31 '19 at 13:24
  • @AlexMiller, I've updated my answer. – BlackCat Jul 31 '19 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.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.