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.

  • Can you extract the value of the map in Javascript and then bind it to wherever you want it? – akcorp2003 Jul 19 '17 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 '17 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 '17 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? – Mohit Arora Jul 26 '17 at 11:06
  • Added a new answer which I'm hoping will at least partly resolve this old and curly question! – Caspar Harmer Sep 10 '18 at 23:42
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]);
          },
      }
  );
}
  • 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 '18 at 12:49
  • Done. Your version is much cleaner. @sfdcfox – Caspar Harmer Sep 11 '18 at 17:58
  • 1
    I was not aware of value providers, this is an interesting solution, accepting this – Patlatus Sep 13 '18 at 15:19
  • 1
    At least this doesn't use Apex – Patlatus Sep 13 '18 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 '18 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.

  • 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 '17 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 '17 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.

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.