11

How we handle dependent wire in LWC?

For example :

  @wire(getObjectInfo, { objectApiName: OPP_OBJECT })
  objectInfo;

  @wire(getPicklistValues, {
    recordTypeId: '$objectInfo.data.defaultRecordTypeId',
    fieldApiName: STATUS_FIELD
  })
  pickListValues;

  @wire(getRecord, { recordId: '$recordId', fields: FIELDS })
  wiredRecord({ error, data }) {
    if (ldsErrorHandling(error) && data) {
      this._status = getFieldValue(data, STATUS_FIELD);
    }
  }

I have an operation that depends on the Stage Field of opportunity and also the picklist values. With normal promises or callbacks I can know when the two values are not null. If is a normal apex call we can have both information at the same time. But how we handle this type of situations with @wire.

My solution for this (is this the only way?):

  @wire(getObjectInfo, { objectApiName: OPP_OBJECT })
  objectInfo;

  @wire(getPicklistValues, {
    recordTypeId: '$objectInfo.data.defaultRecordTypeId',
    fieldApiName: STATUS_FIELD
  })
  handlePickValues({ error, data }) {
    if (ldsErrorHandling(error) && data) {
      this._data = data;
      this.dependentLogic();
    }
  }

  @wire(getRecord, { recordId: '$recordId', fields: FIELDS })
  wiredRecord({ error, data }) {
    if (ldsErrorHandling(error) && data) {
      this._status = getFieldValue(data, STATUS_FIELD);
      this.dependentLogic();
    }
  }

  dependentLogic() {
    if (this._status && this._data) //do logic
  }

Thank you in advance

  • As far as I can tell you the platform should be able to handle this dependency. Don't know for sure though how. Have you tried this and is there a particular issue that you are facing? – Jayant Das Apr 26 '19 at 0:28
  • The issue is, I want to make sure that the two variables are filled before I do my logic. Right now I use in both getPicklistValues and getRecord with functions instead putting directly in a var. Inside the function I call a method. On top of this method I verify if both var are not null, and then I do the logic. I don't like this approach, and I like to know exactly what my code is doing. – Elias Pinheiro Apr 26 '19 at 7:54
  • +1 to @JayantDas's answer: the code you provide should work. If it doesn't please post the minimal code that fails with details of the error. @wire (like Promises) operates asynchronously. You must coordinate their resolution. You can do this with a simple check. Eg if (this.objectInfo.data && this.picklistValue.data) { ... } – Kevin Venkiteswaran Jul 18 '19 at 19:31
  • Hello @KevinVenkiteswaran, the code always worked. My only doubt was that with @wire the only way I know is to call two or more times(depending how many dependencies) the same function and check if all values that we need is not null. And I was asking if has a better way to do. – Elias Pinheiro Jul 23 '19 at 10:09
  • @EliasPinheiro Did you get any solution, what you did at last, I've the same scenario, I don't want to create dependency on the wired services and having a business logic which dependent on two different wired service output. – user2809299 Jan 7 at 17:23
6

According to LWC docs:

The component’s JavaScript prefaces the value of the objectInfo parameter with $ to indicate that it’s dynamic and reactive. It references a property of the component instance. If its value changes, the template rerenders.

Before understanding dynamic and reactive variable lets understand reactive (that is , track).

When track variable is re-assigned/changed in Javascript, LWC will re-render the DOM with updated track variables.

What will happen if dynamic ('$objectInfo') changes?

Well, the wire functions dependent on this variable is triggered.

So, change in dynamic '$objectInfo' will trigger the Javascript functions which are dependent on it. IMPORTANT: this will not re-render DOM.

objectInfo is made both dynamic and reactive by using it as $objectInfo and also decorating it with track.

So, below code will work:

  @wire(getObjectInfo, { objectApiName: OPP_OBJECT })
  objectInfo;

  @wire(getPicklistValues, {
    recordTypeId: '$objectInfo.data.defaultRecordTypeId',
    fieldApiName: STATUS_FIELD
  })
  pickListValues;

Reason: Here second wire will be triggered ONLY after first wire updates objectInfo. Thus pickListValues will be updated after getting objectInfo

Best practice: Do not use '$dynamic' variables in HTML (by decorating as track). Because if you are decorating this dynamic variable as track, 1st it will rerender the DOM before wire is triggered and again after wire gets the data, DOM is rerendered again (leading to performance degradation due to twice rerender of DOM). You can verify this with rerenderedCallback.


Important Notes: https://salesforcesas.home.blog/2019/07/23/dynamic-and-reactive-javascript-class-properties-in-lwc-track-wire-and/

| improve this answer | |
  • Are you sure that it will trigger that way? – ytiq Jul 23 '19 at 13:05
  • YES! Tested it here: salesforcesas.home.blog/2019/07/23/… – salesforce-sas Jul 23 '19 at 13:06
  • Hello @salesforcesas, thank you very much for your answer, but I think you did not understand what I asked. I have two wire methods that has no dependencies in each other. But I have business logic that depends on both. And if I made the two wire dependent they will get data in serialized mode that is slower than parallel requests. – Elias Pinheiro Jul 23 '19 at 15:54
  • 1
    If I use getRecord in an imperative way I will lose the reactivity of the wire in the lightning data service. But for other things it will be the best choice for sure. – Elias Pinheiro Jul 24 '19 at 13:33
  • 1
    @EliasPinheiro did you find any way to handle this imperatively without losing reactivity? – Muzaffar Mahmood Jun 3 at 13:44
1

I had similar requirements. What I decided to do is to have @track property and pass it as a parameter to @wire method.

@track
objectInfo;
@wire(getObjectInfo, { objectApiName: OPP_OBJECT })
setObjectInfo({error, data}) {
    if (data) {
        this.objectInfo = data;
    } else if (error) {
        // ...
    }
}

@wire(getPicklistValues, {
    recordTypeId: '$objectInfo.data.defaultRecordTypeId',
    fieldApiName: STATUS_FIELD
})
pickListValues;

So when this.objectInfo is set, then getPicklistValues is called.

| improve this answer | |
  • Hello @ytiq, thank you very much for your answer, but I think you did not understand what I asked. I have two wire methods that has no dependencies in each other. But I have business logic that depends on both. And if I made the two wire dependent they will get data in serialized mode that is slower than parallel requests. – Elias Pinheiro Jul 23 '19 at 15:55
0

Addressing this from the perspective of having logic that is dependent on two wire calls (and not a wire method that depends on another, which has already been answered), an alternative to using a common function call is to put the dependent logic into a common getter:

@wire(getObjectInfo, { objectApiName: OPP_OBJECT })
objectInfo;

@wire(getPicklistValues, {
    recordTypeId: "$objectInfo.data.defaultRecordTypeId",
    fieldApiName: STATUS_FIELD
})
handlePickValues({ error, data }) {
    if (ldsErrorHandling(error) && data) {
        this._data = data;
    }
}

@wire(getRecord, { recordId: "$recordId", fields: FIELDS })
wiredRecord({ error, data }) {
    if (ldsErrorHandling(error) && data) {
        this._status = getFieldValue(data, STATUS_FIELD);
    }
}

get dependentResult() {
    let result;
    if(this._status && this._data) {
        //Process logic based on results from both wires. 
    }
    return result;
}

Then you can use this.dependentResult elsewhere in your component.

| improve this answer | |

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.