从’gapi.auth2’导入{GoogleAuth};
我总是有错误:
错误TS2307:找不到模块’gapi.auth2′
我应该使用一些相对目录搜索,例如’../../typings/gapi.auth2’?
或者也许我使用gapi的方式是完全错误的?
谢谢!
npm install --save @types/gapi npm install --save @types/gapi.auth2
这将在node_modules文件夹中安装两个软件包@types/gapi和@types/gapi.auth2,并将配置保存在package.json中.
检查node_modules文件夹以检查它们是否正确安装.如果您的Angular2应用程序名为main-app,您应该看到:
main-app/ node_modules/ @types/ gapi/ gapi.auth2/
编辑tsconfig.json以包含新的gapi和gapi.auth2类型(下面只是一个摘录):
{ "compileOnSave": false,"compilerOptions": { "types": ["gapi","gapi.auth2"] } }
此时我强烈建议您抓咖啡阅读Typescript Module Resolution,您可以直接跳到Node.js如何解析模块:
[…] resolution for a non-relative module name is performed
differently. Node will look for your modules in special folders named
node_modules
. Anode_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 eachnode_modules
until it finds
the module you tried to load.
因此,您不需要在Angular2服务或组件中(或者在您使用gapi或gapi.auth2的任何位置)添加对类型定义的引用.
但是,如果你确实添加了对gapi或gapi.auth2 TypeScript定义的引用,它必须引用使用npm install安装的.ts文件(注意,你必须保持/// oherwise你会收到错误):
/// <reference path="../../node_modules/@types/gapi/index.d.ts" />
路径是相对的,因此您的文件可能会有所不同,具体取决于.ts文件相对于安装TypeScript定义的位置.
无论是添加显式引用还是使用TypeScript的Node模块解析机制,您仍然需要在.ts文件中声明变量,以便Angular2在编译时知道窗口gapi变量.添加声明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 });
Running functions via
zone.run
allows you to reenter Angular zone from
a task that was executed outside of the Angular zone […]
这正是我们想要的,因为对gapi.load的调用离开了Angular区域.省略这一点可以留下非常时髦的结果,这些结果很难调试.
第三,loadClient()返回一个已解析的promise – 允许调用者选择如何处理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服务.在高级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; }); }
<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/导入并将其保留为阻止调用.