function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Kristian Kauper 1Kristian Kauper 1 

LWC component works on Contact but not on Opportunity

We have created an LWC component for use on Contact and Opportunity pages. In testing, the component works fine on those two pages, but a couple of customers have reported that the component installs fine on their Contact page, but not on their Opportunity page.

In the case of the Opportunity page, our component appears in the list of components, and they can drag it on to the page, but then a loader appears and the process never completes.

This has happened to at least two customers, so this does not appear to be an isolated issue.

Does anyone know what could cause this?
Gian Piere VallejosGian Piere Vallejos
Can you share the code of your HTML and Js files? Maybe it would be something related to the renderedCallback stage. But it's hard to tell without see the code. 
Kristian Kauper 1Kristian Kauper 1
Hi Gian,

Ah, I guess I didn't understand that the component is actually initialized/rendered during the process of adding it to the page. I thought it was just a dummy image to represent the component until the page had been saved and an Opportunity page viewed. So, it's actually the component's loader which never completes.

I won't paste all of the javascript, because it's too large and probably not pertinent. Can you see any issues with this initialization logic?
 
const USER_FIELDS = [
  'User.Email',
]

const CONTACT_FIELDS = [
  'Contact.Email',
  'Contact.FirstName',
  'Contact.LastName',
  //'Contact.Title',
  //'Contact.Phone',
  //'Contact.MobilePhone',
  //'Contact.LastModifiedDate',
]

const OPPORTUNITY_FIELDS = [
  'Opportunity.Amount',
  'Opportunity.CloseDate',
  'Opportunity.CreatedDate',
  'Opportunity.Description',
  'Opportunity.ExpectedRevenue',
  'Opportunity.IsClosed',
  'Opportunity.IsWon',
  'Opportunity.Name',
  'Opportunity.StageName',
  'Opportunity.TotalOpportunityQuantity',
  'Opportunity.LastModifiedDate',
]

export default class Destinationcard extends LightningElement {

  @track isLoading = true
  @track isProcessing = false
  @track errorMessage = false
  @track isAuthenticated = false

  @track user = null
  @track contact = null
  @track deal = null
  @track dealContacts = []
  @track destinations = []
  @track totalDestinations = 0
  @track moreDestinations = 0
  @track hasMoreDestinations = false
  @track moreDestinationsAsLink = false

  @track showAppIframe = false

  iconEmlen = emlenicon
  @track iframeUrl = ''
  emlenApi = new Emlenapi(DEV_MODE)

  // Fetch user data
  @wire(getRecord, { recordId: USER_ID, fields: USER_FIELDS }) sfUser

  get sfUserEmail() {
    return getFieldValue(this.sfUser.data, 'User.Email')
  }

  // The ID of this contact or opportunity record
  @api recordId
  sfContactId
  sfDealId

  // The type of this record
  @api objectApiName

  // Used for debugging
  @wire(getObjectInfos, { objectApiNames: [ CONTACT_OBJECT, OPPORTUNITY_OBJECT, OPPORTUNITY_CONTACT_ROLE_OBJECT ] })
    objectProperties

  // Fetch contact data
  sfContact
  @wire(getRecord, { recordId: '$sfContactId', fields: CONTACT_FIELDS, type: 'Contact' })
  getContact({ error, data }) {
    if (data) {
      this.sfContact = data
      this.initialize()
    }
  }

  get sfContactEmail() {
    return getFieldValue(this.sfContact, 'Contact.Email')
  }

  // Fetch deal data
  sfDeal
  @wire(getRecord, { recordId: '$sfDealId', fields: OPPORTUNITY_FIELDS })
  getDeal({ error, data }) {
    if (data) {
      this.sfDeal = data
      this.initialize()
    }
  }

  get sfDealName() {
    return getFieldValue(this.sfDeal, 'Opportunity.Name')
  }

  // Fetch the deal's Contact Roles
  sfContactIds
  sfContactSearch
  @wire(getRelatedListRecords, {
    parentRecordId: '$sfDealId',
    relatedListId: 'OpportunityContactRoles',
    pageSize: 100,
    fields: ['OpportunityContactRole.ContactId'],
  }) getDealContactRoles({ error, data }) {
    if (Array.isArray(data?.records)) {
      this.sfContactIds = data.records.map(record => record.fields.ContactId.value)
      this.sfContactSearch = [
        {
          recordIds: this.sfContactIds,
          fields: CONTACT_FIELDS,
          // optionalFields: [EMAIL_FIELD]
        },
      ]
    }
  }

  // Fetch the deal's contacts
  sfDealContacts
  @wire(getRecords, {
    records: '$sfContactSearch',
  }) getDealContacts({ error, data }) {
    if (Array.isArray(data?.results)) {
      this.sfDealContacts = data.results.map(result => result.result)
    }
  }

  errorCallback(error) {

    this.isLoading = false
    this.isProcessing = false

    let isFlash = false

    if (error instanceof NotAuthenticated) {
      this.errorMessage = 'You have been logged out. Please log in again.'
      this.isAuthenticated = false
      isFlash = true
    }

    else if (error instanceof InvalidLogin) {
      this.errorMessage = 'Unknown account. Please try again.'
      isFlash = true
    }

    else {
      let message = 'An error has occurred. Please try loading again.'
      if (DEV_MODE || error instanceof PublicError) {
        message = error.message
      }
      this.errorMessage = message
    }

    if (isFlash) {
      // eslint-disable-next-line @lwc/lwc/no-async-operation
      setTimeout(() => {
        /* istanbul ignore next */
        this.errorMessage = ''
      }, 3000)
    }
  }

  async connectedCallback() {
    try {
      if (this.objectApiName === 'Contact') {
        this.sfContactId = this.recordId
      } else if (this.objectApiName === 'Opportunity') {
        this.sfDealId = this.recordId
      } else {
        throw new PublicError('This component is not valid on this page')
      }

      //console.log('objectInfo', JSON.stringify(this.objectProperties))
      //console.log('contact', this.sfContactId, JSON.stringify(this.sfContact))
      //console.log('deal', this.sfDealId, JSON.stringify(this.sfDeal))
      //console.log('contacts', JSON.stringify(this.sfDealContacts))

    } catch (e) {
      this.errorCallback(e)
    }
  }

  async initialize() {
    try {

      // Install listener for messages from the iframed app
      window.addEventListener('message', /* istanbul ignore next */ async(event) => {
        const message = event.data
        let payload = {}
        try {
          payload = JSON.parse(message)
        } catch (e) {
          // Ignore
        }
        if (payload?.action === 'DONE') {
          await this.handleCloseAppIframe()
        }
      }, false)

    } catch (e) {
      this.errorCallback(e)
    } finally {
      this.isLoading = false
    }
  }

  handleInputChange(e) {
    if (e.target.name !== undefined && this[e.target.name] !== undefined) {
      this[e.target.name] = e.target.value
    }
  }

  async handleCloseAppIframe() /* istanbul ignore next */ {
    this.isProcessing = true
    this.showAppIframe = false
  }

  // <snip>
}