[SalesForce] Cannot update lightning component Map attribute

I'm having a problem with setting values during component event handling where the values are sent from a child component, during initialization, to the parent component. The values to be set are in a Map attribute, where the key is a string and the value is a string array.

I initially had a null related exception and already tried adding the 'default="{}"' to my parent component's attribute declaration:

<aura:attribute name="fieldValues"
                type="Map"
                default="{}"/>

This gets me over the first hurdle with a null related exception (which I subsequently found was covered in this question). However, when I try to update the map in the component's controller in an event handler I get a different error. The error is shown later, but first the context.

The handler declaration in the parent component is:

<aura:handler name="selectionChanged" event="c:filterSelectionChange" action="{!c.handleFilterSelectionChange}"/>

The handler code looks like this:

handleFilterSelectionChange: function (component, event, helper) {
    var field = event.getSource().get("v.field").fieldPath; // a value from the child component
    var value = event.getParam("value"); // a value from the event

    var fieldValues = component.get("v.fieldValues");

    fieldValues.set(field, value);

    component.set("v.fieldValues", fieldValues);
}

The "filterSelectionChange" event looks like this:

<aura:event type="COMPONENT">
    <aura:attribute name="value" type="String[]"/>
</aura:event>

This event is fired during the initialization of a child component. The child component includes the following:

<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

<aura:registerEvent name="selectionChanged" type="c:filterSelectionChange"/>

The child's controller's doInit looks like this:

doInit: function (component, event, helper) {
    var selection = helper.initSelection(component); // returns a string array value

    var selectionChangeEvent = component.getEvent("selectionChanged");

    selectionChangeEvent.setParams({
        value: selection
    });

    selectionChangeEvent.fire();
}

The error I get (which prevents the child from rendering) happens on the line:

fieldValues.set(field, value);

in the parent component's "handleFilterSelectionChange" event handler, see above, and is:

Uncaught Action failed: c:filter$controller$handleFilterSelectionChange [fieldValues.set is not a function]
Callback failed: apex://FilterController/ACTION$getFieldSet
throws at https://xxxx.visualforce.com/auraFW/javascript/a24vMs8Ru7Q5OyxXWeHeyg/aura_prod.js:38:15

Note that I am using Lightning-Out to render the component in a visual force page (though I don't believe that is specifically relevant to this issue).

You can also see (from the error's details) that this is triggered from the callback handler for a server-side controller query "getFieldSet". I know that this works as all the correct processing occurs prior to adding the management of this map of values. To be clear, this server-side action is handled thus in the parent component's controller:

doInit: function (component, event, helper) {
    var action = component.get("c.getFieldSet");

    action.setParams({
        objectType: component.get("v.objectType"),
        fieldSetName: component.get("v.fieldSet")
    });

    action.setCallback(this, function (response) {
        var state = response.getState();

        if (state === "SUCCESS") {
            component.set("v.fields", response.getReturnValue());
        } else {
            ...
        }
    });

    $A.enqueueAction(action);
}

Setting the "v.fields" attribute on the component triggers the rendering of the parent, which in turn creates and renders the children.

Do you know what I'm doing wrong? Is this a limitation due to triggering this flow in child component initialization? Is it linked to Lightning-Out or the handling in a callback for a server-side action (I doubt it)? Or have I used the wrong approach for managing the parent component's "fieldValues" map?

Any suggestions gratefully received.

Best Answer

There is no .set() method on a Javascript object. Although you have called it a Map when you wrote this:

<aura:attribute name="fieldValues"
                type="Map"
                default="{}"/>

Lightning pretty much ignores the value in the type there. In most cases, it has no effect. So, the type of the default value is what you actually get in your JS, which is making an JS Object, not a Map.

So, you should write:

fieldValues[field] = value;
Related Topic