Building a Vue application with customized authentication for the Google Calendar API in JavaScript

Struggling with understanding the migration from GAPI to GIS in Vue? Google has provided a helpful document on this topic: https://developers.google.com/identity/oauth2/web/guides/migration-to-gis#server-side-web-apps

Attempting to implement code from the GIS section of the migration doc, I encountered an error:

Uncaught TypeError: Cannot read properties of undefined (reading 'requestAccessToken')

<template>
    <h1>Google Identity Services Authorization Token model</h1>
    <button @click="getToken()">Get access token</button><br><br>
    <button @click="loadCalendar()">Load Calendar</button><br><br>
    <button @click="revokeToken()">Revoke token</button>

</template>

<script>
    var client
    var access_token

    
    export default {
        
        methods:{
            initClient() {
                client = google.accounts.oauth2.initTokenClient({
                client_id: '...',
                scope: 'https://www.googleapis.com/auth/calendar.readonly \
                        https://www.googleapis.com/auth/contacts.readonly',
                callback: (tokenResponse) => {
                    access_token = tokenResponse.access_token;
                },
                });
            },
            getToken() {
                client.requestAccessToken();
            },
            revokeToken() {
                google.accounts.oauth2.revoke(access_token, () => {console.log('access token revoked')});
            },
            loadCalendar() {
                var xhr = new XMLHttpRequest();
                xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
                xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
                xhr.send();
            }
    }
    }
</script>

In the index.html file, make sure to include:

<script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>

Looking for assistance to resolve this issue. Any help would be appreciated!

Answer №1

Make sure to follow these steps in order to properly integrate the GSI script into your Vue component:

  • Avoid including the script tag in your index.html
  • Instead, load the GSI script directly within your Vue component
  • (Optional) Implement a check in your component to display buttons only when the client is loaded

Here's the revised code with comments for clarity:

<template>
  <h1>Google Identity Services Authorization Token model</h1>

  <!-- Restrict button usage until client is fully loaded -->
  <template v-if="!client">
    <button @click="getToken()">Get access token</button><br /><br />
    <button @click="loadCalendar()">Load Calendar</button><br /><br />
    <button @click="revokeToken()">Revoke token</button>
  </template>
  <span v-else>Loading Google APIs</span>
</template>

<script>
export default {
  // Transfer variables to data
  data() {
    return {
      client: null,
      access_token: null,
    };
  },
  methods: {
    initClient() {
      this.client = google.accounts.oauth2.initTokenClient({
        client_id: "...",
        scope:
          "https://www.googleapis.com/auth/calendar.readonly \
           https://www.googleapis.com/auth/contacts.readonly",
        callback: (tokenResponse) => {
          this.access_token = tokenResponse.access_token;
        },
      });
    },
    getToken() {
      this.client.requestAccessToken();
    },
    revokeToken() {
      google.accounts.oauth2.revoke(this.access_token, () => {
        console.log("access token revoked");
      });
    },
    loadCalendar() {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", "https://www.googleapis.com/calendar/v3/calendars/primary/events");
      xhr.setRequestHeader("Authorization", "Bearer " + this.access_token);
      xhr.send();
    },
    
    // Load the script here
    loadGsiScript() {
      const gsiScript = document.createElement("script");
      gsiScript.src = "https://accounts.google.com/gsi/client";
      gsiScript.async = true;
      gsiScript.defer = true;

      // Initialize client after script is loaded
      gsiScript.onload = this.initClient;
      document.head.appendChild(gsiScript);
    },
  },

  // Trigger loading of GSI script upon mounting
  mounted() {
    this.loadGsiScript();
  },
};
</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What's the best approach: Backbone-Relational discovery or retrieval?

Although the model caching feature in Backbone-Relational is effective, loading a simple model securely requires considerable code. For example: // Attempt to locate a model in the cache this.model = MyModel.find({id:id}); if(this.model){ // Model re ...

VueJS with Vuetify: Issue with draggable cards in a responsive grid

I am currently working on creating a gallery that allows users to rearrange images. To test this functionality, I am using an array of numbers. It is important that the gallery is responsive and displays as a single column on mobile devices. The issue I ...

What causes a globally declared array to remain empty in the global scope even after data is pushed to it within a callback function?

Initially, I set an empty array as the value for the global variable artistURLs. Then, within the Cheerio .each() iterator method, I push strings (stored in the local variable artistURL) into the artistURLs array. var request = require('request&apos ...

JavaScript/jQuery boolean data type

In my current coding project, I am dealing with a scenario where the user has the option to download either a specific "Slice" of a pie chart or the entire chart. When a user clicks on a slice, it sends a variable named source to indicate which slice was ...

Struggling to align elements in React/JS/M?

My challenge is aligning the elements in the middle of my page. I aim to have 3 elements per row, with n rows depending on the number of "VideoBox" components. It's crucial that these elements fit within the lettering of the P R O F E S S I O N A L ...

Having trouble with a Reactjs Facebook login library - update the componentClicked function to be async

Currently, I am working on incorporating Facebook login into my React application using Redux. Within my loginUser.js file, the "FacebookLogIn" component appears as follows: <FacebookLogin appId="375026166397978" autoLoad={true} fields="name, ...

Angular/JS encountered a premature end of program unexpectedly

I am taking my first steps in the world of web development with Angular (and JavaScript in general). I have been trying to rewrite some basic and common examples using Angular. One thing I attempted was to display a simple message using data binding. This ...

Gathering feedback from a webpage using JavaScript and jQuery

I have been experimenting with different tools such as Selenium and BeautifulSoup in an attempt to scrape the contents of the following website/pages: . Specifically, I am looking to extract the reviews/sections which are dynamically loaded by JS, jQuery ...

Tips for transferring date values in an ajax request to a web application handler

I am currently working on plotting a graph between two dates using Google Charts. My goal is to send date values to the controller, which is implemented with webapp2. However, I am facing difficulties in figuring out how to send date values to the controll ...

Sharing information with Django through Ajax and jQuery

I'm facing an issue with posting html5 geolocation data to the django admin backend. I've managed to display the latitude and longitude on the html page but can't seem to successfully submit the data to django. I suspect there might be a pro ...

Navigate the conversation within the dialog without affecting the content below

Is it possible to have a dialog with scrollable content on top of a page that is also scrollable, but when trying to scroll inside the dialog using the mouse wheel, only the dialog body scrolls and not the page below it? What approach can be used to accom ...

Guide to setting up a Cordova and TypeScript project using the command line interface

For my mobile application development, I rely on Cordova and execute cordova create MyApp in the command-line to initiate a new project. I am familiar with JavaScript but now require TypeScript for my project. Please assist me in setting up a Cordova pro ...

Is it possible to use both "npm install --global" and "--save" simultaneously?

I'm curious if it is practical to use both the --global and --save parameters in the npm install command simultaneously. For instance: npm install gulp -g -s From my understanding, since there is no package.json in the npm system folder, I assume th ...

Troubles encountered when cascading val(), text(), and data()

Here is my JavaScript/jQuery code: $select.append($("<option />") .val(this.id) .text(this.text) .data('name', this.name) .data('isstorage', this.isstorage)); Although it successfully assigns values to t ...

What is causing the issue with sending data accurately to the Controller?

I am encountering an issue while trying to send data from a Vue component through an API to a controller. The function in the component looks like this (where `this.selectedItems` is an array of objects): async getEntries() { try { this.projectEntrie ...

Implement a personalized version of a caching strategy handler in Workbox/Vue

Context: In the development of my SPA PWA using Vue.js, I have incorporated a remote PostgreSQL database accessible through PostgREST over HTTP. Combining Workbox Service Worker and IndexedDB, I have successfully implemented local storage for database tab ...

transferring data from JavaScript on the client side to Express on the server side

Using JavaScript, I have created the HTML DOM that you see in the code below. Now, my goal is to send the value of an input created in JavaScript to the server side. The server is built with node.js and express framework. Once the value is sent to the serv ...

The importance of using clearTimeOut in debounce function

Could you explain the importance of using clearTimeout in debounce function? Let's take a look at the code below: const saveInput = (name) => { console.log('saveinput ', name); } const debounce = (fn, timeout = 3000) => { ...

How is it possible to encounter a Javascript unexpected token ] error within an HTML code?

While working on my project, I encountered a JavaScript error in the console of Chrome. The error message stated "Unexpected token ]" and it was pointing to a specific line of raw HTML code. I am puzzled about what could be causing this issue. Unfortunatel ...

JavaScript can retrieve the default page name that is hidden within the browser's URL

When a URL is entered without a specific file name at the end, such as "www.google.com," the server typically serves a default file like "index.html" or "default.aspx." In this scenario, if the browser displays "www.google.com," I am looking to extract the ...