1

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 Answer 1

2

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.

You must log in to answer this question.

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