0

I am dynamically creating some DOM elements inside my component's controller and adding them to the page. I need to set the onclick event on these elements to call another controller function that fires an event.

Here is how I create the elements

var consoEvent = $A.get('e.c:ConsommationPrsLoad');
for(var index in prsList)
{
    var prs = prsList[index];
    var prestationBlock = document.createElement("div");

    // defining the onclick behaviour
    prestationBlock.onclick = function()
    {
        var prsID = prs.DW_PRSID__c;
        consoEvent.setParams({ 'prsID' : prsID });
        consoEvent.fire();
    };

    document.getElementById("container").appendChild(prestationBlock);
}

I have 2 issues

  1. The prsID I retrieve from prs element is always the same for every element created.
  2. Only the first time I click an element fires the event : then, nothing happens eventwise !! How weird...
1

This is not the "lightning" way of doing things. You should be (a) creating the "component" and (b) using method references, and (c) using merge references.

Here's a minimalist example:

<aura:application >
    <aura:attribute name="dynamic" type="Aura.Component[]" />
    <aura:handler name="init" value="{!this}" action="{!c.init}" />
    {!v.dynamic}
</aura:application>

({
    init: function(component, event, helper) {
        $A.createComponents(
            [["div", { onclick: component.getReference("c.click"), "data-item-value": 42 }]],
            function(components) {
                component.set("v.dynamic", components);
            }
        );
    },
    click: function(component, event, helper) {
        alert(event.target.dataset.itemValue);
    }
})

/* Adding some style to see the div */
.THIS {
    border: 1px solid red;
    min-height: 100px;
}

Note how we bind to a reference for the controller's event handler, attach the data we want to preserve directly to the component, and use that data in the event handler. This is the standard method for creating elements in Lightning correctly.

If you want to use document.createElement, you can do so, but your onclick handler must use $A.getCallback or you'll have problems, and you must remember to only use an event once. After it is fired, it may not be used again. Our code gets around this by creating the event on the click handler.

Here's a version that should probably work in your case (but still not recommended):

for(var index in prsList)
{
    var prs = prsList[index];
    var prestationBlock = document.createElement("div");

    // defining the onclick behaviour
    prestationBlock.addEventListener("click", function() {
        var localPrs = prs; // Copy to local scope
        return $A.getCallback(function()
        {
            var consoEvent = $A.get('e.c:ConsommationPrsLoad');
            var prsID = localPrs.DW_PRSID__c;
            consoEvent.setParams({ prsID : prsID });
            consoEvent.fire();
        })
    });

    document.getElementById("container").appendChild(prestationBlock);
}

The prsID I retrieve from prs element is always the same for every element created.

The variables are in the wrong scope. You need to create a new closure. This is demonstrated in the last example.

Only the first time I click an element fires the event : then, nothing happens eventwise !! How weird...

You can't reuse events, which is what you tried to do. You have to create a new event every time you want to fire an event.

  • thank you very much for this complete answer I'm working on it ! – metheM Mar 14 at 15:10
  • Ok I have a question : what if I want to create some HTML children and append them to the <div> I created thanks to the $A.createComponent method ? – metheM Mar 14 at 15:52
  • @metheM set the "body" of the parent element, such as parent.set("v.body", children). – sfdcfox Mar 14 at 16:03
  • simple enough :-) I'm sorry I should have told you earlier, I need to append multiple parapgraphs. Should I create an array, push the children inside and assign it to the parent body ? – metheM Mar 14 at 16:05
  • @metheM Yes, body is an Array, so that would be the appropriate method for doing so. – sfdcfox Mar 14 at 16:06

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.