在angular 2打字稿中导入gapi.auth2 [英] Import gapi.auth2 in angular 2 typescript
问题描述
我试图在Google Typei中从Google gapi.auth2导入一些类或函数.但是,即使我在类型目录中正确添加了gapi.auth2类型,下面的代码也无法正常工作.
import { GoogleAuth } from 'gapi.auth2';
我总是出错:
Error TS2307: Cannot find module 'gapi.auth2'
我应该使用一些相对目录搜索,例如"../../typings/gapi.auth2"吗?
或者我使用gapi的方式是完全错误的?
谢谢!
要将gapi
和gapi.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/
红色药丸和蓝色药丸方案:
-
如果还没有提供任何类型编译器选项应该不需要将
gapi
或gapi.auth2
显式添加到"types": []
编译器选项中,因为- 默认情况下,编译过程中会包含所有可见的
@types
软件包. -
node_modules/@types
(位于任何封闭文件夹中)中的程序包被视为可见.
- 默认情况下,编译过程中会包含所有可见的
-
但是.如果已经指定了
types
,则 TSConfig参考说明您必须添加gapi
或gapi.auth2
,否则默认情况下将不包括它们.在本部分中,编辑tsconfig.json
以包括新的gapi
和gapi.auth2
类型:
{
"compilerOptions": {
"types": ["jest", "lodash", "gapi", "gapi.auth2"]
}
}
在这一点上,我强烈建议您喝咖啡并阅读打字稿模块分辨率,您可以直接跳至 Node.js如何解析模块:
为非相对模块名称执行[...]解析 完全不同. Node会在名为的特殊文件夹中寻找您的模块
node_modules
.node_modules
文件夹可以与处于同一级别. 当前文件,或目录链中更高的文件.节点会走 目录链,遍历每个node_modules
直到找到 您尝试加载的模块.
因此,您无需在Angular2服务或组件(或使用gapi
或gapi.auth2
的任何位置)中添加对类型定义的引用.
但是,如果确实添加了对gapi
或gapi.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.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 再次用于确保有角区域重新输入.
在实践中,我将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
orgapi.auth2
to the"types": []
compiler option because- By default all visible
@types
packages are included during compilation. - Packages in
node_modules/@types
(of any enclosing folder) are considered visible.
- By default all visible
But. If
types
is already specified, the TSConfig Reference explains that you MUST addgapi
orgapi.auth2
otherwise they will NOT be included by default. In this secnario, edittsconfig.json
to include newgapi
andgapi.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
. 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.
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屋!