0

I have one LWC component that is using the import functionality to pull in another LWC component. The imported component has a public method exposed via the @api decorator.

The code is as follows (I boiled it down to make it more digestible):

Component that gets imported

import { LightningElement, api, wire } from 'lwc';
import getPricingData from '@salesforce/apex/CustomPricingService.getPricingData';

export default class CustomPricing extends LightningElement {

    _productId;

    @wire(getPricingData, {productId:`$_productId`}) pricingData;

    @api getPricing(pricingType, inputs){
        this._productId = inputs.productId;
    }
}

Component that performs the import

import { LightningElement } from 'lwc';
import CustomPricing from 'c/customPricing';

export default class UsesCustomPricing extends LightningElement {

    handleButtonClick(buttonEvent){
        CustomPricing.getPricing( 'myType', { productId: '01t000000000000000' });
    }
}

When the public method is invoked, there is an error.

Uncaught TypeError: CustomPricing__default.default.getPricing is not a function throws at https://sandboxname--c.visualforce.com/auraFW/javascript/Q3onN6EmJyHRC52_NEPe2B/aura_proddebug.js:11107:11

I logged the imported component to the console and saw that it was just a class that was imported. So I tried to create a new instance of the class to try and invoke the public method as shown below:

Component that performs the import - Revision 1

import { LightningElement } from 'lwc';
import CustomPricing from 'c/customPricing';

export default class UsesCustomPricing extends LightningElement {

    handleButtonClick(buttonEvent){
        let newInstance = new CustomPricing();
        newInstance.getPricing( 'myType', { productId: '01t000000000000000' });
    }
}

It results in an error:

Uncaught ReferenceError: Illegal constructor throws at https://sandboxname--c.visualforce.com/auraFW/javascript/Q3onN6EmJyHRC52_NEPe2B/aura_proddebug.js:11107:11

So I feel like I have a few options.

  1. Just directly add the imported component to the markup of the importing component instead of importing it in the JS file. Then would just need to do a this.template.querySelectorAll('c-custom-pricing')[0].getPricing('myType',{productId:'01t000000000000000'}); But this doesn't feel right and I'd like to learn the correct way to address this issue.
  2. Modify the imported component to no longer export a class and instead have it export a function. But then I lose the LWC caching from the @wire functionality that I intentionally want.
  3. Try to figure out a way to export a function from the CustomPricing component that can somehow interact with the CustomPricing class which would allow the caching from @wire to function. Would be similar to
import { LightningElement, api, wire } from 'lwc';
import getPricingData from '@salesforce/apex/CustomPricingService.getPricingData';

export default class CustomPricing extends LightningElement {

    productId;

    @wire(getPricingData, {productId:`$productId`}) pricingData;

    @api getPricing(pricingType, inputs){
        this.productId = inputs.productId;
    }
}

export function myFunction(pricingType, inputs){

    let newInstance = new CustomPricing();
    return newInstance.getPricing(pricingType, inputs);
}
1

An imperative Apex call would probably be more appropriate here:

import { refreshApex } from '@salesforce/apex';
import getPricingData from '@salesforce/apex/CustomPricingService.getPricingData';
_cache = {}
export function getPriceData(productId) {
  return new Promise((resolve, reject) => {
    if(_cache[productId]) {
      refreshApex(_cache[productId]).then(
        result => (_cache[productId] = result, resolve(result.data))
      ).catch(error => reject(error));
    } else {
      getPricingData({productId}).then(
        result => (_cache[productId] = result, resolve(result.data))
      ).catch(error => reject(error));
   });
}

Which then makes your dependent code:

import { getPriceData } from 'c/customPricing';
...
handleButtonClick(event) {
  try {
    let pricingData = await getPriceData(event.target.dataset.productId);
    ...
  } catch(e) {
    // deal with error here
  }
}

You should not try to extend a LightningElement unless a component is a real UI component. You can still use the export default class design if you want, but I don't believe wire methods work properly across classes, hence the imperative calls.

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.