16

In VisualForce we could access dynamically to map elements using syntax

{!map[mapIndex]}

In Lightning if we try to define a map and use this syntax we would receive an error saying

Failed to save undefined: expecting a positive integer, found 'mapIndex' at column 9 of expression: (v.map[mapIndex]) : Source

Which means that Lightning doesn't understand how to process maps and objects dynamically in Javascript. However, in pure javascript it is not a problem to access map or object member dynamically.

Is there any way to avoid this restriction?

Update: I am looking for some solution which wouldn't require javascript. I am looking for solution which would use only native syntax of Lightning Component.

5
  • Can you extract the value of the map in Javascript and then bind it to wherever you want it?
    – akcorp2003
    Jul 19, 2017 at 15:23
  • I had already suggestions to use javascript, but I am looking for some solution which wouldn't require javascript.
    – Patlatus
    Jul 20, 2017 at 7:07
  • +1. I feel it is a major drawback of lightning framework that we cannot iterate a map collection without the help of Javascript. I ran into this more than once and it is painful.
    – javanoob
    Jul 25, 2017 at 12:34
  • I am afraid that you cannot reference the elements in a map by providing dynamic indexes. Although, you can do it by hardcoding the indexes. Have you tried it using wrapper class? Jul 26, 2017 at 11:06
  • Added a new answer which I'm hoping will at least partly resolve this old and curly question! Sep 10, 2018 at 23:42

3 Answers 3

4

Ok, I think I have got the closest solution so far.

What you can do is create a custom value provider, which, whilst it means you need to define some Javascript to process a Map, you only need to do this once and can pass any Map to it to iterate.

It's not the same as the old Apex version, but it works and is probably more customizable (for example, you could output both keys and values together)

Component code:

<aura:application extends="force:slds" >
  <aura:attribute name="providersAdded" default="false" type="Boolean" />
  <aura:handler name="init" value="{!this}" action="{!c.init}" />
  <aura:attribute name="mymap" type="Map" default="{'name':'France','capital':'Paris'}"/>    

  <aura:if isTrue="{!v.providersAdded}">
  <!--Note the use of the special value provider that allows Map iteration-->
  <aura:iteration items="{!i.mymap}" var="value">
      <div >{!value}</div>
      <!--France-->
      <!--Paris-->
  </aura:iteration>
  </aura:if>
</aura:application>

Controller.js:

init: function(component, event, helper) {
  component.addValueProvider(
      'i',
      {
          get: function(key, comp) {
              let localMap = component.get("v."+key); 
              return Object.keys(localMap).map(key => localMap[key]);
          },
      }
  );
}
7
  • 1
    let localMap = component.get("v."+key); return Object.keys(localMap).map(key => localMap[key]); would be more JavaScript-y, but I really like this solution.
    – sfdcfox
    Sep 11, 2018 at 12:49
  • Done. Your version is much cleaner. @sfdcfox Sep 11, 2018 at 17:58
  • 1
    I was not aware of value providers, this is an interesting solution, accepting this
    – Patlatus
    Sep 13, 2018 at 15:19
  • 1
    At least this doesn't use Apex
    – Patlatus
    Sep 13, 2018 at 15:24
  • but why cannot we collapse the callback to one liner like this (key,comp) => Object.values( component.get("v."+key) );
    – Patlatus
    Sep 13, 2018 at 15:27
-3

Found a way out to iterate over map in lightning component. It can be done using another lightning custom component which is pretty simple to write and use.

A simple example here to demonstrate

Server-side controller

public class ReturnMapClass {

        @AuraEnabled 
        public static Map<String,String> fetchMapValues(){

                return new Map<String,String> {'one' => 'Test value 1','two' => 'Test value 2'};
        }
}

showValues.cmp for showing map values

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >

    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <aura:attribute name="mapInstance" type="Map" />
    <aura:attribute name="key" type="String"/>
    <aura:attribute name="dataValue" type="String"/>

    {!v.dataValue}

</aura:component>

showValuesController.js

({
    doInit : function(component, event, helper) {

        var objKey = component.get("v.key");        
        var objMap = component.get("v.mapInstance");           

        component.set('v.dataValue', objMap[objKey]);        
    }
})

mapComponent.cmp which contains the map to iterate and calls ShowValues.cmp to achieve it

<aura:component controller="ReturnMapClass"
                implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >

    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <aura:attribute name="mapInstance" type="Map" default="{'name':'France','capital':'Paris'}"/>    
    <aura:attribute name="mapKeys" type="String[]" />

    <aura:if isTrue="{!!v.mapKeys.empty}">
        <aura:iteration items="{!v.mapKeys}" var="item">
            <c:ShowValues mapInstance="{!v.mapInstance}" key="{!item}" /><br/>
        </aura:iteration>
    </aura:if>

</aura:component>

mapComponentController.js

({
    doInit : function(component, event, helper) {

        helper.fetchMapFromController(component);
    }
})

mapComponentHelper.js

({
    fetchMapFromController : function(component) {      

        var action = component.get("c.fetchMapValues");
        action.setCallback(this, function(response){

            var responseMap = response.getReturnValue();
            component.set("v.mapInstance",responseMap); 

            var arrKeys = [];
            for(var key in responseMap){

                arrKeys.push(key);
            }

            component.set("v.mapKeys",arrKeys);            
        });
        $A.enqueueAction(action);
    }
})

Place your component into the LEX view of any page/record or in communities. Note that the code can still be optimised and enhanced to avoid exceptions.

2
  • It is mentioned in question that I was not looking solution which require javascript, I was looking for native Lightning Component syntax like that which is available for Visualforce
    – Patlatus
    Oct 14, 2017 at 12:06
  • Got that. Could not find any native solution to it. We can possibly delete this answer as it does not serves the purpose of thread.
    – Sikha Baid
    Oct 14, 2017 at 15:30
-3

You could use an aura:iteration and set the start and end indexes to what you want, or if you're just looking to know what the index is,, just use it's provided indexVar attribute. Hard to know what you are trying to accomplish.

You must log in to answer this question.

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