1

I'm building a small component which displays information based on a currently selected object called metric.

Metric Card Component

    <aura:component implements="forceCommunity:availableForAllPageTypes" access="global">
        <aura:attribute name="metricLabelList" type="String[]" default="[]"/>
        <aura:attribute name="metric" type="SObject"/>
        <aura:attribute name="metricObjects" type="SObject[]"/>
        <aura:attribute name="selectedMetric" type="String" default="Clicks"/>
        <aura:handler name="change" value="{!v.metricObjects}" action="{!c.onMetricObjectsChanged}"/>

        <div class="wsm_metric-card wsm_box-shadow">
            <lightning:buttonMenu aura:id="menu" onselect="{! c.selectOption }" class="wsm_select" label="{!v.selectedMetric}" variant="bare-inverse" alternativeText="Show menu">
                <aura:iteration items="{!v.metricLabelList}" var="metricOpt">
                    <lightning:menuItem value="{!metricOpt}" label="{!metricOpt}" />
                </aura:iteration>
            </lightning:buttonMenu>                     
            <div class="current-value slds-m-vertical_x-small">
                <span class="metric-value">{!v.metric.current}</span>
            </div>

            <div class="past-value positive">
                <span></span>
                <span class="value-delta">{!v.metric.previous}</span><span class="percentage-delta"> (-6.72%)</span>
            </div>
        </div>
    </aura:component>

I'm passing the attributes value from a parent component like this:

<c:Metric_Card metricLabelList="{!v.metricLabels}" metric="{!v.metrics[0]}" metricObjects="{!v.metrics}"></c:Metric_Card>

The idea is that the parent component makes a request, fetches the data and then just passes the values to the Metric Card so it displays the information.

In the MC component I have a button which I use to select a different value, look for that value in an array of objects and change the displayed information, however, when i change values, the array of objects metricObjects is being modified without any instruction to do so.

This is the method I'm using when changing values in the button:

Metric Card Helper

selectOption: function(component, event) {
        var selectedItem = event.getParam("value");
        component.set("v.selectedMetric", selectedItem);
        var metric = component.get("v.metricObjects").find(element => {
            return element.label === selectedItem;
        });
        component.set("v.metric", metric);
    }

What I get is the following:

After loading the component the first time, my array is correctly defined:

enter image description here

But after selecting a different value, the same array is modified and now shoes duplicate values or even undefined values (see the "Impressions" object twice now):

enter image description here

QUESTION: Why is the metricObjects array being modified after I select a different value from by button menu, since I'm only directly setting the value of the metric object?

2
  • This is a well written post with a lot of useful information about the problem you are facing. However, it does not contain a clear question. Could you please take a moment to edit it's content to just clearly and concisely summarize what you are asking?
    – Adrian Larson
    Feb 18, 2020 at 19:53
  • 1
    @AdrianLarson Will add that explicitly at the end of the post in a minute
    – IvanS95
    Feb 18, 2020 at 19:55

1 Answer 1

3

{!...} is a two-way binding. This means that any changes you make to the attribute from the parent will appear to the child, and any changes in the child should also affect the parent. I say "should" because there are some bugs regarding when multiple children share the same parent attribute, not all of them may see the change. Regardless, this is at least expected behavior for the situation at hand.

You can probably use the one-way binding technique if you'd like ({#...}), but you'll then need to notify the parent of any changes. If that doesn't work, use an internal attribute to copy the data to, and then you can adjust the values that way:

<!-- we're in the child component -->
<aura:attribute access="public" type="Metric__c" name="metric" />
<aura:handler name="change" value="{!v.metric}" action="{!c.updateMetric}" />
<aura:attribute access="private" type="Metric__c" name="metricCopy" />

...

updateMetric: function(c, e, h) {
  c.set("v.metricCopy", c.get("v.metric")); // Or whatever else you need to do
}
5
  • Noted, however in my case I'm not seeing why that would be an issue, since I'm updating the metric variable in the child component, and in that same component the metricObject array is the one getting modified as well
    – IvanS95
    Feb 18, 2020 at 19:52
  • 1
    @IvanS95 Correct. You passed in an index in to an array, so when you updated the attribute on the child side, the index in that array also got modified.
    – sfdcfox
    Feb 18, 2020 at 20:00
  • So you are saying that the value stored in metric during component intialization ({!v.metrics[0]}) is a reference to that object in the metricsObject array?
    – IvanS95
    Feb 18, 2020 at 20:03
  • @IvanS95 Yes, that's correct.
    – sfdcfox
    Feb 18, 2020 at 20:10
  • I see, alright so I'll have to find a different way to set an initial metric loaded in each metric card, thank you!
    – IvanS95
    Feb 18, 2020 at 20:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .