在angular 2打字稿中导入gapi.auth2 [英] Import gapi.auth2 in angular 2 typescript

查看:82
本文介绍了在angular 2打字稿中导入gapi.auth2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在Google Typei中从Google gapi.auth2导入一些类或函数.但是,即使我在类型目录中正确添加了gapi.auth2类型,下面的代码也无法正常工作.

import { GoogleAuth } from 'gapi.auth2';

我总是出错:

Error TS2307: Cannot find module 'gapi.auth2'

我应该使用一些相对目录搜索,例如"../../typings/gapi.auth2"吗?

或者我使用gapi的方式是完全错误的?

谢谢!

解决方案

要将gapigapi.auth与Angular2一起使用,请使用NPM安装类型脚本定义.

npm install --save @types/gapi
npm install --save @types/gapi.auth2

这将安装两个软件包, @ types/gapi @ types/gapi.auth2 node_modules文件夹中,并将配置保存在package.json.

检查您的node_modules文件夹以检查它们是否正确安装.如果您的Angular应用程序称为"main-app",则应该看到:

main-app/
  node_modules/
    @types/
      gapi/
      gapi.auth2/

红色药丸和蓝色药丸方案:

  • 如果还没有提供任何类型编译器选项应该不需要将gapigapi.auth2显式添加到"types": []编译器选项中,因为

    1. 默认情况下,编译过程中会包含所有可见的@types软件包.
    2. node_modules/@types(位于任何封闭文件夹中)中的程序包被视为可见.

  • 但是.如果已经指定了types,则 TSConfig参考说明您必须添加gapigapi.auth2,否则默认情况下将不包括它们.在本部分中,编辑tsconfig.json以包括新的gapigapi.auth2类型:

{
   "compilerOptions": {
     "types": ["jest", "lodash", "gapi", "gapi.auth2"]
    }
}

在这一点上,我强烈建议您喝咖啡并阅读打字稿模块分辨率,您可以直接跳至 Node.js如何解析模块:

为非相对模块名称执行

[...]解析 完全不同. Node会在名为的特殊文件夹中寻找您的模块 node_modules. node_modules文件夹可以与处于同一级别. 当前文件,或目录链中更高的文件.节点会走 目录链,遍历每个node_modules直到找到 您尝试加载的模块.

因此,您无需在Angular2服务或组件(或使用gapigapi.auth2的任何位置)中添加对类型定义的引用.

但是,如果确实添加了对gapigapi.auth2 TypeScript定义的引用,则它必须引用使用npm install安装的.ts文件(注意,必须保持///的其他方式)出现错误):

/// <reference path="../../node_modules/@types/gapi/index.d.ts" />

该路径是相对的,因此您的路径可能会有所不同,具体取决于.ts文件相对于TypeScript定义的安装位置.

无论是添加显式引用还是使用TypeScript的Node模块解析机制,您仍需要在.ts文件中声明变量,以便Angular2在编译时了解window gapi变量.将declare var gapi: any;添加到您的.ts文件中,但不要 not 将其放置在类定义中.我把我的放在任何进口以下:

// You may not have this explicit reference.
/// <reference path="../../node_modules/@types/gapi/index.d.ts" />
import { NgZone, Injectable, Optional } from '@angular/core';
declare var gapi: any;

查看定义本身( https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/gapi/index.d.ts ),仅导出函数.相反,这些接口是实现细节,因此不导出它们,并且对命名空间之外的代码也不可见.

TypeScript文档

与其他JavaScript库一起使用 a>值得一读,以了解我们将通过所有这些工作获得什么.

接下来,从您自己的函数(可能在Angular Service中)加载gapi客户端:

 loadClient(): Promise<any> {
     return new Promise((resolve, reject) => {
         this.zone.run(() => {
                gapi.load('client', {
                    callback: resolve,
                    onerror: reject,
                    timeout: 1000, // 5 seconds.
                    ontimeout: reject
                });
         });
    });
}

此功能很简单,这就是原因...

首先,请注意,我们正在调用 gapi.使用配置对象而不是回调加载.可以使用 GAPI参考状态:

  • 库完成后调用的回调函数 加载中.
  • 一个封装各种配置参数的对象 对于这种方法.仅需要回调.

使用配置选项可以使我们在加载库超时或错误时拒绝.以我的经验,加载库比初始化失败更多,这就是为什么配置对象比回调更好的原因.

第二,我们在其中包装gapi.load

this.zone.run(() => {
  // gapi.load
});

已记录NgZone.run 和状态

通过zone.run运行功能可让您从以下位置重新进入Angular区域 在Angular区域之外执行的任务[...]

这正是我们想要的,因为对gapi.load的调用离开了Angular区域.省略此选项可能会导致非常时髦的结果,难以调试.

第三,loadClient()返回已解决的承诺-允许调用方选择如何处理gapi.load.例如,如果我们的loadClient方法属于Angular服务apiLoaderServce,则组件可以使用ngOnInit加载gapi:

ngOnInit(): void {
    this.apiLoaderService.loadClient().then(
        result => this.apiLoaded = true,
        err => this.apiLoaded = false
    );
}

一旦调用gapi.loadgapi.client将准备就绪,您应该使用它通过API密钥,OAuth客户端ID,作用域和API发现文档来初始化JavaScript客户端:

initClient(): Promise<any> {
    var API_KEY = // Your API key.
    var DISCOVERY_DOC = // Your discovery doc URL.
    var initObj = {
        'apiKey': API_KEY,
        'discoveryDocs': [DISCOVERY_DOC],
    };

    return new Promise((resolve, reject) => {
        this.zone.run(() => {
            gapi.client.init(initObj).then(resolve, reject);
        });
    });
}

通知我们的朋友 NgZone.run 再次用于确保有角区域重新输入.

在实践中,我将loadClient()initClient()添加到Angular Service中.在高级Angular组件(通常仅在app-component下方)中,我在ngOnInit中加载并初始化:

ngOnInit(): void {
    this.apiLoaderService.loadClient().then(
        result => {
            this.apiLoaded = true;
            return this.apiLoaderService.initClient()
        },
        err => {
            this.apiFailed = true;
        }
    ).then(result => {
        this.apiReady = true;
    }, err => {
        this.apiFailed = true;
    });
}

最后,您需要将gapi脚本文件添加到文件中.

<html>
  <head>
    <script src="https://apis.google.com/js/api.js"></script>

请勿使用async defer属性,因为它们会导致Angular世界在加载gapi库之前进入.

<!-- This will not work. -->
<html>
  <head>
    <script async defer src="https://apis.google.com/js/api.js"></script>

我以前建议通过加载 gapi的本地缩小副本来保持页面加载速度更快库 /main-app/src/assests文件夹中,然后导入:

    <html>
      <head>
        <script src="assets/api.js"></script>

但是,我强烈建议不这样做. Google可能会更新 https://apis.google.com/js/api.js 您的客户会破产.我被这两次吸引住了.最后,最好是从//apis.google.com/js/导入并将其保留为阻塞调用.

I tried to import some classes or function from Google gapi.auth2 in typescript. But below code never works even I correctly added the gapi.auth2 types in typings directory.

import { GoogleAuth } from 'gapi.auth2';

I always got error:

Error TS2307: Cannot find module 'gapi.auth2'

Shall I use some relative directory searching, such as '../../typings/gapi.auth2'?

Or maybe the way I am using the gapi is totally wrong?

Thanks!

解决方案

To use gapi and gapi.auth with Angular2, install the type script definitions using NPM.

npm install --save @types/gapi
npm install --save @types/gapi.auth2

This will install two packages, @types/gapi and @types/gapi.auth2 to the node_modules folder and save the configuration in package.json.

Inspect your node_modules folder to check they install correctly. If your Angular app is called "main-app", you should see:

main-app/
  node_modules/
    @types/
      gapi/
      gapi.auth2/

The red pill and blue pill scenario:

  • If have have NOT provided any types compiler options there should be no need to explicitly add gapi or gapi.auth2 to the "types": [] compiler option because

    1. By default all visible @types packages are included during compilation.
    2. Packages in node_modules/@types (of any enclosing folder) are considered visible.

  • But. If types is already specified, the TSConfig Reference explains that you MUST add gapi or gapi.auth2 otherwise they will NOT be included by default. In this secnario, edit tsconfig.json to include new gapi and gapi.auth2 types:

{
   "compilerOptions": {
     "types": ["jest", "lodash", "gapi", "gapi.auth2"]
    }
}

At this point I highly recommend grabbing a coffee and reading Typescript Module Resolution, you can skip straight to How Node.js resolves modules:

[...] resolution for a non-relative module name is performed differently. Node will look for your modules in special folders named node_modules. A node_modules folder can be on the same level as the current file, or higher up in the directory chain. Node will walk up the directory chain, looking through each node_modules until it finds the module you tried to load.

For this reason, you shouldn't need to add a reference to the type definitions in your Angular2 Service or Component (or wherever you're using gapi or gapi.auth2).

However, if you do add a reference to the gapi or gapi.auth2 TypeScript definitions, it must reference the .ts file installed using npm install (note, you must keep the /// oherwise you'll get an error):

/// <reference path="../../node_modules/@types/gapi/index.d.ts" />

The path is relative, so yours may differ depending on where your .ts file is relative to where you installed the TypeScript definitions.

Whether you added an explicit reference or used TypeScript's Node module resolution mechanism, you still need to declare your variables in your .ts file so Angular2 knows about the window gapi variable at compile time. Add declare var gapi: any; to your .ts file but do not place it within a class definition. I put mine just below any imports:

// You may not have this explicit reference.
/// <reference path="../../node_modules/@types/gapi/index.d.ts" />
import { NgZone, Injectable, Optional } from '@angular/core';
declare var gapi: any;

Looking at the definitions themselves (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/gapi/index.d.ts), only functions are exported. Conversely, the interfaces are implementation details, so they are left un-exported and will not be visible to code outside the namespace.

Working with Other JavaScript Libraries in TypeScript documentation is worth reading to understand what we're getting with all this work.

Next, load the gapi client from your own function (possibly in an Angular Service):

 loadClient(): Promise<any> {
     return new Promise((resolve, reject) => {
         this.zone.run(() => {
                gapi.load('client', {
                    callback: resolve,
                    onerror: reject,
                    timeout: 1000, // 5 seconds.
                    ontimeout: reject
                });
         });
    });
}

This function is non-trivial, here's why...

Firstly, note we're calling gapi.load with a configuration object and not a callback. The GAPI reference states either can be used:

  • A callback function that is called when the libraries have finished loading.
  • An object encapsulating the various configuration parameters for this method. Only callback is required.

Using a configuration option allows us to reject the Promise when loading the library times-out, or just errors. In my experience, loading the library fails more often than initializing it - which is why configuration object is better than just a callback.

Secondly, we're wrapping gapi.load in

this.zone.run(() => {
  // gapi.load
});

NgZone.run is documented and states

Running functions via zone.run allows you to reenter Angular zone from a task that was executed outside of the Angular zone [...]

This is exactly what we want since the call to gapi.load leaves the Angular zone. Omitting this can leave to very funky results that can be hard to debug.

Thirdly, loadClient() returns a promise that is resolved - allowing the caller to choose how they handle gapi.load. For example if our loadClient method belonged to an Angular service, apiLoaderServce, a component may use ngOnInit to load gapi:

ngOnInit(): void {
    this.apiLoaderService.loadClient().then(
        result => this.apiLoaded = true,
        err => this.apiLoaded = false
    );
}

Once gapi.load has been called, gapi.client will be ready and you should use it to initializes the JavaScript client with you API key, OAuth client ID, scope, and API discovery document(s):

initClient(): Promise<any> {
    var API_KEY = // Your API key.
    var DISCOVERY_DOC = // Your discovery doc URL.
    var initObj = {
        'apiKey': API_KEY,
        'discoveryDocs': [DISCOVERY_DOC],
    };

    return new Promise((resolve, reject) => {
        this.zone.run(() => {
            gapi.client.init(initObj).then(resolve, reject);
        });
    });
}

Notice our friend NgZone.run is used once again to ensure the Angular Zone is re-entered.

In practice, I add loadClient() and initClient() to an Angular Service. In a high-level Angular component (usually just below the app-component) I load and initialize in ngOnInit:

ngOnInit(): void {
    this.apiLoaderService.loadClient().then(
        result => {
            this.apiLoaded = true;
            return this.apiLoaderService.initClient()
        },
        err => {
            this.apiFailed = true;
        }
    ).then(result => {
        this.apiReady = true;
    }, err => {
        this.apiFailed = true;
    });
}

Lastly, you need to add the gapi script file to your file.

<html>
  <head>
    <script src="https://apis.google.com/js/api.js"></script>

You must not use the async or defer attributes since they will cause the Angular world to enter before the gapi library has loaded.

<!-- This will not work. -->
<html>
  <head>
    <script async defer src="https://apis.google.com/js/api.js"></script>

I previously suggested keeping page-load speeds fast by loading a local, minified copy of the gapi library in the /main-app/src/assests folder and importing:

    <html>
      <head>
        <script src="assets/api.js"></script>

However, I strongly recommend not doing this. Google may update https://apis.google.com/js/api.js and your client will break. I have been caught-out by this twice. In the end it was better just to import from //apis.google.com/js/ and keep it as a blocking call.

这篇关于在angular 2打字稿中导入gapi.auth2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆