25

How can expression functions similar to Visualforce and Lightning Aura Components be achieved within lightning web components?

For example, <template if:true={index % 5 == 0}><br></template> expression index % 5 == 0 is not compyling at all.

accountList.html

<template>
    <template if:true={accounts.data}>
        <template for:each={accounts.data} for:item='item' for:index='index'>
            <!--LWC DOES NOT SUPPORT EXPRESSION FUNCTIONS-->
            <!--<template if:true={index % 5 == 0}><br></template>-->
            {item}
        </template>
    </template>
</template>

accountList.js

import {LightningElement, wire} from 'lwc';
import getAccounts from '@salesforce/apex/TestController.getAccounts';

export default class AccountList extends LightningElement {

    @wire(getAccounts) accounts;

}

Since LWC doesn't support Expression Functions, I'd like to know how to implement them.

16

There are multiple options. First, note, that all computations are in JavaScript, not in the markup.

Option 1 You implement the display of your {item} (which is currently only the JSON object), into a dedicated component AccountListItem. And implement in there the logic that @POZ suggested.

Something along the lines of:

import {LightningElement, api} from 'lwc';

export default class AccountListItem extends LightningElement {
   @api index;
   @api item;

   get isMod5() {
     return this.index % 5 == 0;
   }
}

Option 2 You use a wired function instead of a wired property. And then do some fun stuff with a changed data model. Although this is hacky.

import {LightningElement, wire, track} from 'lwc';
import getAccounts from '@salesforce/apex/TestController.getAccounts';

export default class AccountList extends LightningElement {

    @wire(getAccounts)
    wiredAccounts({ error, data}) {
       if (error) {
         // do error handling, please
       } else if (data) {
         this.accounts = data.map((acct, index) => {
               return {
                  acct: acct,
                  isMod5: index % 5 == 0 ? true : false
               }
           })
       }
    }
11

EDIT: changed my answer because previous one was wrong:

You have to write a JS expression that paginates your list like this:

get getContacts() {
  return [
    { id: 0, items: ['a', 'b', 'c', 'd', 'e']}, // Page 0, max 5 item
    { id: 1, items: ['f', 'g']} // Page 1
  ];
}

Then use two iterations in your template:

<template for:each={getContacts} for:item="page">
  <div key={page.id}>
    <template for:each={page.items} for:item="contact">
      {contact}
    </template>
  </div>
</template>
  • 2
    How does index get passed to this function? – Jayant Das Dec 21 '18 at 17:26
  • Thanks for pointing this out @JayantDas, I've edited my answer. – POZ Dec 21 '18 at 17:41
  • This doesn't work, as the first element inside a for:each must have a 'key' attribute. This makes rendering lists of lists quite tricky in LWC – Rob Baillie Aug 21 at 12:20
  • Yes, you're right Rob, I've updated my answer to include a page id (a simple index) that acts as a key for the first level iteration. – POZ Aug 23 at 7:27
5

Because expressions are not allowed in Lightning Web Component template, you would have required a JavaScript getter function here. Refer to HTML Template Directives documentation for details.

Excerpt below from documentation:

if:true|false={expression}

..... (some text omitted)

The engine doesn’t allow computed expressions. To compute the value of expression, use a getter in the JavaScript class.

But, in your case you need to pass the index var in the iteration, so even having a getter function doesn't seem to do the job here.


I am not completely sure of the use case here as why you want to utilize the index within the iteration, but as I don't see a way to set the index to a JS function, here's an approach that you can take by utilizing Composition.

Even though this approach solves the problem here, but still seems to be an overhead of creating another component to achieve this what could have been achieved inline.

The approach here is:

  • Create a custom LWC
  • Pass the index value to the component within the iteration in the parent component
  • Compute the value of the index within that component and perform your required operation

Let's say I create a custom LWC as c-my-index-component, which looks like as below:

<template>
    <template if:true={indexVar}>
        // do something
    </template>
</template>

In the JS, I have this property defined as:

import { LightningElement, api, track } from 'lwc';
export default class MyCustomIndex extends LightningElement {
    @track myVariable;

    set indexVar(value) {
        this.myVariable = value;
    }

    @api
    get indexVar() {
        return (this.myVariable % 5 === 0)
    }
}

And then finally I use it in my existing component in iteration as below:

<template for:each={accounts.data} for:item='item' for:index='index'>
    <c-my-index-component key={item.Id} index-var={index}></c-my-index-component>
     // my other code
</template>

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.