Salesforce Asked by Oleh Berehovskyi on November 19, 2021
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.
I might get fair bit of criticism for writing this, but here it goes
I remember the days when I used to go through a lot of expressions in Visualforce Pages, and it is REALLY hard to read/debug such inline expressions.
I feel it is quite helpful as LWC forces us to separate logic from HTML by using getters:
get someCondition() { return this.someVar == 5; }
It also helps the framework to get rid of parsing completely different syntax, implementing/maintaining code to keep it working.
By utilizing the getters we get to use Javascript to apply any sort of operations before evaluating the condition output.
BUT
There could be situations/implementations where you might end up writing a lot of getters to get the work done. That's where you'll want to write inline expressions as it adds the ease while implementing.
below example illustrates how I implemented this with also utilizing JS, but this does involve some performance kicks as well:
<c-lwc-if condition="(this.a == this.b) && (this.c == 6 || this.b == 'easypeasy')" scope={scope}>
<div slot="if">
Render some HTML
</div>
<div slot="else">
Render some other HTML
</div>
</c-lwc-if>
The condition property is actually JavaScript code but is limited to access the passed scope.
I don't really use the one mentioned above & lean towards the getters for implementing expressions-like use cases
One of the major use cases involved rendering different components based on a condition, which would require code something like:
<template>
<template if:true={condition1}>
component 1
</template>
<template if:true={condition2}>
component 2
</template>
<template if:true={condition3}>
component 3
</template>
... and so on
</template>
Which within Aura and LWC would be easily implemented using expressions and one would not need to write multiple boolean properties/getters to get the job done.
You could also use multiple component templates to implement a solution for this, but wouldn't that require a new implementation for each time you had such requirement.
So I took some inspiration from classic switch-case control flow statements in varous languages and implemented something :
markup:
<c-lwc-switch expression="this.a" scope={scope}>
<c-lwc-case value="4">
<div>Print Something</div>
</c-lwc-case>
<c-lwc-case value="5">
<div>Print Something else</div>
</c-lwc-case>
<c-lwc-case value="'test'">
<div>Print test</div>
</c-lwc-case>
<c-lwc-case default>
<div>Print Default</div>
</c-lwc-case>
</c-lwc-switch>
js:
get scope(){
return {
a : this.b
}
}
Above solution is reusable and solves the problem declarative and helps us with lack of expressions without actually using expressions.
The switch-case reusable solution helps implement the mentioned use case quickly and reduces my efforts, though it still might require some security checks.
here's the implementation: https://github.com/prashantk0001/lwc-if-expressions https://github.com/prashantk0001/lwc-switch-expressions
Answered by Prashant Kashyap on November 19, 2021
You can also achieve this via CSS:
div:not(:first-of-type) {
display: none;
}
Answered by Peter Zheng on November 19, 2021
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
}
})
}
}
Answered by muenzpraeger on November 19, 2021
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>
Answered by POZ on November 19, 2021
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:
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>
Answered by Jayant Das on November 19, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP