[SalesForce] Lightning Components – Run an action on a selected list

I've been hitting a wall with this for so long that I'm guessing it cannot be done. I've got a lightning component that is showing a list of objects from a custom class. The 3 relevant scripts are:

**InvoiceController.apxc**
public class InvoiceController {

// <Name> getmyOpportunities </Name>
// <summary> Generates a list of items to invoice from Opportunities and Billing Events </summary>
// <returns type="List<tempInvoice>"> A List of custom Invoices </returns>
@AuraEnabled
public Static List<temp_Invoice> getmyOpportunities() {

    List<temp_Invoice> myOpportunities = new List<temp_Invoice>();

    List<Opportunity> myInvoiceOpportunities = [SELECT PO_Number__c, CloseDate, Name, Owner.Name, CurrencyIsoCode, Account.Xero_Contact_ID__c, Account.CurrencyIsoCode, Account.Name, Account.Owner.Name, (SELECT Quantity, UnitPrice, ListPrice, Discount, ProductCode, Manufacturer_Cost_Price__c, Manufacturer_Currency__c ,product2.Manufacturer__r.Xero_Contact_ID__c, product2.Manufacturer__r.Name, PriceBookEntry.UnitPrice, PricebookEntry.Name, Description, PricebookEntry.product2.Family FROM OpportunityLineItems), (SELECT Id FROM Commissions__r WHERE Invoice__c = null LIMIT 1) FROM Opportunity WHERE Id not in (SELECT Opportunity__c FROM Invoice__c) AND StageName = 'Closed Won' AND Project_Type__c = 'Software' ORDER BY CloseDate DESC];

    system.debug('myInvoiceOpportunities: ' + myInvoiceOpportunities);

    for(Opportunity Op : myInvoiceOpportunities) {

        temp_Invoice myInvoiceOpportunity = new temp_Invoice();
        myInvoiceOpportunity.OpportunityDetails = Op;
        myInvoiceOpportunity.ToInvoice = false;
        myInvoiceOpportunity.LineType = 'Opportunity';
        myInvoiceOpportunity.Id = Op.Id;
        myInvoiceOpportunity.Name = Op.Name;
        myInvoiceOpportunity.Account = Op.Account.Name;
        myInvoiceOpportunity.ClosedDate = ((Datetime) Op.CloseDate).format('dd/MM/YYYY');
        myInvoiceOpportunity.PO_Number = Op.PO_Number__c;
        myInvoiceOpportunity.OppOwner = Op.Owner.Name;
        myInvoiceOpportunity.AccOwner = Op.Account.Owner.Name;
        myOpportunities.add(myInvoiceOpportunity);

    }

    return myOpportunities;

}

// <Name> InvoiceOpportunity </Name>
// <summary> A custom Object for storing the values needed to create an Invoice in Xero </summary>

public class temp_Invoice{

    @AuraEnabled
    public Boolean ToInvoice{get; set;}
    @AuraEnabled
    public String LineType{get; set;}
    @AuraEnabled
    public Opportunity OpportunityDetails{get; set;}
    @AuraEnabled
    public pse__Billing_Event__c ProjectDetails{get; set;}

    @AuraEnabled
    public String Id{get; set;}
    @AuraEnabled
    public String Name{get; set;}
    @AuraEnabled
    public String Account{get; set;}
    @AuraEnabled
    public String ClosedDate{get; set;}
    @AuraEnabled 
    public String PO_Number{get; set;}
    @AuraEnabled
    public String OppOwner{get; set;}
    @AuraEnabled
    public String AccOwner{get; set;}
    @AuraEnabled
    public Boolean XeroId{get; set;}

    // <Name> tempInvoice </Name>
    // <summary> Constructor for the tempInvoice Object </summary>
    public temp_Invoice() {

        this.ToInvoice = false;
        this.LineType = 'Unknown';
        this.Id = 'Unknown';
        this.Name = 'Unknown';
        this.Account = 'Unknown';
        this.ClosedDate = 'Unknown';
        this.PO_Number = 'Unknown';
        this.OppOwner = 'Unknown';
        this.AccOwner = 'Unknown';
        this.XeroId= false;

        this.OpportunityDetails = new Opportunity();
        this.ProjectDetails = new pse__Billing_Event__c();

    }

}

}

**Invoice.cmp**
<aura:component controller="InvoiceController" implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >
<aura:attribute name="invoices" type="InvoiceController.temp_Invoice[]" />
<aura:handler name="init" action="{!c.doInit}" value="{!this}" />   
<table class="slds-table slds-table--bordered slds-max-medium-table--stacked-horizontal">
    <thead>
        <tr class="slds-text-heading--label">
            <th class="slds-cell-shrink">
                    Select
            </th>
            <th class="slds-is-sortable" scope="col">
                <div class="slds-truncate">Opportunity / Project
                    <button class="slds-button slds-button--icon-bare">
                        <span class="slds-assistive-text">Sort</span>
                    </button>
                </div>
            </th>
            <th class="slds-is-sortable" scope="col">
                <div class="slds-truncate">Account
                    <button class="slds-button slds-button--icon-bare">
                        <span class="slds-assistive-text">Sort</span>
                    </button>
                </div>
            </th>
            <th class="slds-is-sortable" scope="col">
                <div class="slds-truncate">Closed
                    <button class="slds-button slds-button--icon-bare">
                        <span class="slds-assistive-text">Sort</span>
                    </button>
                </div>
            </th>
            <th class="slds-is-sortable" scope="col">
                <div class="slds-truncate">PO Number
                    <button class="slds-button slds-button--icon-bare">
                        <span class="slds-assistive-text">Sort</span>
                    </button>
                </div>
            </th>
            <th class="slds-is-sortable" scope="col">
                <div class="slds-truncate">Opportunity Owner
                    <button class="slds-button slds-button--icon-bare">
                        <span class="slds-assistive-text">Sort</span>
                    </button>
                </div>
            </th>
            <th class="slds-is-sortable" scope="col">
                <div class="slds-truncate">Account Owner
                    <button class="slds-button slds-button--icon-bare">
                        <span class="slds-assistive-text">Sort</span>
                    </button>
                </div>
            </th>
        </tr>
    </thead>
    <tbody>
        <aura:iteration items="{!v.invoices}" var="invoice">
            <tr class="slds-hint-parent">
                <td class="slds-cell-shrink" data-label="Select Row">
                    <label class="slds-checkbox">
                        <ui:inputCheckbox aura:id="checkbox" value="{!invoice.ToInvoice}"/>
                        <span class="slds-checkbox--faux"></span>
                        <span class="slds-assistive-text">Select Row</span>
                    </label>
                </td>
                <th class="slds-truncate" scope="row" data-label="Opportunity Name">{!invoice.Name}</th>
                <td class="slds-truncate" data-label="Account Name">{!invoice.Account}</td>
                <td class="" data-label="Close Date">{!invoice.ClosedDate}</td>
                <td class="" data-label="PO Number">{!invoice.PO_Number}</td>
                <td class="" data-label="Opportunity Owner">{!invoice.OppOwner}</td>
                <td class="" data-label="Account Owner">{!invoice.AccOwner}</td>
            </tr>
        </aura:iteration>
    </tbody>
</table>
<ui:button aura:id="button" buttonTitle="Click" class="button" label="Click me" press="{!c.getInput}"/>
<ui:outputText aura:id="outName" value="" class="text"/>
</aura:component>

({
doInit : function(component, event, helper) {
    var action = component.get("c.getmyOpportunities");
    action.setCallback(this, function(data) {
        component.set("v.invoices", data.getReturnValue());
    });
    $A.enqueueAction(action);
},
getInput : function(cmp, evt, helper) {
    var myobjects = cmp.find("checkbox");
    var allObjects = cmp.get("v.invoices");
    console.log(allObjects);        
    var myText = cmp.find("outName");
    var greet = "Hi, " + myobjects[0].get("v.value");
    myText.set("v.value", greet);
}
})

This is working in that I've got access to the array of check boxes (so I know which ones have been selected), and I've got the original array of objects (which I'll need to do my processing) but I cannot work out how to determine which checkbox relates to the which object.

I've done almost the same thing using Visualforce and ticking the checkbox changes the underlying data so it works. So I guess my problem is not being able to either update the underlying data, or not being able to pass the data via the checkbox?

My end goal is to have a list of items that I can select and when I click the button I perform an action only on the selected items, however I would be happy if I had a button on each line and clicking that ran the action only on that line.

Best Answer

@Charles T - You were right it was a Salesforce bug and was fixed with the update :)

Thank you