在angular 2 typescript

时间:2016-06-29 05:41:17

标签: typescript angular gapi

我尝试在typescript中从Google gapi.auth2导入一些类或函数。但是,即使我在typings目录中正确添加了gapi.auth2类型,代码也永远无法正常工作。

import { GoogleAuth } from 'gapi.auth2';

我总是有错误:

Error TS2307: Cannot find module 'gapi.auth2'

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

或许我使用gapi的方式完全错了?

谢谢!

3 个答案:

答案 0 :(得分:36)

要在Angular2中使用gapigapi.auth,请使用NPM安装类型脚本定义。

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

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

检查您的node_modules文件夹,检查它们是否正确安装。如果您的Angular2应用程序名为main-app,您应该看到:

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

修改tsconfig.json以包含新的gapigapi.auth2类型(以下只是摘录):

{
  "compileOnSave": false,
  "compilerOptions": {
    "types": ["gapi", "gapi.auth2"]
  }
}

此时我强烈建议您抓一杯咖啡阅读Typescript Module Resolution,您可以直接跳到 Node.js如何解析模块

  

[...]执行非​​相对模块名称的解析   不同。 Node将在名为的特殊文件夹中查找您的模块   node_modulesnode_modules文件夹可以与...处于同一级别   当前文件,或目录链中的更高版本。节点将走了   目录链,查看每个node_modules直到找到它   您尝试加载的模块。

因此,您不需要在Angular2服务或组件中添加对类型定义的引用(或者您使用gapigapi.auth2的任何地方)。

但是,如果您确实添加了对gapigapi.auth2 TypeScript定义的引用,则必须引用使用.ts安装的npm install文件(注意,您必须保留///或者你会收到错误):

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

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

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

// 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 documentation中使用其他JavaScript库值得一读,以了解我们在完成所有这些工作后所获得的目标。

接下来,使用您自己的函数加载gapi客户端(可能在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
                });
         });
    });
}

这个功能非常重要,而且有充分的理由......

首先,请注意我们使用配置对象而不仅仅是回调来调用gapi.load。可以使用GAPI reference个州:

  • 库完成时调用的回调函数 加载。
  • 封装各种配置参数的对象 对于这种方法。只需要回调。

使用配置选项,我们可以在加载库超时或只是错误时拒绝 Promise。根据我的经验,加载库比初始化失败更常见 - 这就是配置对象优于回调的原因。

其次,我们正在

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

NgZone.run is documented和州

  

通过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.load后,gapi.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再次使用,以确保重新进入Angular Zone。

在实践中,我将loadClient()initClient()添加到角色服务中。在高级Angular组件中(通常在app组件下方),我在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属性,因为在gapi加载之前,任何一个都会导致Angular 2世界进入。

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

我之前建议通过在/main-app/src/assests文件夹中加载gapi library的本地缩小副本并导入:

来快速保持页面加载速度
    <html>
      <head>
        <script src="assets/api.js"></script>

但是,强烈建议不要这个。 Google可能会更新https://apis.google.com/js/api.js,您的客户将会中断。我被这两次抓住了。最后,最好从//apis.google.com/js/导入并将其保留为阻止调用。

答案 1 :(得分:4)

这是从@Jack's回复修改为使用RxJS库。虽然原始问题要求Angular 2,但我在这里使用Angular 5以防任何人使用更新版本。

  1. 第一步是相同的,用npm下载gapi类型。

    String.Concat
  2. 您需要更新tsconfig.json。如果您遇到问题,可能还需要更新tsconfig.app.json和tsconfig.spec.json。它们继承自tsconfig.json,但是如果你指定类型,我认为它们可能会覆盖基础。下面的代码段:

    StringBuilder
  3. 添加对Google npm install --save @types/gapi npm install --save @types/gapi.auth2 的引用。我把我放在"typeRoots": [ "node_modules/@types" ], "types": [ "gapi", "gapi.auth2" ], "lib": [ "es2017", "dom" ] 。我将platform.jsindex.html遗漏为@Jack推荐。

    async
  4. 接下来创建一个身份验证服务。完整的代码在这里:

    defer
  5. 我们在这里发生了很多事情。首先注意RxJS BehaviorSubjects。我们将使用这些来通知我们的组件变更。我们的<script src="https://apis.google.com/js/platform.js"></script> 函数使用Google的库来获取import { Injectable, NgZone, Output } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { BehaviorSubject } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { User } from './User'; @Injectable() export class AuthenticatorService { public auth2: any; public user$: BehaviorSubject<User> = new BehaviorSubject<User>(null); public isLoggedIn$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); public isLoaded$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); constructor(private zone: NgZone, private http: HttpClient) { } validateToken(token: string): Observable<User> { return this.http.get<User>(`http://yourServer:3000/validationApi/${token}`); } signIn(): void { this.auth2.signIn().then(user => { this.validateToken(user.getAuthResponse().id_token).subscribe(user => { this.zone.run(() => { this.user$.next(user); this.isLoggedIn$.next(true); }); }, (err) => { console.error(err); }); }); }; signOut(): void { this.auth2.signOut().then(() => { this.zone.run(() => { this.isLoggedIn$.next(false); this.user$.next(null); }); }, (err) => { console.error(err); }); } loadAuth2(): void { gapi.load('auth2', () => { gapi.auth2.init({ client_id: 'yourClientId', fetch_basic_profile: true }).then((auth) => { this.zone.run(() => { this.auth2 = auth; this.isLoaded$.next(true); }); }, ); }); } } 个对象。如果您需要有关Google身份验证库的更多信息,请查看their introductiontheir documentation。请注意,一旦我们收到loadAuth2对象,我们就会使用gapi.auth2.GoogleAuththis.zone.run中运行整个功能会导致我出现意外行为。接下来我们使用RxJS GoogleAuth NgZone并将值设置为true。您会在BehaviorSubjectisLoaded$函数中看到类似行为 - 获取结果并在signIn()中运行并更新相应的signOut()

    1. 现在我们有了我们的服务,现在是时候使用它了。我们将创建一个用于登录和注销的组件。代码如下:

      NgZone
    2. 这里最重要的部分是BehaviorSubject实施。这是我们订阅AuthenticatorService的更改并相应地更新视图的地方。

      希望这些步骤可以帮助那些人在他们的项目中设置gapi.auth2。

答案 2 :(得分:0)

编译器将尝试查找环境模块声明。 所以你应该安装:

https://github.com/retyped/gapi.auth2-tsd-ambient