3

Am I allowed to use the wire service in an ES module, i.e. a 'headless' utility class? Or, must I extend LightningElement also?

import {wire} from 'lwc';
import sapLogin from '@salesforce/apex/SAPService.login';//cachable=true

export class SapSession
{
    //sessionInfo = undefined;//EDIT oops don't need to declare this
    //the wire below does that

    @wire(sapLogin)sessionInfo

    get Cookie() {
        return sessionInfo.data.Cookies;//delimited string of cookies returned        
    }
    get Error() {
        return sessionInfo.error;//wire failure returns error poperty      
    }
}

When I deploy the above class, there are no compilation issues. But, it also doesn't seem to be working in the consuming lwc, either, e.g.

import { LightningElement } from 'lwc';
import { SapSession } from 'c/sapSession';

export default class someUiComponent extends LightningElement {

    session = undefined;

    // One of the standard lifecycle callbacks you can include in an LWC
    connectedCallback() {
        this.session = SapSession;
    }

}

someUiComponent.html

<template>
    <div>
        little test:
      
        <div if:true={session}>
            <div if:true={session.Cookie}> {session.Cookie}</div>
            <div if:true={session.Error}> {session.Error}</div>
        </div>
    </div>
</template>

2 Answers 2

8

In LWC OSS, you can freely use wire wherever you want. In Salesforce, though, you can only use wire methods inside the current LightningElement. Even if you made the service class extend LightningElement, you'd still find it won't work without adding the component to the DOM.

That said, you can add it to the DOM and have it work:

<c-sap-session></c-sap-session>

...

const this.template.querySelector('c-sap-session').Cookie;

Also make sure that you use the @api decorator:

import {api, wire, LightningElement } from 'lwc';
import sapLogin from '@salesforce/apex/SAPService.login';//cachable=true

export class SapSession extends LightningElement
{
    @wire(sapLogin, {})sessionInfo

    @api get Cookie() {
        return sessionInfo.data.Cookies;//delimited string of cookies returned        
    }
    @api get Error() {
        return sessionInfo.error;//wire failure returns error poperty      
    }
}

Finally, note that wire methods are not provisioned until after connectedCallback and the initial renderedCallback. I'd recommend setting up an onload handler to let the parent know when the data is ready:

import {api, wire, LightningElement } from 'lwc';
import sapLogin from '@salesforce/apex/SAPService.login';//cachable=true

export class SapSession extends LightningElement
{
    sessionInfo;
    @wire(sapLogin, {})sessionInfoHandler(result) {
      this.sessionInfo = result;
      if(result.data) {
        this.dispatchEvent(new CustomEvent('load', { detail: result.data }))
      }
    }
    @api get Cookie() {
        return sessionInfo.data.Cookies;//delimited string of cookies returned        
    }
    @api get Error() {
        return sessionInfo.error;//wire failure returns error poperty      
    }
}

Or something similar.


Alternatively, you can call the method imperatively, which avoids the need for all this extra work:

import { api } from 'lwc'
import sapLogin from '@salesforce/apex/SAPService.login';//cachable=true

export class SapSession {
    sessionInfo = {}
    async constructor() {
        this.sessionInfo = await sapLogin({})
    }

    @api get Cookie() {
        return sessionInfo.data.Cookies;//delimited string of cookies returned        
    }
    @api get Error() {
        return sessionInfo.error;//wire failure returns error poperty      
    }
}

However, even though we call this in the constructor, it is guaranteed to not be available during the connectedCallback of components that use it.

Some additional work may be necessary, but any of these designs should get you close.

tl;dr wire only works in a DOM-connected LightningElement, so making a service class won't help; call the method imperatively, or accept that you need to embed the component in every component that needs this data.

4
  • This is great info. As I think through my design, I'm probably overthinking this.. I should probably just treat my wire service, as if it IS the module.. and simply put this line at the top of all my UI components: import sapLogin from '@salesforce/apex/SAPService.login'; I guess I was trying to ecapsulate the error handling a little bit, but, I'm not sure it's worth the other complications
    – bkwdesign
    Commented Dec 13, 2021 at 19:54
  • @bkwdesign That's what I'd say, wire methods are already pretty terse, no need to complicate things.
    – sfdcfox
    Commented Dec 13, 2021 at 20:12
  • annnnnd.. I'm thinking if I did follow through with my original approach, now that I'm getting my head wrapped around things a bit more.. maybe I oughta use slotting ``` <template> <lightning-card title="SAP Session Provider" icon-name={icon}> <slot></slot> </lightning-card> </template> ```
    – bkwdesign
    Commented Dec 13, 2021 at 20:19
  • @bkwdesign Yeah, I didn't want to make too many assumptions, but that looks like a reasonable approach to your code.
    – sfdcfox
    Commented Dec 13, 2021 at 20:21
2

@wire'd functions can only be used within the context of an extended LightningElement class, so, you cannot create a module, import the wire service module and use it independently.

3
  • The documentation I've read so far seems really silent on this topic, and my experience thus far proves your comment to be right
    – bkwdesign
    Commented Dec 13, 2021 at 19:30
  • @bkwdesign there are workarounds, though. I'm writing something up now.
    – sfdcfox
    Commented Dec 13, 2021 at 19:30
  • nice, didn't know of the workaround you posted, +1
    – glls
    Commented Dec 14, 2021 at 0:05

You must log in to answer this question.

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