Showing posts with label lwc. Show all posts
Showing posts with label lwc. Show all posts

Sunday, 14 May 2023

Light DOM Scoped Slots in Summer 23

Introduction

It's that time again - another Salesforce release is a few weeks away, the preview release notes are out, and preview scratch orgs can be spun up to try out some of the new features. There's a few changes around Lightning Components in the upcoming release, and the first one that caught my eye was the concept of scoped slots, made possible by the general availability of the light DOM.

The light DOM is what we used to call the DOM before web components came along - the standard Document Object Model representing the structure of a web page that is visible and accessible to any JavaScript that cares to look. The light aspect comes from the fact that web components by default use shadow DOM - a hidden DOM structure attached to the regular DOM that only the component can see. 

Scoped slots are an inversion of the usual slot behaviour seen with Lightning Web Components. The standard case allows a parent component to pass markup into a child component to render in specific locations. Scoped slots turns this on its head and allows child components to pass information up to parent components that can be rendered in a specific slot. All of which sounds very cool, but at first glance it seemed a solution looking for a problem. The example in the release notes didn't do much to change this view, as they showed a child iterating a list and the parent rendering the contents of the list item. Given that the parent can iterate the list just as easily as the child, I couldn't see the value that was added.

One thing I've found with Lightning Web Components is a lot of the new features have equivalents in another JavaScript framework - Vue.js seems to be the prime candidate at the moment, and sure enough there's the concept of scoped slots there. Unfortunately the various blogs that I read on this topic didn't help my understanding enormously - when you aren't familiar with the framework then the examples aren't always helpful. What I did take away from this is the value in scoped slots is to separate the generation of the data from the rendering, so that it is available for re-use. The release notes example didn't really provide this as the list iteration is the same regardless of whether you do it in the parent or child, so I needed to figure out another use case.

The Sample

Simple list iteration is easy, but what about the case where I have a JavaScript object created from an Apex Map and want to iterate that? I can't do this in markup, instead I have to create a list of properties from the object. This feels like a situation where a child component that can handle any conversion and iteration required and simply send the properties back to the parent. 

My component that handles this is called mapIterator, and when it receives an object via an @api property, it creates a list from it:

set objMap(value) {
    this._objMap=value;
    if (this._objMap) {
        this.values=[];
        for (let key in this._objMap) {
            this.values.push(this._objMap[key]);
        }
    }
}

and the HTML markup simply iterates this and makes each entry available to the parent via the lwc:slot-bind directive, remembering to render in light DOM mode:

<template lwc:render-mode="light"> 
    <template for:each={values} for:item="value">
        <slot key={value} lwc:slot-bind={value}></slot>
    </template>
</template>

I have several parent components that make use of this - one to generate a simple list of accounts, one to generate a list of account cards, and one to generate a list of opportunity cards. The mapIterator provides the conversion and iteration of the object properties to each of this with no changes required. You can find all of these at the Github repository, but here's the markup from the simple list :

<c-map-iterator obj-map={accountsMap}>
    <template lwc:slot-data="value">
        <div class="slds-p-left_small slds-p-bottom_xx-small">
            <strong>ID:</strong> {value.Id}
        </div>
        <div class="slds-p-left_large slds-p-bottom_small">
            <strong>Name:</strong>{value.Name}
        </div>
    </template>
</c-map-iterator>

Access to the element from the iterator is provided by the lwc:slot-data directive, and as you can see the parent handles all the presentation side of things.

Conclusion

There is definite value here in separating the conversion/iteration from the rendering of the content. That said, I think you might run into issues if you are rendering the iterated elements using markup that must be a direct descendant of a containing element. In that case you won't be able to separate everything, as you get the child element markup in the way. I think there you'd likely need specialisations of the iterator that also renders the container, which won't feel quite as clean.

More Information




Sunday, 30 April 2023

Reactive Flow Components in Spring 23


Introduction

A typical scenarios where I favour lightning pages over flows is where a user's decisions around field values need to have a major impact on the rest of the elements being displayed, and where a wizard interface would slow the user down too much. A great example of this is our BrightMEDIA order booking page. As the user inputs information about the type of advert, where it needs to appear, what size it is and when it appears, the price is dynamically recalculated and displayed. The user typically has the customer on the phone, and there may be a few cycles of tweaking the details to hit a price point - what happens if we make it a bit smaller, move it to another section etc. Paging back and forward through the wizard wouldn't be a great experience, along with the need to keep saving the order as navigation between pages takes place. 

With the Spring 23 release of Salesforce, it looks like in future flow will become a candidate for this kind of user interface with the advent of the Reactive Flow Components beta. Simply put, you can wire the input of one component up to the output of another component, and react when changes occur. 

As this is a beta, you'll have to opt-in to it for any org where you want to use reactive components by checking the box in the Automation Settings 

The Sample

A lot of the demos around reactive anything tends to be a table of accounts that updates when the user inputs the desired number of entries. That's exactly what I started with to get a handle on how this works, but it's not overly interesting, so I decided to return to our BrightMEDIA accelerator for inspiration. 

One of the more complex Lightning Components we have in the order booking page is the data picker. This can render as a drop down or a calendar, with dates available for selection rendered in specific styles depending on whether capacity is available, if they are contiguous with a selected start date, and many other rules. 

So for my sample I have a couple of custom Lightning Web Components:

  • A Publication Picker - this is a simple choice style component, but I've had to go custom as the standard choice component doesn't yet have reactive support. Each publication has a checkbox that indicates if it publishes on weekends as well as weekdays.
  • A Date Picker - this is a calendar component that by default displays a 7 day week, but if the selected Publication doesn't publish on weekends, switches to a 5 day week.
I create a simple screen flow and add a two column screen component, with my two custom components:



The Publication Picker defines an output property named publishesWeekends, which is set to the value from the selected Publication. I wire this to the weekends input property on the datePicker:


When I run the flow, as I switch my choices between Publications, the Date Picker changes how it renders to reflect 7 days a week publishing:


or 5 days a week:


It's early days for this, but definitely a step in the right direction. This feels like an area where you'll benefit from developer support to create truly reactive experiences, as this will really pop with custom components that can change their styling based on user selections. 

The Code


You can find the code behind the sample in Github. Spin up a scratch org, deploy the code, create a couple of publications and run the flow.  Don't forget to opt in to the beta as outlined above, or like me you'll sit there scratching your head trying to figure out what you've broken :)
More Information



Wednesday, 29 March 2023

Lightning Web Component References in Spring '23

Introduction

Another week, another post about new LWC directives in 'Spring 23 - this time the lwc:ref directive. As the name suggests, this provides a reference to the component, one that can be used in JavaScript to access the element with a minimum of fuss.

Usage

To define a reference to a component, simply specify the lwc:ref directive in the HTML:

<h1 class="slds-var-m-bottom_small" lwc:ref="Heading1">Directives</h1>

and to access the element in JavaScript via the reference:

const headingEle=this.refs.Heading1;

As this.refs is an object, you can also retrieve a reference based on a variable:

const refVal='Heading1';

   ...
   
const chosenEle=this.refs[refval];

Notes/Gotchas

The reference value in the HTML markup must be a string literal - you can't use properties to dynamically generate the reference. This has the knock-on effect that you can't use it in loops to generate a unique reference for each element. If you try, you'll get an error similar to the following when you deploy or push your source:
     LWC1158:
         Invalid lwc:ref usage on element "<li>". lwc:ref cannot be used inside
         for:each or an iterator.

Hopefully this will change in a future release, as it's a key use case that has been a struggle since Visualforce days. 

You can, however, define multiple references with the same name - in this case the last one defined will win. I'm surprised that this isn't blocked by the platform, as it feels like a really easy mistake to make and one that I'm sure I'll encounter a lot over the next couple of years!

If you extract a reference that doesn't exist, in true JavaScript fashion it will return null rather than indicating an error. Always check your reference resolved to an element before taking any further action!

Why Use Refs

So why should we use references, when we can already provide a reference via a data attribute, and dynamically generate the value to boot? There are a few good reasons:
  •  Clarity. A data attribute is a generic mechanism for storing additional information on an HTML element - information that is useful to the application. In order to determine the purpose of this information, I need to examine the JavaScript to figure out how it is used. A reference, on the other hand, has a single purpose - to provide a way to locate the element based on the reference value - no need to look at any JavaScript. As mentioned above, I have to compromise on this clarity and fall back on data attributes to dynamically generate a value, but hopefully that will be coming soon.

  • Cleanliness. Data attributes appear in the rendered HTML, while references don't. This also has the benefit that you aren't exposing something you rely on to possible external interference or for someone else's code to rely on, thus creating an unexpected dependency.

  • Performance - my opinion! I'd also imagine that it's more performant to use references than executing selector queries - the this.refs object is built once, applies to the current template only, and I then extract elements anywhere in my JavaScript with no additional effort. Using selectors I have to execute a query each time I need an element, and I might need multiple selectors to find disparate elements by their attributes. This is the case in Vue.js which has it's own references implementation, so I'd imagine LWC is similar. 

Related Posts




Saturday, 18 February 2023

Conditional LWC Directives in Spring '23

 Introduction

The Spring '23 release of Salesforce is now fully live, after a last minute delay in the US. One of the new features was a set of Lightning Web Component directives for conditional rendering. A relatively minor change, but in my view one with a large impact, as it adds clarity. And as we all know, clarity is the gift that keeps on giving to everyone that works on the component after you.

Old Mechanism

The previous conditional directives were if:true and if:false. Nothing wrong with these per se, but once you start nesting things the clarity starts to leak away. Consider a made up example of a search results page where I want to :

  • display a placeholder if a search hasn't been run
  • display a message if the search has been run but there are no results
  • display the results if the search has been run and there are results
  • display the result count on the right hand side if the search is run and there are results
  • display an additional message if there are more than the 100 results displayed on the page

To assist me I have a few properties - searchRun, hasResults, hasMoreResults, resultCount, and my markup is:
<template if:true={searchRun}>
    <template if:true={hasResults}>
        Results go here!
        <template if:true={hasMoreResults}>
            More than 10 results - please refine your search
        </template>
    </template>
    <div style="float:right">{resultCount} results</div>
    <template if:false={hasResults}>
        Search returned no results
    </template>
 </template>
 <template if:false={searchRun}>
     No search run.
 </template>

While it's not terrible, it's also a bit confusing - lots of true/false and I have to examine the property binding to figure out the relationship between them. The eagle-eyed will also spot the bug - my result count sits outside of the conditional for hasResults, so will be displayed in all cases. This is easily done when the if and else conditionals are independent of each other.

New Mechanism

The new conditionals are lwc:if, lwc:elif and lwc:else. Note that these are dependent on each other - you can't have an lwc:else unless it follows an lwc:if, and it is executed if the lwc:if evaluates to false.

Reworking the markup from above gives me:

<template lwc:if={searchRun}>
    <template lwc:if={hasResults}>
        Results go here!
        <template lwc:if={hasMoreResults}>
            More than 10 results - please refine your search
        </template>
        <div style="float:right">{resultCount} results</div>
    </template>
    <template lwc:else>
        Search returned no results
    </template>
</template>
<template lwc:else>
    No search run.
</template>

which I think is much clearer. I can easily see the markup that will render when the properties evaluate to true or not, and I can see the dependencies as the lwc:else (or lwc:elif) always follows an lwc:if (or lwc:elif). Even more so when I collapse the if template body in VS Code:

Another benefit is that I can't misplace my result count div - if I try to place markup between two dependent lwc conditional directives, in this case between lwc:if and lwc:else, it is immediately called out as a problem:


Related Posts

I've waxed lyrical about clarity in the past, regarding the System.Assert class introduced in Winter '23.




Sunday, 20 November 2022

LWC Alerts in Winter 23


Introduction

The Winter 23 release of Salesforce provided something that, in my view, we've been desperately seeking since the original (Aura) Lightning Components broke cover in 2014 - modal alerts provided by the platform. I'd imagine there are hundreds if not thousands of modal implementations out there, mostly based on the Lightning Design System styling, and all being maintained separately. Some in Aura, some in LWC, but all duplicating effort.

I feel like we have cross-origin alert blocking in Chrome to thank for this - if that wasn't breaking things then I can't see Salesforce would suddenly have prioritised it after all these years - but it doesn't matter how we got them, we have them!

Show Me The Code!

The alerts are refreshingly simple to use too - simply import LightningAlert:

import LightningAlert from 'lightning/alert';

and then execute the LightningAlert.open() function:

    async showAlert() {
        await LightningAlert.open({
            message: 'Here is the alert that will be shown to the user',
            theme: 'warning',
            label: 'Alerted',
            variant: 'header'
        });
    }

and the user sees the alert


The LightningAlert.open() function returns a promise that is resolved when the alert is closed. Note that I've used an async function and the await keyword - I don't have any further processing to carry out while the alert is open, so I use await to stop my function until the user closes the alert. 

Demo Site


When there's a component like this with a number of themes and variants, I typically like to create myself a demo page so I can easily try them all out when I need to. In this case I have a simple form that allows the user to choose the theme and variant, then displays the alert with the selected configuration. 



In the past I'd have exposed this through one of my Free Force sites, but those all disappeared a few months ago so I needed to start again. The new location is https://demo.bobbuzzard.org, which is a Google Site with a custom domain. This particular demo can be found at: https://demo.bobbuzzard.org/lwc/alerts  - it's a Lightning Web Component inside a Visualforce Page using Lightning Out, so with the various layers involved it may take a couple of seconds to render the first time. It does allow guest access though, so worth the trade off in my view. 

Related Posts


Saturday, 19 February 2022

Lightning Web Component Getters

Introduction

When Lightning Web Components were released, one feature gap to Aura components I was pleased to see was the lack of support for expressions in the HTML template. 

Aura followed the trail blazed by Visualforce in allowing this, but if not used cautiously the expressions end up polluting the HTML making it difficult to understand. Especially for those that only write HTML, or even worse are learning it. Here's a somewhat redacted version from one of my personal projects from a few years ago:

Even leaving aside the use of i and j for iterator variables, it isn't enormously clear what name and lastrow will evaluate to.

Handling Expressions in LWC

One way to handle expressions is to enhance the properties that are being used in the HTML. In the example above, I'd process the ccs elements returned from the server and wrap them in an object that provides the name and lastrow properties, then change the HTML to iterate the wrappers and bind directly to those properties. All the logic sits where it belongs, server side. 

This technique also works for non-collection properties, but I tend to avoid that where possible. As components get more complex you end up with a bunch of properties whose sole purpose is to surface some information in the HTML and a fair bit of code to manage them, blurring the actual state of the component. 

The Power of the Getter

For single property values, getters are a better solution in many cases. With a getter you don't store a value, but calculate it on demand when a method is invoked, much like properties in Apex. The template can bind to a getter in the same way it can to a property, so there's no additional connecting up required.

The real magic with getters in LWC is they react to changes in the properties that they use to calculate their value. Rather than your code having to detect a change to a genuine state property and update avalue that is bound from the HTML, when a property that is used inside a getter changes, the getter is automatically re-run and the new value calculated and used in the template.

Here's a simple example of this - I have three inputs for title, firstname and lastname, and I calculate the fullname based on those values. My JavaScript maintains the three state properties and provides a getter that creates the fullname by concatenating the values:

export default class GetterExample extends LightningElement {
    title='';
    firstname='';
    lastname='';

    titleChanged(event) {
        this.title=event.detail.value;
    }

    firstnameChanged(event) {
        this.firstname=event.detail.value;
    }

    lastnameChanged(event) {
        this.lastname=event.detail.value;
    }
    
    get fullname() {
        return this.title + ' ' + this.firstname + ' ' + this.lastname;
    }
}

and this is used in my HTML as follows:

<template>
    <lightning-card title="Getter - Concatenate Values">
        <div class="slds-var-p-around_small">
            <div>
                <lightning-input label="Title" type="text" value={title} onchange={titleChanged}></lightning-input>
            </div>
            <div>
                <lightning-input label="First Name" type="text" value={firstname} onchange={firstnameChanged}></lightning-input>
            </div>
            <div>
                <lightning-input label="Last Name" type="text" value={lastname} onchange={lastnameChanged}></lightning-input>
            </div>
            <div class="slds-var-p-top_small">
                Full name : {fullname}
            </div>
        </div>
    </lightning-card>
</template>

Note that I don't have to do anything to cause the fullname to rerender when the user supplies a title, firstname or lastname. The platform detects that those properties are used in my getter and automatically calls it when they change. This saves me loads of code compared to aura.

You can also have getters that rely on each other and the whole chain gets re-evaluated when a referenced property changes. Extending my example above to use the fullname in a sentence:

get sentence() {
    return this.fullname + ' built a lightning component';
}

and binding directly to the setter:

<div class="slds-var-p-top_small">
    Use it in a sentence : {sentence}
</div>

and as I complete the full name, the sentence is automatically calculated and rendered, even though I'm only referencing another getter that was re-evaluated:




You can find the component in my lwc-blogs repository at : https://github.com/keirbowden/lwc-blogs/tree/main/force-app/main/default/lwc/getterExample

Another area that Lightning Web Components score in is they are built on top of web standards, so if I want to change values that impact getters outside of the user interactions, I can use a regular setinterval  rather than having to wrap it inside a $A.getCallback function call, as my next sample shows:


In this case there is a countdown property that is calculated based on the timer having been started and not expiring, and an interval timer that counts down to zero and then cancels itself:

timer=30;
interval=null;

startCountdown() {
    this.interval=setInterval(() => {
        this.timer--;
        if (this.timer==0) {
           clearInterval(this.interval);
        }
    }, 1000);
}

get countdown() {
    let result='Timer expired';
    if (null==this.interval) {
        result='Timer not started';
    }
    else if (this.timer>0) {
        result=this.timer + ' seconds to go!';
    }

    return result;
}
and once again, I can just bind directly to the getter in the certainty that if the interval is populated or the timer changes, the UI will change with no further involvement from me.
<div class="slds-var-p-top_medium">
    <div class={countdownClass}>{countdown}</div>
</div>

Note that I'm also using a getter to determine the colour that the countdown information should be displayed in, removing more logic that would probably be in the view if using Aura:



You can also find this sample in the lwc-blogs repo at : https://github.com/keirbowden/lwc-blogs/tree/main/force-app/main/default/lwc/getterCountdown

Related Posts 

Saturday, 22 January 2022

refreshApex and Lightning Web Components

Introduction

One of the things I particularly like about Lightning Web Components is the wire service. The ability to let the platform send me the data when it decides things are ready, rather than having to figure out when to call an Apex method based on rendered callbacks and the like really cleaned up my code from the days of Aura.

Updated Records on the Server

The one area that it doesn't automatically handle for me is sending me new data when a record is updated server side, by other Apex code or maybe the controller of the LWC that is using the wire service. Initially my approach was to return the updated records to the component if it was my own controller method carrying out the update, or switch to imperative Apex calls once I knew an update had taken place. Neither of these were particularly satisfactory though.

The solution is the refreshApex method, which you import from salesforce/apex. Per the docs 

Sometimes, you know that the cache is stale. If the cache is stale, the component needs fresh data. To query the server for updated data and refresh the cache, import and call the refreshApex() function.

Exactly what I need, and it keeps my data in the scope of the wire service, so if that gets notified that there is a newer version of the data, I'll get that too.

There's a slight gotcha that makes it easy to get this wrong. When I first tried it I was convinced that it didn't work, because I'd misunderstood a key aspect of the docs. The instruction in question is:

NOTE The parameter you refresh with refreshApex() must be an object that was previously emitted by an Apex @wire.

So in my wire method handler, I captured the object that I'd retrieved:
@wire(GetPage, {name: 'home'})
gotPage(result) {
    if (result.data) {
        this.page=result.data;
        this.dataToRefresh=result.data;
    }

and when I knew the record had been updated server side, I executed the refreshApex method:

getLatest() {
refreshApex(this.dataToRefresh);
}
and nothing happened. I got no errors, but the data didn't change either. As refreshApex returns a promise, I figured maybe the issue was it wasn't resolving, so I added the code to handle success and failure:
getLatest() {
refreshApex(this.dataToRefresh)
    .then(()=> {
        this.dispatchEvent(
            new ShowToastEvent({
                title: 'Success',
                message: 'Refreshed Data',
                variant: 'success'
            })
        );
    })
    .catch((error) => {
        this.dispatchEvent(
            new ShowToastEvent({
                title: 'Error Refreshing Data',
                message: message,
                variant: 'error'
            })
        );
    });
}

this time I got an error, that didn't make a lot of sense to me, but seemed like the refreshApex method wasn't returning a promise, even though it was supposed to return an promise.

As it was the first time I'd used it, I had no idea what the correct usage looked like, so there was a bit of trial and error before I realised that "an object that was previously emitted by an Apex @wire" meant the entire object rather than the data property that I was extracting. And reading further on the docs confirmed this, which reminded me to always RTFM!

Updating my wire method handler to capture the whole thing rather than the records from the server:

@wire(GetPage, {name: 'home'})
gotPage(result) {
    if (result.data) {
        this.page=result.data;
        this.dataToRefresh=result;
    }
and things were all good:


Notice that I don't have to care what data was returned by the wired method, I just save it somewhere and pass it as the parameter.

Example Component

My LWC Blogs repo has an example component that shows refreshApex being used correctly. If you are going to try it out in a scratch org, check the instructions in the README as there's a little bit of setup to get the page to work correctly. 

The component retrieves a Page record that has the name 'Home' and shows how many views it has had. There's a button you can click that increments the views count by calling an Apex method. Once the method completes, the record cached server side is updated using the refreshApex method, and the user receives notifications about all sorts of things:



Related Posts