Contentful logo

Contentful Community

Error while using contenful hosting with gsheets api

Hey guys I have a simple UI extension that talks to google sheets that works on my machine and works if I self host but errors out when I try to use contenful hosting. Error: Cannot read property ‘init’ of undefined, the section of code that is failing is gapi.client.init({, relevant section of code:

select-rate-plan-extension
<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize_button" style="display: none;">Google Sheets Authorize</button>
<button id="signout_button" style="display: none;">Google Sheets Sign Out</button>

<pre id="error" style="white-space: pre-wrap;color:red;font-weight: bold;"></pre>
<select id="rates" style="display: none;">
    <option>Select a rate plan</option>
</select>

<script type="text/javascript">
    // Client ID and API key from the Developer Console
    const CLIENT_ID = '<ID HERE>';
    const API_KEY = '<KEY HERE>';

    // Array of API discovery doc URLs for APIs used by the quickstart
    const DISCOVERY_DOCS = ["https://sheets.googleapis.com/$discovery/rest?version=v4"];

    // Authorization scopes required by the API; multiple scopes can be
    // included, separated by spaces.
    const SCOPES = "https://www.googleapis.com/auth/spreadsheets.readonly";

    const authorizeButton = document.getElementById('authorize_button');
    const signoutButton = document.getElementById('signout_button');
    const rates = document.getElementById('rates');

    /**
     *  On load, called to load the auth2 library and API client library.
     */
    function handleClientLoad() {
        gapi.load('client:auth2', initClient);
    }

    /**
     *  Initializes the API client library and sets up sign-in state
     *  listeners.
     */
    function initClient() {
        gapi.client.init({
            apiKey: API_KEY,
            clientId: CLIENT_ID,
            discoveryDocs: DISCOVERY_DOCS,
            scope: SCOPES
        }).then(function () {
            // Listen for sign-in state changes.
            gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);

            // Handle the initial sign-in state.
            updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
            authorizeButton.onclick = handleAuthClick;
            signoutButton.onclick = handleSignoutClick;
        }, function (error) {
            appendPre(JSON.stringify(error, null, 2));
        });
    }

    /**
     * Handlers for contentful
     */
    window.contentfulExtension.init(function (extension) {
        // Getting current value from contentful
        const rates = document.getElementById('rates');
        const value = extension.field.getValue();
        rates.value = value;

        // Hander for field value changes, send changes to contentful.
        rates.onchange = (newValue) => {
            if (newValue) {
                extension.field.setValue(newValue);
            } else {
                extension.field.removeValue();
            }
        };

        // Handler for external field value changes (e.g. when multiple authors are working on the same entry).
        extension.field.onValueChanged((newValue) => { rates.value = value; });

        // Resizes the extension iframe whenever the size of the document changes.
        extension.window.startAutoResizer()
    });
</script>

<script async defer src="https://apis.google.com/js/api.js" onload="this.onload=function(){};handleClientLoad()"
    onreadystatechange="if (this.readyState === 'complete') this.onload()"></script>

sounds like the google apis JS isn’t being loaded completely before you try to call init. See this for details and possible solutions. Here’s a key portion:

we observed this issue when injecting the api.js script asynchronously, in some browsers. We’re not sure yet what the root cause of the bug is, but in the meantime you can use a setTimeout before the gapi.client.init call or not injecting dynamically the api.js script.