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

JavaScript - The onkeypress event continuously adds text to the end

In my Angular application, I have implemented an input field with the onkeypress event handler to automatically remove any commas entered by the user: <input name="option" ng-model="main.optionToAdd" onkeypress="this.value = this.value.replace(/,/g ...

Converting SHA-256 from Java to JavaScript in an Ionic project using NPM: A step-by-step guide

In Java, I have a code snippet that needs to be converted into JavaScript using the Ionic Framework. I attempted to use Ionic App along with NPM packages like "crypto-js" and "js-sha256", but I was unable to find a suitable solution. String generate ...

Error with the login component in React.js (codesandbox link included)

Hey there! I'm a beginner programmer and I recently dove into a tutorial on creating a Login Form. However, I've run into a few issues along the way. Overview of My Project: The login form I'm working on was built using create-react-app. T ...

Firefox does not support event.dataTransfer.files for drag and drop operations

Currently, I am implementing drag and drop functionality into one of my projects and encountering an issue specifically with Firefox. The code I am using for testing purposes is as follows: document.getElementById("folder_files").addEventListener("drop", ...

Verify if the value of localStorage matches the specified value, then conceal the element

This is my second query and I'm hoping it covers everything. My knowledge of javascript is limited, which has made it difficult for me to get my code working properly. Despite trying various different approaches, I have been unable to resolve the issu ...

Creating a dropdown navigation menu using jQuery

I have been experimenting with creating a customized Drop Down menu using ul li and Jquery, following this helpful Tutorial. Here is the sample HTML Code for testing: <div id="dd" class="wrapper-dropdown-3" tabindex="1"> <span>OS</span> ...

Unlock the innerHTML of a DIV by clicking a button with ng-click in Angular JS

I am curious about something. There is a <div> and a <button> in my code. I am looking to access the inner markup of the <div> within the ng-click function without relying on jQuery. Can you give me some guidance? <div id = text-entry ...

Javascript function that sets the opacity to 0

Hello everyone, I'm new to SO and seeking some guidance on improving my post! I have a function that sets the opacity of an element to 0 in order to clear it. While this works fine, it becomes burdensome when dealing with multiple elements that requi ...

What is the process for triggering a PHP function when a form element is clicked in a webpage?

Currently, I am trying to implement a jQuery colorbox on my webpage which appears over a <select> drop-down list. My goal is to trigger an AJAX call every time a new option is selected from the drop-down. Even though I have written the following cod ...

Sometimes the data-autoplay feature does not function properly in jQueryfullPage.js

I am in the process of developing an HTML page using jQueryfullPage.js. Within this page, I have incorporated 17 videos across 17 different sections. These videos are presented as YouTube iframes with a data-autoplay attribute. Overall, everything is fun ...

Google Maps Circle Radius Functionality Malfunctioning

I've been trying to implement a map scaling feature using a slider in my code but I'm having trouble getting it to work. While the map is displaying correctly, the radius won't update as intended. Any assistance with this would be greatly ap ...

Choose a Different Value for Another HTML Element's Class

Is there a way to preselect an option on another page before it loads? Consider two pages, A and B. If a user clicks a button on page A, I want the default option on page B to be changed to "something" before redirecting them. How can this be achieved s ...

Tips for examining a rate-limited HTTP request function

I am trying to test a naive rate limiter I implemented in Node.js for making HTTP requests to an external service. The service has a limit of 10 requests per second, and I am facing timing issues with my current implementation. Despite being aware of the i ...

Utilize jQuery to invoke an ASP page method from an HTML page

I have a static web page named index.html which includes text boxes for data input. My goal is to send this data to an ASP page called Default.aspx using jQuery/AJAX. I attempted the following: $.ajax({ url: "Default.aspx/GetData", ...

upright scrolling mouse slider

In my project, I have implemented a vertical slider with mousewheel control using SwiperJS from https://swiperjs.com/. Although the slider is working perfectly fine, I am looking to fix the positions while scrolling on the page similar to the example pro ...

Exploring the vueJS canvas life cycle and transitioning between components

Struggling with displaying Vue components that contain a canvas with different behaviors. Looking to switch components using v-if and passing different properties. Here's a small example: Currently, when switching from 'blue' to 'red ...

Can I obtain a dictionary containing the connections between labels and phrases from the classifier?

I've implemented a LogisticRegressionClassifier using the natural library for node: const natural = require('natural'); const classifier = new natural.LogisticRegressionClassifier(); classifier.addDocument('category1', 'sent ...

How to use jQuery to target the second or any desired div element with a specific class

I'm having trouble with something seemingly simple Let's say we are looking for a class called .contentdiv, for example. I am trying to target the second or nth occurrence of the .contentdiv class in a document and retrieve the HTML of that spe ...

Raising the css value for each element that is impacted

I am faced with an infinite number of elements that I need to arrange next to each other. Each element has a class called "box". My goal is to separate each box by 10px increments, meaning the first element will have a left property of 0px, the second 10px ...

The Vue.js class bound to the object remains static even after the object is updated

When I have a number of buttons, generated using the v-for directive, with each button having an initial class based on a string from an object. There is an event that changes this string when a button is clicked. However, the class does not get updated a ...