4

I am creating this Lightning component:

<aura:component>
  <aura:attribute name="record" type="Account" />
  <aura:attribute name="recordList" type="Account[]" />
   ...
</aura:component>

..dynamically using...

 $A.createComponent(
    "c:myComponent",
    {
        "record": cmp.getReference("v.account"),
        "recordList": cmp.getReference("v.accountList")
    },
);

I am perfectly able to access the single record but unable to access the record list.

myMethod: function(cmp) {
   cmp.get("v.record").Name = "Acme";  // Works

   for(var record of cmp.get("v.recordList")) {
      record.Name = "Acme";  // Fails
   }
}

Accessing the recordList fails with:

Uncaught Action failed: myComponent$controller$myMethod [Cannot add property Name, object is not extensible]

When I console.log both attributes v.record is a Map representing an SObject but the recordList is an obfuscated "thing"

Proxy {0: XI, 1: XI, 2: XI, 3: XI, 4: XI, 5: XI, 6: XI, 7: XI, 8: XI, 9: XI, length: 10}
[[Handler]]
:
Object
[[Target]]
:
Array(10)
0
:
XI {path: Array(3), Ja: "v.recordList.a0246000005mfQzAAI", Kd: false, Ga: a, pb: a, …}

Strangely we could make it work by temporarily assigning/casting it to another attribute of type Account.

for(var record of cmp.get("v.recordList")) {
    cmp.set("v.tempRecord", record);

    var temp = cmp.get("v.tempRecord");
    temp[field] = number;
    cmp.set("v.tempRecord", temp);

    cmp.clearReference("v.tempRecord");
}

Why that? A bug? By design?

  • 2
    Interesting question, have you tried treating the recordList like an array? e.g. iterate over it by doing cmp.get("v.recordList").forEach(function(record){record.Name = "Acme";}) for...of treats it as an iterable, rather than an array, and could possible be the source of the odd results. – Aidan Dec 8 '17 at 11:53
  • Using forEach compared to for of didn't change a thing. – Robert Sösemann Dec 8 '17 at 12:37
  • 1
    I think this is LockerService related based on the error but not sure why it's locked down. Try using Account[] as the recordList type instead of List. I've had better luck with arrays than lists in the Lightning framework. – Luke Freeland Dec 8 '17 at 21:27
  • According to developer.salesforce.com/docs/atlas.en-us.lightning.meta/… LockerService seems to obfuscate any HTMLCollection type to an obfuscated Proxy. But why and how do I then access collections? – Robert Sösemann Dec 9 '17 at 21:38
  • @RobertSösemann What release did you test this on? I tried to reproduce the issue on Winter 18 and the upcoming Spring 18, the assignment inside the for loop works fine. Will post my repro steps as an answer because there is limited space here. – Ravi Jayaramappa Dec 10 '17 at 20:12
1

Seems to work fine on Winter 18 and Spring 18(upcoming) releases. Here's the code I used to repro the issue.

Apex Controller

public with sharing class AccountController {
    @AuraEnabled
    public static List<Account> findAll() {
    return [SELECT id, name
            FROM Account
            LIMIT 50];
    }
}

App code

<aura:application controller="AccountController">
    <aura:attribute name="accounts" type="Account[]"/>
    <aura:attribute name="account" type="Account" />
     App
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <c:containerCmp account="{!v.account}" accountList="{!v.accounts}"/>
</aura:application>

App Controller

({
    doInit: function(component, event) {
        var action = component.get("c.findAll");
        action.setCallback(this, function(a) {
            component.set("v.accounts", a.getReturnValue());
            component.set("v.account", a.getReturnValue()[0]);
        });
        $A.enqueueAction(action);
    }
})

c:containerCmp

<aura:component>
  <aura:attribute name="account" type="Account" />
  <aura:attribute name="accountList" type="Account[]" />
  <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    container cmp
    {!v.body}
</aura:component>

c:containerCmp controller

({
    doInit : function(cmp) {
        $A.createComponent(
            "c:myComponent",
            {
                "record": cmp.getReference("v.account"),
                "recordList": cmp.getReference("v.accountList")
            },
            function(newInnerCmp, status, errorMessage) {
                if (status === "SUCCESS") {
                    var body = cmp.get("v.body");
                    body.push(newInnerCmp);
                    cmp.set("v.body", body);
                }
            }
        );
    }
})

c:myComponent

<aura:component>
  <aura:attribute name="record" type="Account" />
  <aura:attribute name="recordList" type="Account[]" />
  <ui:button press="{!c.clickHandler}" label="Show account details"/>
  <br/>
  <aura:iteration items="{!v.recordList}" var="recordd">
      {!recordd.Id} : {!recordd.Name} <br/>
  </aura:iteration>
</aura:component>

c:myComponent controller

({
    clickHandler : function(cmp, event, helper) {
        cmp.get("v.record").Name = "Acme";  // Works
        for(var record of cmp.get("v.recordList")) {
            record.Name = "Acme";  // Suppose to fail here, but works fine
        }
        // Optional step to reflect the mutation in the UI
        // cmp.set("v.recordList", cmp.get("v.recordList"));
    }
})
  • @RobertSösemann Let me know what you did differently compared to the repro posted above. – Ravi Jayaramappa Dec 10 '17 at 20:21
  • The only obvious difference is that instead of passing an Account[] we pass a Map<Id, CustomObject__c> and populate recordList by initializing it as [] and pushing items from recodsMap[recordId] – Robert Sösemann Dec 11 '17 at 0:13
  • Ravi, please see my "workaround" answer below. I got help by Kris from the Aura team and there seems to be a bug. He is going to file it and said there is a slight chance there is already a fix delivered in Spring'18. In case you discuss and assess this issue again internally I would love to see an aditional answer or comments by you. – Robert Sösemann Dec 11 '17 at 8:56
1

An incredibly helpful developer of the Aura Platform team did a Hangout session with me to debug this and confirmed that this is either an open bug or something that will be fixed in Spring'18.

For the time being this way to access the reference works as a workaround

myMethod: function(cmp) {
   ...

   for(var i=0; i<records.length; i++) {
        var record = cmp.get("v.records."+i);
        record['Name'] = "Acme";
        cmp.set("v.records." + i, record);
    }
}

So the important thing is not the loop type or Aura type of the attribute. It's the way how I get the reference by cmp.get("v.records." + i) out of the array and how I set it using cmp.set("v.records." + i, record).

Note: My code was way more complex than the samples I gave here. Instead of and Account array it was a Map. Instead of accessing the Name field it had to work for generic fields and other similar nifty complexities

  • great spot, seems like cmp.get() is doing the magic casting refrerences into real records. – Basti Dec 11 '17 at 10:20

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.