Skip to main content
added 42 characters in body
Source Link
Robs
  • 9.4k
  • 21
  • 119
  • 224
/* eslint-disable @lwc/lwc/no-async-operation */
import { LightningElement, wire, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import PP from '@salesforce/resourceUrl/penpal'
import getVisualforceDomainURL from '@salesforce/apex/LC_VisualforceDomainController.getVisualforceDomainURL';

export default class Api extends LightningElement {

    _penpal = {};
    penpalInitialized = false;
    iframeSrc = '';

    @wire(getVisualforceDomainURL) wiredDomainUrl({ error, data }) {
        if (data) {
            this.iframeSrc = data + '/apex/LC_APIPage';
            console.log(this.iframeSrc);
            this.initializePenpal();
        } else if (error) {
            console.error('LWC_API: error getting Visualforce Domain URL', error);
            this.iframeSrc = undefined;
        }
    }

    connectedCallback() {

        Promise.all([
            loadScript(this, PP)
        ]).then(() => { });
    }

    initializePenpal() {

        let initialized = this.penpalInitialized;
        var self = this;

        // Since the iframe source is calculated asynchronously,
        // we listen to the component's render events and each time
        // check if the iframe is ready, and if so, then we initialize
        // penpal to connect this component to the iframe.
        // Since we only want to do this once, we also set the initialized flag.
        if (!initialized) {

            const container = this.template.querySelector('div');
            const iframeElmt = document.createElement('iframe');
            iframeElmt.src = this.iframeSrc;
            container.appendChild(iframeElmt);

            if (iframeElmt != null) {

                this.penpalInitialized = true;

                // eslint-disable-next-line no-undef
                const connection = Penpal.connectToChild({
                    // The iframe to which a connection should be made
                    iframe: iframeElmt
                });

                this._penpal.connection = connection;

                connection.promise.then(function (child) {
                    // Cache a reference to the child so that we can
                    // use it in the restRequest/fetchRequest methods,
                    // as well as be able to destroy it when this component unrenders.
                    console.log(child);
                    self._penpal.child = child[0];child;
                }).catch(function (err) {
                    console.error('LWC_API: Error establishing connection to iframe', err);
                    this.penpalInitialized = false;
                });

            } // else, iframe source is empty, keep waiting
        }
    }

    // Makes a Salesforce REST API request and returns a promise that resolves to the response.

    @api restRequest(request) {

        var self = this;

        var defaultRequest = {
            'method': 'get'
        };

        var defaultHeaders = {
            'Content-Type': 'application/json'
        };

        request = Object.assign({}, defaultRequest, request);
        request.headers = Object.assign({}, defaultHeaders, request.headers);

        return this.getPenpalChild().then(function (child) {
            return self.makePenpalRequest('rest', child, request);
        });
    }

    @api fetchRequest(request) {
        return this.getPenpalChild().then(function (child) {
            return this.makePenpalRequest('fetch', child, request);
        });
    }

    // ------------------------------------------------------------

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate handshake to complete
     * then resolves with reference to the postmate child for making requests.
     */
    getPenpalChild() {

        var child;
        var self = this;
        var timerId;
        var timeout = 10000; // ten seconds
        var pollFrequency = 500; // half a second
        var startTime = new Date().getTime();
        var endTime = startTime + timeout;

        return new Promise(function (resolve, reject) {

            child = self._penpal.child;

            if (child) {

                resolve(child);

            } else {

                // all time values in milliseconds
                timerId = setInterval(function () {

                    child = this._penpal.child;

                    if (child) {

                        // parent-child postmate handshake now complete
                        clearInterval(timerId);
                        resolve(child);

                    } else {

                        // check if we have exceeded our timeout
                        let currentTime = new Date().getTime();
                        if (currentTime > endTime) {
                            clearInterval(timerId);
                            reject('LWC_API: Timeout trying to establish connection to iframe');
                        }
                        // else, keep polling
                    }

                }, pollFrequency);

            }

        });

    }

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate request to complete
     * then resolves with response from the child iframe.
     */
    makePenpalRequest(requestType, child, request) {

        var p;

        if (requestType === 'rest') {
            console.log('makePenpalRequest');
            console.log(child);
            console.log(child.restRequest);
            p = child.restRequest(request);
        } else if (requestType === 'fetch') {
            p = child.fetchRequest(request);
        } else {
            p = Promise.resolve({
                success: false,
                data: 'LWC_API: Invalid request type: ' + requestType
            });
        }

        console.log('makePenpalRequest');

        return p.then(function (response) {

            console.log(response);

            if (response.success) {
                return response.data;
            }
            throw new Error(response.data);

        });

    }

    disconnectedCallback() { // unrender
        // this.superUnrender();
        // When component unrenders then cleanup penpal
        // resources by destroying the connection and nulling out
        // the helper's cached reference to the connection and child.
        // This ensures that the helper.handleXyzRequest(..) methods
        // wait appropriately for the new parent-child handshake to complete
        // when this component is re-initialized and scripts are loaded.
        if (this._penpal && this._penpal.connection) {
            this._penpal.connection.destroy();
            this._penpal = {};
        }
    }

}
/* eslint-disable @lwc/lwc/no-async-operation */
import { LightningElement, wire, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import PP from '@salesforce/resourceUrl/penpal'
import getVisualforceDomainURL from '@salesforce/apex/LC_VisualforceDomainController.getVisualforceDomainURL';

export default class Api extends LightningElement {

    _penpal = {};
    penpalInitialized = false;
    iframeSrc = '';

    @wire(getVisualforceDomainURL) wiredDomainUrl({ error, data }) {
        if (data) {
            this.iframeSrc = data + '/apex/LC_APIPage';
            console.log(this.iframeSrc);
            this.initializePenpal();
        } else if (error) {
            console.error('LWC_API: error getting Visualforce Domain URL', error);
            this.iframeSrc = undefined;
        }
    }

    connectedCallback() {

        Promise.all([
            loadScript(this, PP)
        ]).then(() => { });
    }

    initializePenpal() {

        let initialized = this.penpalInitialized;
        var self = this;

        // Since the iframe source is calculated asynchronously,
        // we listen to the component's render events and each time
        // check if the iframe is ready, and if so, then we initialize
        // penpal to connect this component to the iframe.
        // Since we only want to do this once, we also set the initialized flag.
        if (!initialized) {

            const container = this.template.querySelector('div');
            const iframeElmt = document.createElement('iframe');
            iframeElmt.src = this.iframeSrc;
            container.appendChild(iframeElmt);

            if (iframeElmt != null) {

                this.penpalInitialized = true;

                // eslint-disable-next-line no-undef
                const connection = Penpal.connectToChild({
                    // The iframe to which a connection should be made
                    iframe: iframeElmt
                });

                this._penpal.connection = connection;

                connection.promise.then(function (child) {
                    // Cache a reference to the child so that we can
                    // use it in the restRequest/fetchRequest methods,
                    // as well as be able to destroy it when this component unrenders.
                    self._penpal.child = child[0];
                }).catch(function (err) {
                    console.error('LWC_API: Error establishing connection to iframe', err);
                    this.penpalInitialized = false;
                });

            } // else, iframe source is empty, keep waiting
        }
    }

    // Makes a Salesforce REST API request and returns a promise that resolves to the response.

    @api restRequest(request) {

        var self = this;

        var defaultRequest = {
            'method': 'get'
        };

        var defaultHeaders = {
            'Content-Type': 'application/json'
        };

        request = Object.assign({}, defaultRequest, request);
        request.headers = Object.assign({}, defaultHeaders, request.headers);

        return this.getPenpalChild().then(function (child) {
            return self.makePenpalRequest('rest', child, request);
        });
    }

    @api fetchRequest(request) {
        return this.getPenpalChild().then(function (child) {
            return this.makePenpalRequest('fetch', child, request);
        });
    }

    // ------------------------------------------------------------

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate handshake to complete
     * then resolves with reference to the postmate child for making requests.
     */
    getPenpalChild() {

        var child;
        var self = this;
        var timerId;
        var timeout = 10000; // ten seconds
        var pollFrequency = 500; // half a second
        var startTime = new Date().getTime();
        var endTime = startTime + timeout;

        return new Promise(function (resolve, reject) {

            child = self._penpal.child;

            if (child) {

                resolve(child);

            } else {

                // all time values in milliseconds
                timerId = setInterval(function () {

                    child = this._penpal.child;

                    if (child) {

                        // parent-child postmate handshake now complete
                        clearInterval(timerId);
                        resolve(child);

                    } else {

                        // check if we have exceeded our timeout
                        let currentTime = new Date().getTime();
                        if (currentTime > endTime) {
                            clearInterval(timerId);
                            reject('LWC_API: Timeout trying to establish connection to iframe');
                        }
                        // else, keep polling
                    }

                }, pollFrequency);

            }

        });

    }

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate request to complete
     * then resolves with response from the child iframe.
     */
    makePenpalRequest(requestType, child, request) {

        var p;

        if (requestType === 'rest') {
            console.log('makePenpalRequest');
            console.log(child);
            console.log(child.restRequest);
            p = child.restRequest(request);
        } else if (requestType === 'fetch') {
            p = child.fetchRequest(request);
        } else {
            p = Promise.resolve({
                success: false,
                data: 'LWC_API: Invalid request type: ' + requestType
            });
        }

        console.log('makePenpalRequest');

        return p.then(function (response) {

            console.log(response);

            if (response.success) {
                return response.data;
            }
            throw new Error(response.data);

        });

    }

    disconnectedCallback() { // unrender
        // this.superUnrender();
        // When component unrenders then cleanup penpal
        // resources by destroying the connection and nulling out
        // the helper's cached reference to the connection and child.
        // This ensures that the helper.handleXyzRequest(..) methods
        // wait appropriately for the new parent-child handshake to complete
        // when this component is re-initialized and scripts are loaded.
        if (this._penpal && this._penpal.connection) {
            this._penpal.connection.destroy();
            this._penpal = {};
        }
    }

}
/* eslint-disable @lwc/lwc/no-async-operation */
import { LightningElement, wire, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import PP from '@salesforce/resourceUrl/penpal'
import getVisualforceDomainURL from '@salesforce/apex/LC_VisualforceDomainController.getVisualforceDomainURL';

export default class Api extends LightningElement {

    _penpal = {};
    penpalInitialized = false;
    iframeSrc = '';

    @wire(getVisualforceDomainURL) wiredDomainUrl({ error, data }) {
        if (data) {
            this.iframeSrc = data + '/apex/LC_APIPage';
            console.log(this.iframeSrc);
            this.initializePenpal();
        } else if (error) {
            console.error('LWC_API: error getting Visualforce Domain URL', error);
            this.iframeSrc = undefined;
        }
    }

    connectedCallback() {

        Promise.all([
            loadScript(this, PP)
        ]).then(() => { });
    }

    initializePenpal() {

        let initialized = this.penpalInitialized;
        var self = this;

        // Since the iframe source is calculated asynchronously,
        // we listen to the component's render events and each time
        // check if the iframe is ready, and if so, then we initialize
        // penpal to connect this component to the iframe.
        // Since we only want to do this once, we also set the initialized flag.
        if (!initialized) {

            const container = this.template.querySelector('div');
            const iframeElmt = document.createElement('iframe');
            iframeElmt.src = this.iframeSrc;
            container.appendChild(iframeElmt);

            if (iframeElmt != null) {

                this.penpalInitialized = true;

                // eslint-disable-next-line no-undef
                const connection = Penpal.connectToChild({
                    // The iframe to which a connection should be made
                    iframe: iframeElmt
                });

                this._penpal.connection = connection;

                connection.promise.then(function (child) {
                    // Cache a reference to the child so that we can
                    // use it in the restRequest/fetchRequest methods,
                    // as well as be able to destroy it when this component unrenders.
                    console.log(child);
                    self._penpal.child = child;
                }).catch(function (err) {
                    console.error('LWC_API: Error establishing connection to iframe', err);
                    this.penpalInitialized = false;
                });

            } // else, iframe source is empty, keep waiting
        }
    }

    // Makes a Salesforce REST API request and returns a promise that resolves to the response.

    @api restRequest(request) {

        var self = this;

        var defaultRequest = {
            'method': 'get'
        };

        var defaultHeaders = {
            'Content-Type': 'application/json'
        };

        request = Object.assign({}, defaultRequest, request);
        request.headers = Object.assign({}, defaultHeaders, request.headers);

        return this.getPenpalChild().then(function (child) {
            return self.makePenpalRequest('rest', child, request);
        });
    }

    @api fetchRequest(request) {
        return this.getPenpalChild().then(function (child) {
            return this.makePenpalRequest('fetch', child, request);
        });
    }

    // ------------------------------------------------------------

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate handshake to complete
     * then resolves with reference to the postmate child for making requests.
     */
    getPenpalChild() {

        var child;
        var self = this;
        var timerId;
        var timeout = 10000; // ten seconds
        var pollFrequency = 500; // half a second
        var startTime = new Date().getTime();
        var endTime = startTime + timeout;

        return new Promise(function (resolve, reject) {

            child = self._penpal.child;

            if (child) {

                resolve(child);

            } else {

                // all time values in milliseconds
                timerId = setInterval(function () {

                    child = this._penpal.child;

                    if (child) {

                        // parent-child postmate handshake now complete
                        clearInterval(timerId);
                        resolve(child);

                    } else {

                        // check if we have exceeded our timeout
                        let currentTime = new Date().getTime();
                        if (currentTime > endTime) {
                            clearInterval(timerId);
                            reject('LWC_API: Timeout trying to establish connection to iframe');
                        }
                        // else, keep polling
                    }

                }, pollFrequency);

            }

        });

    }

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate request to complete
     * then resolves with response from the child iframe.
     */
    makePenpalRequest(requestType, child, request) {

        var p;

        if (requestType === 'rest') {
            console.log('makePenpalRequest');
            console.log(child);
            console.log(child.restRequest);
            p = child.restRequest(request);
        } else if (requestType === 'fetch') {
            p = child.fetchRequest(request);
        } else {
            p = Promise.resolve({
                success: false,
                data: 'LWC_API: Invalid request type: ' + requestType
            });
        }

        console.log('makePenpalRequest');

        return p.then(function (response) {

            console.log(response);

            if (response.success) {
                return response.data;
            }
            throw new Error(response.data);

        });

    }

    disconnectedCallback() { // unrender
        // this.superUnrender();
        // When component unrenders then cleanup penpal
        // resources by destroying the connection and nulling out
        // the helper's cached reference to the connection and child.
        // This ensures that the helper.handleXyzRequest(..) methods
        // wait appropriately for the new parent-child handshake to complete
        // when this component is re-initialized and scripts are loaded.
        if (this._penpal && this._penpal.connection) {
            this._penpal.connection.destroy();
            this._penpal = {};
        }
    }

}
deleted 180 characters in body
Source Link
Robs
  • 9.4k
  • 21
  • 119
  • 224

TypeError Errors when using Penpal in Lightning Web Component to call Visualforce page via iframe

The catch is getting an error:

TypeError

With no details.

Reviewing the api.js component, I think the issue is in method makePenpalRequest around line:

p = child.restRequest(request);

Because when I do console.log(childconnection.restRequest) I get undefined, but child is a Promisepromise as expecteddoes not resolve.

/* eslint-disable @lwc/lwc/no-async-operation */
import { LightningElement, wire, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import PP from '@salesforce/resourceUrl/penpal'
import getVisualforceDomainURL from '@salesforce/apex/LC_VisualforceDomainController.getVisualforceDomainURL';

export default class Api extends LightningElement {

    _penpal = {};
    penpalInitialized = false;
    iframeSrc = '';

    @wire(getVisualforceDomainURL) wiredDomainUrl({ error, data }) {
        if (data) {
            this.iframeSrc = data + '/apex/LC_APIPage';
            console.log(this.iframeSrc);
            this.initializePenpal();
        } else if (error) {
            console.error('LWC_API: error getting Visualforce Domain URL', error);
            this.iframeSrc = undefined;
        }
    }

    connectedCallback() {

        Promise.all([
            loadScript(this, PP)
        ]).then(() => { });
    }

    initializePenpal() {

        let initialized = this.penpalInitialized;
        var self = this;

        // Since the iframe source is calculated asynchronously,
        // we listen to the component's render events and each time
        // check if the iframe is ready, and if so, then we initialize
        // penpal to connect this component to the iframe.
        // Since we only want to do this once, we also set the initialized flag.
        if (!initialized) {

            const container = this.template.querySelector('div');
            const iframeElmt = document.createElement('iframe');
            iframeElmt.src = this.iframeSrc;
            container.appendChild(iframeElmt);

            if (iframeElmt != null) {

                this.penpalInitialized = true;

                // eslint-disable-next-line no-undef
                const connection = Penpal.connectToChild({
                    // The iframe to which a connection should be made
                    iframe: iframeElmt
                });

                this._penpal.connection = connection;

                Promiseconnection.all([connection])promise.then(function (child) {
                    // Cache a reference to the child so that we can
                    // use it in the restRequest/fetchRequest methods,
                    // as well as be able to destroy it when this component unrenders.
                    self._penpal.child = child[0];
                }).catch(function (err) {
                    console.error('LWC_API: Error establishing connection to iframe', err);
                    this.penpalInitialized = false;
                });

            } // else, iframe source is empty, keep waiting
        }
    }

    // Makes a Salesforce REST API request and returns a promise that resolves to the response.

    @api restRequest(request) {

        var self = this;

        var defaultRequest = {
            'method': 'get'
        };

        var defaultHeaders = {
            'Content-Type': 'application/json'
        };

        request = Object.assign({}, defaultRequest, request);
        request.headers = Object.assign({}, defaultHeaders, request.headers);

        return this.getPenpalChild().then(function (child) {
            return self.makePenpalRequest('rest', child, request);
        });
    }

    @api fetchRequest(request) {
        return this.getPenpalChild().then(function (child) {
            return this.makePenpalRequest('fetch', child, request);
        });
    }

    // ------------------------------------------------------------

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate handshake to complete
     * then resolves with reference to the postmate child for making requests.
     */
    getPenpalChild() {

        var child;
        var self = this;
        var timerId;
        var timeout = 10000; // ten seconds
        var pollFrequency = 500; // half a second
        var startTime = new Date().getTime();
        var endTime = startTime + timeout;

        return new Promise(function (resolve, reject) {

            child = self._penpal.child;

            if (child) {

                resolve(child);

            } else {

                // all time values in milliseconds
                timerId = setInterval(function () {

                    child = this._penpal.child;

                    if (child) {

                        // parent-child postmate handshake now complete
                        clearInterval(timerId);
                        resolve(child);

                    } else {

                        // check if we have exceeded our timeout
                        let currentTime = new Date().getTime();
                        if (currentTime > endTime) {
                            clearInterval(timerId);
                            reject('LWC_API: Timeout trying to establish connection to iframe');
                        }
                        // else, keep polling
                    }

                }, pollFrequency);

            }

        });

    }

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate request to complete
     * then resolves with response from the child iframe.
     */
    makePenpalRequest(requestType, child, request) {

        var p;

        if (requestType === 'rest') {
            console.log('makePenpalRequest');
            console.log(child);
            console.log(child.restRequest);
            p = child.restRequest(request);
        } else if (requestType === 'fetch') {
            p = child.fetchRequest(request);
        } else {
            p = Promise.resolve({
                success: false,
                data: 'LWC_API: Invalid request type: ' + requestType
            });
        }

        console.log('makePenpalRequest');

        return p.then(function (response) {

            console.log(response);

            if (response.success) {
                return response.data;
            }
            throw new Error(response.data);

        });

    }

    disconnectedCallback() { // unrender
        // this.superUnrender();
        // When component unrenders then cleanup penpal
        // resources by destroying the connection and nulling out
        // the helper's cached reference to the connection and child.
        // This ensures that the helper.handleXyzRequest(..) methods
        // wait appropriately for the new parent-child handshake to complete
        // when this component is re-initialized and scripts are loaded.
        if (this._penpal && this._penpal.connection) {
            this._penpal.connection.destroy();
            this._penpal = {};
        }
    }

}

TypeError when using Penpal in Lightning Web Component to call Visualforce page via iframe

The catch is getting an error:

TypeError

With no details.

Reviewing the api.js component, I think the issue is in method makePenpalRequest around line:

p = child.restRequest(request);

Because when I do console.log(child.restRequest) I get undefined, but child is a Promise as expected.

/* eslint-disable @lwc/lwc/no-async-operation */
import { LightningElement, wire, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import PP from '@salesforce/resourceUrl/penpal'
import getVisualforceDomainURL from '@salesforce/apex/LC_VisualforceDomainController.getVisualforceDomainURL';

export default class Api extends LightningElement {

    _penpal = {};
    penpalInitialized = false;
    iframeSrc = '';

    @wire(getVisualforceDomainURL) wiredDomainUrl({ error, data }) {
        if (data) {
            this.iframeSrc = data + '/apex/LC_APIPage';
            console.log(this.iframeSrc);
            this.initializePenpal();
        } else if (error) {
            console.error('LWC_API: error getting Visualforce Domain URL', error);
            this.iframeSrc = undefined;
        }
    }

    connectedCallback() {

        Promise.all([
            loadScript(this, PP)
        ]).then(() => { });
    }

    initializePenpal() {

        let initialized = this.penpalInitialized;
        var self = this;

        // Since the iframe source is calculated asynchronously,
        // we listen to the component's render events and each time
        // check if the iframe is ready, and if so, then we initialize
        // penpal to connect this component to the iframe.
        // Since we only want to do this once, we also set the initialized flag.
        if (!initialized) {

            const container = this.template.querySelector('div');
            const iframeElmt = document.createElement('iframe');
            iframeElmt.src = this.iframeSrc;
            container.appendChild(iframeElmt);

            if (iframeElmt != null) {

                this.penpalInitialized = true;

                // eslint-disable-next-line no-undef
                const connection = Penpal.connectToChild({
                    // The iframe to which a connection should be made
                    iframe: iframeElmt
                });

                this._penpal.connection = connection;

                Promise.all([connection]).then(function (child) {
                    // Cache a reference to the child so that we can
                    // use it in the restRequest/fetchRequest methods,
                    // as well as be able to destroy it when this component unrenders.
                    self._penpal.child = child[0];
                }).catch(function (err) {
                    console.error('LWC_API: Error establishing connection to iframe', err);
                    this.penpalInitialized = false;
                });

            } // else, iframe source is empty, keep waiting
        }
    }

    // Makes a Salesforce REST API request and returns a promise that resolves to the response.

    @api restRequest(request) {

        var self = this;

        var defaultRequest = {
            'method': 'get'
        };

        var defaultHeaders = {
            'Content-Type': 'application/json'
        };

        request = Object.assign({}, defaultRequest, request);
        request.headers = Object.assign({}, defaultHeaders, request.headers);

        return this.getPenpalChild().then(function (child) {
            return self.makePenpalRequest('rest', child, request);
        });
    }

    @api fetchRequest(request) {
        return this.getPenpalChild().then(function (child) {
            return this.makePenpalRequest('fetch', child, request);
        });
    }

    // ------------------------------------------------------------

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate handshake to complete
     * then resolves with reference to the postmate child for making requests.
     */
    getPenpalChild() {

        var child;
        var self = this;
        var timerId;
        var timeout = 10000; // ten seconds
        var pollFrequency = 500; // half a second
        var startTime = new Date().getTime();
        var endTime = startTime + timeout;

        return new Promise(function (resolve, reject) {

            child = self._penpal.child;

            if (child) {

                resolve(child);

            } else {

                // all time values in milliseconds
                timerId = setInterval(function () {

                    child = this._penpal.child;

                    if (child) {

                        // parent-child postmate handshake now complete
                        clearInterval(timerId);
                        resolve(child);

                    } else {

                        // check if we have exceeded our timeout
                        let currentTime = new Date().getTime();
                        if (currentTime > endTime) {
                            clearInterval(timerId);
                            reject('LWC_API: Timeout trying to establish connection to iframe');
                        }
                        // else, keep polling
                    }

                }, pollFrequency);

            }

        });

    }

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate request to complete
     * then resolves with response from the child iframe.
     */
    makePenpalRequest(requestType, child, request) {

        var p;

        if (requestType === 'rest') {
            console.log('makePenpalRequest');
            console.log(child);
            console.log(child.restRequest);
            p = child.restRequest(request);
        } else if (requestType === 'fetch') {
            p = child.fetchRequest(request);
        } else {
            p = Promise.resolve({
                success: false,
                data: 'LWC_API: Invalid request type: ' + requestType
            });
        }

        console.log('makePenpalRequest');

        return p.then(function (response) {

            console.log(response);

            if (response.success) {
                return response.data;
            }
            throw new Error(response.data);

        });

    }

    disconnectedCallback() { // unrender
        // this.superUnrender();
        // When component unrenders then cleanup penpal
        // resources by destroying the connection and nulling out
        // the helper's cached reference to the connection and child.
        // This ensures that the helper.handleXyzRequest(..) methods
        // wait appropriately for the new parent-child handshake to complete
        // when this component is re-initialized and scripts are loaded.
        if (this._penpal && this._penpal.connection) {
            this._penpal.connection.destroy();
            this._penpal = {};
        }
    }

}

Errors when using Penpal in Lightning Web Component to call Visualforce page via iframe

Reviewing the api.js component, the connection.promise does not resolve.

/* eslint-disable @lwc/lwc/no-async-operation */
import { LightningElement, wire, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import PP from '@salesforce/resourceUrl/penpal'
import getVisualforceDomainURL from '@salesforce/apex/LC_VisualforceDomainController.getVisualforceDomainURL';

export default class Api extends LightningElement {

    _penpal = {};
    penpalInitialized = false;
    iframeSrc = '';

    @wire(getVisualforceDomainURL) wiredDomainUrl({ error, data }) {
        if (data) {
            this.iframeSrc = data + '/apex/LC_APIPage';
            console.log(this.iframeSrc);
            this.initializePenpal();
        } else if (error) {
            console.error('LWC_API: error getting Visualforce Domain URL', error);
            this.iframeSrc = undefined;
        }
    }

    connectedCallback() {

        Promise.all([
            loadScript(this, PP)
        ]).then(() => { });
    }

    initializePenpal() {

        let initialized = this.penpalInitialized;
        var self = this;

        // Since the iframe source is calculated asynchronously,
        // we listen to the component's render events and each time
        // check if the iframe is ready, and if so, then we initialize
        // penpal to connect this component to the iframe.
        // Since we only want to do this once, we also set the initialized flag.
        if (!initialized) {

            const container = this.template.querySelector('div');
            const iframeElmt = document.createElement('iframe');
            iframeElmt.src = this.iframeSrc;
            container.appendChild(iframeElmt);

            if (iframeElmt != null) {

                this.penpalInitialized = true;

                // eslint-disable-next-line no-undef
                const connection = Penpal.connectToChild({
                    // The iframe to which a connection should be made
                    iframe: iframeElmt
                });

                this._penpal.connection = connection;

                connection.promise.then(function (child) {
                    // Cache a reference to the child so that we can
                    // use it in the restRequest/fetchRequest methods,
                    // as well as be able to destroy it when this component unrenders.
                    self._penpal.child = child[0];
                }).catch(function (err) {
                    console.error('LWC_API: Error establishing connection to iframe', err);
                    this.penpalInitialized = false;
                });

            } // else, iframe source is empty, keep waiting
        }
    }

    // Makes a Salesforce REST API request and returns a promise that resolves to the response.

    @api restRequest(request) {

        var self = this;

        var defaultRequest = {
            'method': 'get'
        };

        var defaultHeaders = {
            'Content-Type': 'application/json'
        };

        request = Object.assign({}, defaultRequest, request);
        request.headers = Object.assign({}, defaultHeaders, request.headers);

        return this.getPenpalChild().then(function (child) {
            return self.makePenpalRequest('rest', child, request);
        });
    }

    @api fetchRequest(request) {
        return this.getPenpalChild().then(function (child) {
            return this.makePenpalRequest('fetch', child, request);
        });
    }

    // ------------------------------------------------------------

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate handshake to complete
     * then resolves with reference to the postmate child for making requests.
     */
    getPenpalChild() {

        var child;
        var self = this;
        var timerId;
        var timeout = 10000; // ten seconds
        var pollFrequency = 500; // half a second
        var startTime = new Date().getTime();
        var endTime = startTime + timeout;

        return new Promise(function (resolve, reject) {

            child = self._penpal.child;

            if (child) {

                resolve(child);

            } else {

                // all time values in milliseconds
                timerId = setInterval(function () {

                    child = this._penpal.child;

                    if (child) {

                        // parent-child postmate handshake now complete
                        clearInterval(timerId);
                        resolve(child);

                    } else {

                        // check if we have exceeded our timeout
                        let currentTime = new Date().getTime();
                        if (currentTime > endTime) {
                            clearInterval(timerId);
                            reject('LWC_API: Timeout trying to establish connection to iframe');
                        }
                        // else, keep polling
                    }

                }, pollFrequency);

            }

        });

    }

    /**
     * For internal use.
     * Returns a promise waiting for the parent-child postmate request to complete
     * then resolves with response from the child iframe.
     */
    makePenpalRequest(requestType, child, request) {

        var p;

        if (requestType === 'rest') {
            console.log('makePenpalRequest');
            console.log(child);
            console.log(child.restRequest);
            p = child.restRequest(request);
        } else if (requestType === 'fetch') {
            p = child.fetchRequest(request);
        } else {
            p = Promise.resolve({
                success: false,
                data: 'LWC_API: Invalid request type: ' + requestType
            });
        }

        console.log('makePenpalRequest');

        return p.then(function (response) {

            console.log(response);

            if (response.success) {
                return response.data;
            }
            throw new Error(response.data);

        });

    }

    disconnectedCallback() { // unrender
        // this.superUnrender();
        // When component unrenders then cleanup penpal
        // resources by destroying the connection and nulling out
        // the helper's cached reference to the connection and child.
        // This ensures that the helper.handleXyzRequest(..) methods
        // wait appropriately for the new parent-child handshake to complete
        // when this component is re-initialized and scripts are loaded.
        if (this._penpal && this._penpal.connection) {
            this._penpal.connection.destroy();
            this._penpal = {};
        }
    }

}
deleted 1 character in body
Source Link
Robs
  • 9.4k
  • 21
  • 119
  • 224

Reviewing the api.js component, I thinikthink the issue is in method makePenpalRequest around line:

Reviewing the api.js component, I thinik the issue is in method makePenpalRequest around line:

Reviewing the api.js component, I think the issue is in method makePenpalRequest around line:

Source Link
Robs
  • 9.4k
  • 21
  • 119
  • 224
Loading