4

How do I add ui:inputDate element dynamically inside aura:iteration with unique value for aura:id attribute?

Here is the sample code:

HelloWorlApp.app:

<aura:application >
    <c:ExampleComponent />
</aura:application>

ExampleComponent.cmp:

<aura:component >
    <aura:attribute name="conList"
                    type="List"
                    default="[{'sfId':'AAAAAAAAAAAAAAA','dob':'1980-01-01'},{'sfId':'BBBBBBBBBBBBBBB','dob':'1990-01-01'}]">
    </aura:attribute>
    <aura:attribute name="xy" type="Aura.Component[]"></aura:attribute>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <table>
        <aura:iteration items="{!v.conList}" var="item">
            <tr>
                <td>{!item.sfId}</td>
                <td>{!v.xy}</td>
            </tr>
        </aura:iteration>
    </table>
</aura:component>

ExampleComponentController.js:

({
    doInit : function(cmp,event,helper){
        console.log('From doInit method');
        var itemsArray = cmp.get("v.conList");
        for (var i = 0; i < itemsArray.length; i++) {
            var item = itemsArray[i];
            $A.createComponent(
                "ui:inputDate",
                {
                    "aura:id": "ip-date-" + item.sfId,
                    "value": item.dob,
                    "displayDatePicker": true,
                    "format": "yyyy-MM-dd"
                },
                function(dateElem, status, errorMessage){
                    if (status === "SUCCESS") {
                        var body = cmp.get("v.xy");
                        body.push(dateElem);
                        cmp.set("v.xy", body);
                    }
                }
            );          
        }
    }
})

Output:

enter image description here

But this is what I want:

enter image description here

I think this is happening because variable v.xy is of array type and I need to pass index of the element I need to print instead of printing the whole array in each iteration but not sure how to do that. I am new to Lightning so I might be missing some obvious thing?

Note:

  1. This may look like duplicate of this one but I am just trying to implement one of the solution suggested there and not able to acheive it.
  • Can you clarify what you're trying to do? There are ways to get a component's value that don't involve dynamic aura:id elements – Sebastian Kessel Jan 2 '17 at 3:44
  • When user changes the date, I want to fire an event(onchange or onblur) and in that event, I want to find for which contact, user is updating the value. Do you think we can do this in a simple way? Please suggest. – javanoob Jan 2 '17 at 3:47
  • Left an answer for you. May need a little work on your end to get it 100%, but should get you close. – Sebastian Kessel Jan 2 '17 at 4:01
5

Here is a way to do it, that doesn't involve dynamic ids. It is some more code, but it also cleanly componentizes information. The idea is that each cell (or, more precisely, row) becomes its own component and informs its parent (the table) if its own id on the event that is fire upon the change value.

Here is an example

ExampleComponent.cmp

Add

<aura:handler name="click" event="c:CellSelect" action="{!c.handleChangeEvent}" includeFacets="true" />

Change

<aura:iteration items="{!v.conList}" var="item">
    <c:TableCell item="{!item}" />
</aura:iteration>

ExampleComponentController.js:

Add Function

handleChangeEvent : function(component, event, helper) {
    console.log("The Id is: " + event.getParam("recordId"));
}

CellSelect.cmp

<aura:event type="COMPONENT" description="Event template">
    <aura:attribute name="recordId" type="String"/>
</aura:event>

TableCell.Cmp

<aura:component >
    <aura:attribute access="public" name="item" type="Object" />

    <aura:registerEvent name="click" type="c:CellSelect"/>

<tr>
        <td>{!item.sfId}</td>
        <td><ui:inputDate value="{!item.dob}" displayDatePicker="true" format="yyyy-MM-dd" onchange="{!v.changedValue}"  /></td>
    </tr>
</aura:component >

TableCellController.js

changedValue : function(component, event, helper) {
    var rec = component.get("v.item");
    var clickEvent = component.getEvent("click");

    clickEvent.setParams({recordId: rec.sfId});
    clickEvent.fire();
}
|improve this answer|||||
  • There might be a typo or two here, I did not have a chance to compile it. I adjusted it from some code I already had. Let me know if it fails and we can fix those either today or tomorrow. – Sebastian Kessel Jan 2 '17 at 3:58
  • Thanks for your answer! So you think we cannot add components dynamically with unique id for aure:Id attribute inside aura:iteration? – javanoob Jan 2 '17 at 4:06
  • 1
    aura:id is supposed to be a string, not an expression. I suspect that trying to do it dynamically is asking for trouble. Besides, IMHO, the provided solution is better as it compartimentalizes functionality better. – Sebastian Kessel Jan 2 '17 at 4:09
2

Breaking into various components as suggested in the above answer is a great way to achieve what you are trying to do .You do not necessarily need to dynamically create the components .

While the above approach suggested in the answer uses lightning component events for your need ,you can also avoid the event file completely and use the arrays to detect the clicked element without actual Id since index is accessible in the controller

Check the below code

inputDateExample.cmp

<aura:component >
<aura:attribute name="conList"
                type="List"
                default="[{'sfId':'AAAAAAAAAAAAAAA','dob':'1980-01-01'},{'sfId':'BBBBBBBBBBBBBBB','dob':'1990-01-01'}]">
</aura:attribute>
<table>
    <aura:iteration items ="{!v.conList}" indexVar="index" var="item">
        <c:inputDateCmp dateItem="{!item}" index="{!index}"/>
     </aura:iteration>
</table>

The custom inputDateCmp

<aura:component >
 <aura:attribute name="dateItem" type="Date" />
 <aura:attribute name="index" type="Integer" />
<tr>
<td>
    {!v.dateItem.sfId}
    {!v.index}
</td>
<td>
    <ui:inputDate aura:id="date-id" 
                          value="{!v.dateItem.dob}" 
                          displayDatePicker="true"
                          blur="{!c.fldChanged}"/>
 </td>
</tr>

inputDateCmpController.js

({
  fldChanged : function(component, event, helper) {
    console.log('Value changed');
    var dateComponent = component.find('date-id');
    console.log(dateComponent);
    console.log(dateComponent.get("v.value"));
    console.log(component.get("v.index"));
  }
})

enter image description here

|improve this answer|||||
  • The trick, Mo, is that presumably there's more logic in the parent component that needs to follow the change in the date. Events are the way to properly communicate to the parent. Obviously, if the parent doesn't need to find out, an event is overkill. – Sebastian Kessel Jan 2 '17 at 5:47
  • 1
    Ah I see now .So in that scenario there will be need for a component event then .Somehow I missed the part where there was need to use the child results up the parent chain – Mohith Shrivastava Jan 2 '17 at 5:49
  • 1
    It's not explicit. I just wanted to call it out. Honestly, the index is not even needed since the SFDC Id is included in the object. – Sebastian Kessel Jan 2 '17 at 5:51
  • 2
    @SebastianKessel Need for index depends on the usecase where the user has to update the specific row when there's a change in the view and refresh the table after server action. Benefit here is, we can update the specific row(becoz we have the index) in the collection rather than iterating the collection all over again and find the specific record based on the record Id. – Praveen Jan 2 '17 at 6:04
  • This is great discussion. Thanks everyone for your time and looking into this. – javanoob Jan 2 '17 at 8:14

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.