【问题标题】:Using GeoFire in an Angular2 App在 Angular2 应用程序中使用 GeoFire
【发布时间】:2016-08-20 12:53:15
【问题描述】:

我正在尝试在我的 Angular2 (RC5) 应用中设置 GeoFire。我已经用 npm 安装了 geofire 和 firebase 并配置了 systemjs 来导入它。这是我的 package.json:

{
  "name": "MyProject",
  "version": "0.1.0",
  "scripts": {
    "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
    "lite": "lite-server",
    "postinstall": "typings install",
    "tsc": "tsc",
    "tsc:w": "tsc -w",
    "typings": "typings"
  },
  "license": "ISC",
  "dependencies": {
    "@angular/common": "^2.0.0-rc.5",
    "@angular/compiler": "^2.0.0-rc.5",
    "@angular/core": "^2.0.0-rc.5",
    "@angular/forms": "0.3.0",
    "@angular/http": "2.0.0-rc.5",
    "@angular/platform-browser": "^2.0.0-rc.5",
    "@angular/platform-browser-dynamic": "^2.0.0-rc.5",
    "@angular/router": "3.0.0-rc.1",
    "@angular/router-deprecated": "2.0.0-rc.2",
    "@angular/upgrade": "2.0.0-rc.5",
    "angular2-in-memory-web-api": "0.0.15",
    "bootstrap": "^3.3.6",
    "core-js": "^2.4.0",
    "firebase": "^3.3.0",
    "geofire": "^4.1.1",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.6",
    "systemjs": "0.19.27",
    "zone.js": "^0.6.12"
  },
  "devDependencies": {
    "concurrently": "^2.0.0",
    "lite-server": "^2.2.0",
    "typescript": "^1.8.10",
    "typings": "^1.0.4"
  }
}

还有我的 systemjs 配置文件:

  var map = {
    'app':                        'app', // 'dist',
    '@angular':                   'node_modules/@angular',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    'rxjs':                       'node_modules/rxjs',
    'firebase':                   'node_modules/firebase',
    'geofire':                    'node_modules/geofire',
  };
  // packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                        { main: 'main.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
    'firebase':                   { main: 'firebase.js', defaultExtension: 'js' },
    'geofire':                    { main: 'dist/geofire.js', defaultExtension: 'js' },
  };

最后我尝试在我的 app.component.ts 文件中导入 geofire,如下所示:

import * as GeoFire from "geofire";

但是在运行 npm start 转译和启动服务器时,得到以下错误:

➜  MyProject npm start

> MyProject@0.1.0 start /Users/max/Development/myproject
> tsc && concurrently "npm run tsc:w" "npm run lite"

app/app.component.ts(3,26): error TS2307: Cannot find module 'geofire'.

npm ERR! Darwin 15.5.0
npm ERR! argv "/usr/local/Cellar/node/5.10.1/bin/node" "/usr/local/bin/npm" "start"
npm ERR! node v5.10.1
npm ERR! npm  v3.8.3
npm ERR! code ELIFECYCLE
npm ERR! MyProject@0.1.0 start: `tsc && concurrently "npm run tsc:w" "npm run lite" `
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the MyProject@0.1.0 start script 'tsc && concurrently "npm run tsc:w" "npm run lite" '.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the MyProject package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     tsc && concurrently "npm run tsc:w" "npm run lite"
npm ERR! You can get information on how to open an issue for this project with:
npm ERR!     npm bugs MyProject
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!     npm owner ls MyProject
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/max/Development/MyProject/npm-debug.log

这是我的 tsconfig:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  }
}

其余项目文件基于此处找到的 angular2 快速入门:

https://angular.io/guide/quickstart

有人知道如何让 tsc 找到模块并成功转译吗?

谢谢。最大。

【问题讨论】:

  • 你是从命令行运行 tsc 吗?这不适用于 SystemJS,您必须使用 github.com/frankwallis/plugin-typescript 并在运行时进行转译(systemjs 会为您完成)或使用 systemjs-builder。
  • @artem 从命令行运行 tsc 和运行 npm start 时都会显示错误消息。我已将完整的错误消息和完整的 package.json 内容添加到原始问题中。
  • GeoFire 有分型吗?如果有,你安装了吗?
  • 不,没有打字,但据我所知,你可以在不打字的情况下使用模块,不是吗?

标签: angular firebase systemjs geofire


【解决方案1】:

该快速入门教程缺少一个重点:SystemJS 和 TypeScript 是两个独立的工具,每个工具都有自己的配置。

将 geofire 添加到 systemjs 配置对 typescript 模块分辨率没有影响。看到这个导入

import * as GeoFire from "geofire";

tsc 尝试在通常找到 javascript 模块类型的通常位置查找 geofire.ts,并给出错误,因为它不存在。

有三种不同的方法可以解决这个问题。

无论如何,systemjs.config.js 中的geofire 包配置必须将其格式声明为global,如下所示:

'geofire': {
    main: 'dist/geofire.js', 
    defaultExtension: 'js', 
    meta: {'dist/geofire.js': {format: 'global'}} 
 },

第一个解决方案是自己为 geofire 创建最简单的类型声明,使用环境模块。

在app文件夹中创建geofire.d.ts文件:

declare namespace geofire {
    function GeoFire(firebaseRef: any): void;
}

declare module 'geofire' {
    export = geofire;
}

这对于 import 语句的编译和对 typecheck 的调用来说已经足够了:

   var geoFire = new GeoFire(firebaseRef);

typescript 2.0 的注意事项

对于 typescript 2.0,上面的代码不起作用。您必须使用特定于打字稿的import = 语法

import GeoFire = require('geofire');

并将geofire.d.ts 中的定义更改为:

declare module 'geofire' {
    function GeoFire(firebaseRef: any): void;
    export = GeoFire;
}

解决此问题的另一种可能方法是为 SystemJS 使用 plugin,它使用 TypeScript API 调用 typescript 并挂钩到其模块解析,以便考虑 SystemJS 配置。使用该插件,该导入将在浏览器中运行时工作,但从命令行调用 tsc 仍然会给出相同的错误。

第三种方式是使用SystemJS API导入geofire,而不是app.component.ts中的import语句:

import { Component } from '@angular/core';

declare var SystemJS : any;
var GeoFirePromise = SystemJS.import('geofire');

@Component({
  selector: 'my-app',
  template: '<h1>My First Angular 2 App</h1>'
})

export class AppComponent {

    constructor() {  // or wherever you need to use geofire

        GeoFirePromise.then(function(GeoFire: any) {
            console.log(typeof GeoFire);
        });
    }

}

请注意,SystemJS.import 返回一个必须加载 geofire.js 的承诺,因此您必须在任何地方使用它

    GeoFirePromise.then(function(GeoFire: any) {

如果不方便,你可以声明全局 GeoFire 变量

declare var GeoFire: any;

并使用 HTML 中的脚本标签加载 geofire.js,如this post on ionic forum 中所述。然后,您不需要将 geofire 添加到 systemjs.config.js。

【讨论】:

  • 感谢@artem 提供如此清晰而全面的答案。您帮助消除了我对这两种工具的一些误解。我去了 geofire.d.ts 路线,因为我认为它是最干净的。最大
  • 你好@artem。解决方案一直在工作,直到我创建了一个 firebase.d.ts 脚本,现在 tsc 显示此错误:app/app.component.ts(37,19): error TS2351: Cannot use 'new' with an expression that type lacks a调用或构造签名——当我尝试这样做时:var geoFire = new GeoFire(firebaseRef);。即使删除了 firebase.d.ts 文件,错误仍然存​​在。知道为什么吗?
  • 抱歉,我还不知道如何正确地进行类型检查。到目前为止,我发现的解决方法是通过 var X: any = GeoFire; 绕过类型检查。 var geofire = new X(...);
  • 好的,感谢您的解决方法。与此同时,我发现如果我使用以下命令导入 tsc 可以成功转换: import { GeoFire } from "geofire";并使用此geofire.d.ts:声明模块'geofire'{export class GeoFire{ref;构造函数(firebaseRef:任何); } }。但在运行时我收到此错误:TypeError: geofire_1.GeoFire is not a constructor
  • 是的,到目前为止,我发现将导入与运行时导出的内容相匹配的唯一方法是完全按照答案中的内容进行操作;您甚至不需要在模块中声明任何内容,因为它无论如何都不会进行类型检查。如果我发现更好的答案,我会再次更新答案
【解决方案2】:

请注意,我仍然不是 TypeScript 专家,这是我第一次涉足类型定义等,但经过漫长的工作日后,我似乎找到了一些可行的方法(至少对于我的设置而言)。我正在使用 Ionic 2 RC2、Angular2 2.1.1、Geofire 4.1.2。构建使用 webpack。我在我的应用程序目录中创建了geofire.d.ts,内容如下:

declare module 'geofire' {

    type EventType = 'ready' | 'key_entered' | 'key_exited' | 'key_moved';

    interface GeoQueryCriteria {
        center: number[];
        radius: number;
    }

    interface GeoQueryUpdateCriteria {
        center?: number[];
        radius?: number;
    }

    interface GeoCallbackRegistration {
        cancel();
    }

    interface GeoQuery {
        center(): number[];
        radius(): number;
        updateCriteria(criteria: GeoQueryUpdateCriteria);
        on(eventType: EventType, callback: (key:string, location: number[], distance: number) => void): GeoCallbackRegistration;
        cancel();
    }

    class GeoFire {
        constructor(ref: any);
        ref(): any;
        set(key: string, loc: number[]): Promise<void>;
        get(key: string): Promise<number[]>;
        remove(key: string): Promise<void>;
        query(criteria: GeoQueryCriteria): GeoQuery;
        static distance(location1: number[], location2: number[]);  
    }

    export = GeoFire;
}

最后在我的服务/页面中,我可以像这样导入 GeoFire:

import GeoFire from 'geofire';

注意 exact 导入的语法没有花括号 {}。我尝试了很多方法让它以不同的方式工作,但我认为 javascript 的编写方式,这就是它必须完成的方式。我找到了消除编译错误但在运行时失败的方法('没有命名的构造函数......'),或者它会在编译中识别构造函数但不是类型。

我只为公共 API 创建它,因此如果您出于某种原因尝试扩展 GeoFire,请注意可能与此定义文件中未公开的其他方法发生名称冲突。

如果这对您有帮助,请告诉我,我会将其贡献给 DefinitelyTyped,这样其他人就不必经历我(当然还有其他拥有 340 次以上浏览量的人)所经历的痛苦。

【讨论】:

  • 我收到一个错误:TypeError: geofire_1.default is not a constructor。有什么办法解决吗?
  • 你是把这个文件加到 tsconfig 还是 angular.json 里?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-05
  • 2017-03-09
相关资源
最近更新 更多