Angular使用TypeScript进行HTTP GET时出现错误:http.get(...).map在[null]中不是函数。

348

我在Angular中遇到了HTTP的问题。

我只想从服务器获取一个JSON列表并在视图中显示它。

服务类

import {Injectable} from "angular2/core";
import {Hall} from "./hall";
import {Http} from "angular2/http";
@Injectable()
export class HallService {
    public http:Http;
    public static PATH:string = 'app/backend/'    

    constructor(http:Http) {
        this.http=http;
    }

    getHalls() {
           return this.http.get(HallService.PATH + 'hall.json').map((res:Response) => res.json());
    }
}

HallListComponent 中,我从服务调用 getHalls 方法:

export class HallListComponent implements OnInit {
    public halls:Hall[];
    public _selectedId:number;

    constructor(private _router:Router,
                private _routeParams:RouteParams,
                private _service:HallService) {
        this._selectedId = +_routeParams.get('id');
    }

    ngOnInit() {
        this._service.getHalls().subscribe((halls:Hall[])=>{ 
            this.halls=halls;
        });
    }
}

然而,我遇到了一个异常:

在 [null] 中,TypeError: this.http.get(...).map 不是一个函数

hall-center.component

import {Component} from "angular2/core";
import {RouterOutlet} from "angular2/router";
import {HallService} from "./hall.service";
import {RouteConfig} from "angular2/router";
import {HallListComponent} from "./hall-list.component";
import {HallDetailComponent} from "./hall-detail.component";
@Component({
    template:`
        <h2>my app</h2>
        <router-outlet></router-outlet>
    `,
    directives: [RouterOutlet],
    providers: [HallService]
})

@RouteConfig([
    {path: '/',         name: 'HallCenter', component:HallListComponent, useAsDefault:true},
    {path: '/hall-list', name: 'HallList', component:HallListComponent}
])

export class HallCenterComponent{}

应用组件

import {Component} from 'angular2/core';
import {ROUTER_DIRECTIVES} from "angular2/router";
import {RouteConfig} from "angular2/router";
import {HallCenterComponent} from "./hall/hall-center.component";
@Component({
    selector: 'my-app',
    template: `
        <h1>Examenopdracht Factory</h1>
        <a [routerLink]="['HallCenter']">Hall overview</a>
        <router-outlet></router-outlet>
    `,
    directives: [ROUTER_DIRECTIVES]
})

@RouteConfig([
    {path: '/hall-center/...', name:'HallCenter',component:HallCenterComponent,useAsDefault:true}
])
export class AppComponent { }

tsconfig.json

{
  "compilerOptions": {
    "target": "ES5",
    "module": "system",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  },
  "exclude": [
    "node_modules"
  ]
}

http.get 不是返回一个 Promise 吗? - bmm6o
2
@bmm6o 新的 Http 服务返回一个可观察对象。 - Brocco
2
我遇到了一个几乎相同的问题,试图将一个项目从Angular2 beta-17迁移到最终版本。对我来说问题在于我的IDE,使用VS 2015,更新3。TypeScript语言服务扩展仍然是1.8.36,而ng2快速入门指南(在我写这篇文章时)使用的是“typescript”:“^2.0.2”。通过Extensions和Updates升级TS lang. service为我解决了这个问题。当安装该更新时,我遇到了这个SO答案,它得出了相同的结论。 - Eric Lease
对于phpstorm/webstorm,使用项目库更新typescript版本也解决了问题。我按照这个SO答案的步骤进行操作:https://dev59.com/7Y3da4cB1Zd3GeqPylrY#31608934 - Sebas
19个回答

541

我认为你需要导入这个:

import 'rxjs/add/operator/map'

如果您想在observable中使用更多的方法,可以使用以下代码。

警告:这将导入所有50个以上的操作符,并将它们添加到您的应用程序中,从而影响包的大小和加载时间。
import 'rxjs/Rx';

更多细节请查看此问题


3
还有哪些错误仍然存在?您可以在stackoverflow上提出一些具体的问题;-) 这个问题也可能会对您有所帮助:https://dev59.com/q1sW5IYBdhLWcg3w7KtL#34450948。 - Thierry Templier
2
从Angular 2 RC 0开始,这不再是必需的。 - Onur Yıldırım
4
@OnurYıldırım,使用rc.4版本时,除非我做错了什么,否则仍需要进行此导入。 - Joseph Gabriel
18
请不要使用'import 'rxjs/Rx';',因为它会导入所有内容,而rxjs往往比较大。根据需要逐个引入运算符。 - Drag0
2
Angular 2与rxjs: 5.0.0-beta.12在此。 我仍然需要import 'rxjs/add/operator/do'...虽然我们不再需要为.map()这样做。但是这有助于我的.do()情况,让我意识到我需要特别导入它。谢谢!我给你点赞 :) - MrCroft
显示剩余4条评论

84

一些背景...新发布的服务器通信开发指南(终于)讨论/提到/解释了这个问题:

RxJS库非常大。对于我们构建生产应用并将其部署到移动设备上而言,大小很重要。我们应该仅包含实际需要的功能。

因此,Angular在rxjs/Observable模块中公开了一个简化版本的Observable,该版本几乎不包括任何操作符,包括我们想在此处使用的map方法等。

添加我们需要的操作符取决于我们自己。我们可以逐个添加每个操作符,直到我们拥有一个完全针对我们需求调整的自定义Observable实现。

因此,就像@Thierry已经回答的那样,我们只需要引入我们需要的操作符即可:

import 'rxjs/add/operator/map';
import 'rxjs/operator/delay';
import 'rxjs/operator/mergeMap';
import 'rxjs/operator/switchMap';

或者,如果我们懒一点,我们可以导入完整的运算符集合。 警告:这将向您的应用程序包中添加所有 50 多个运算符,并影响加载时间

import 'rxjs/Rx';

2
导入 'rxjs/Rx'; 真是太神奇了,我所有的烦恼都瞬间消失了。我简直不敢相信这不是 Rxjs/Angular2 的全球标配。谢谢! - JimB
但它经常不会进行代码检查,因为默认情况下它是被列入黑名单的导入。创建一个新的Angular CLI项目,你就会看到。我自己喜欢这种便利,但在我在纽约工作的地方,惯例是不这样做的。 - Tim Consolazio

24

rxjs 5.5版本开始,你可以使用管道操作符

import { map } from 'rxjs/operators';

import 'rxjs/add/operator/map';有什么问题?

当我们使用这种方法时,map 操作符将被打补丁到 observable.prototype 并成为该对象的一部分。

如果以后您决定从处理可观察流的代码中删除 map 操作符,但未能删除相应的导入语句,则实现 map 的代码仍然是 Observable.prototype 的一部分。

当捆绑程序尝试消除未使用的代码( tree shaking)时,它们可能决定保留 Observable 中 map 操作符的代码,即使在应用程序中没有使用。

解决方案 - 管道操作符

管道 操作符是纯函数,不会打补丁到 Observable。 您可以使用 ES6 导入语法 import { map } from "rxjs/operators" 导入操作符,然后将它们包装成一个函数 pipe(),该函数接受可变数量的参数,即可链接的操作符。

像这样:

getHalls() {
    return this.http.get(HallService.PATH + 'hall.json')
    .pipe(
        map((res: Response) => res.json())
    );
}

是的,现在使用 Rxjs 7+ 版本时,建议使用管道操作符来使用 pipe,很好,我发现所有以前的答案都过时了,没有更新到当前版本的 Angular 和 Rxjs。 - Rebai Ahmed

17

使用 Angular 5,RxJS 导入方式得到了改进。

不再需要:

import 'rxjs/add/operator/map';

我们现在可以

import { map } from 'rxjs/operators';

使用这种方法,现在在您的构建中只会包含所使用的运算符(在此示例中为 map),而以前它会包含 rxjs 库中的所有运算符。请查看下面的链接获取更多详细信息。https://loiane.com/2017/08/angular-rxjs-imports/ - Hiren Shah

15

可以直接使用 Observable.subscribe 方法。

@Injectable()
export class HallService {
    public http:Http;
    public static PATH:string = 'app/backend/'    

    constructor(http:Http) {
        this.http=http;
    }

    getHalls() {
    // ########### No map
           return this.http.get(HallService.PATH + 'hall.json');
    }
}


export class HallListComponent implements OnInit {
    public halls:Hall[];
    / *** /
    ngOnInit() {
        this._service.getHalls()
           .subscribe(halls => this.halls = halls.json()); // <<--
    }
}

6
这不是一个很高效的方法。例如,如果有多个订阅者,他们都需要在数据上运行map函数,而不是只在服务中运行一次。我认为原帖作者的方法是正确的,只是没有加载正确的模块来使用它。 - Evan Plaice

5
对于 Angular 5 及以上版本,更新后的导入行如下:
import { map } from 'rxjs/operators';

或者

import { map } from 'rxjs/operators';

这些版本完全支持可管道运算符,因此您可以轻松使用.pipe()和.subscribe()。

如果您使用的是Angular 2版本,则以下代码应该完全正常:

import 'rxjs/add/operator/map';

或者

import 'rxjs/add/operators/map';

如果您仍然遇到问题,则必须使用以下方法:
import 'rxjs/Rx';

我不建议你直接使用它,因为它会增加加载时间,其中包含大量运算符(有用和无用的)不符合行业规范,所以请确保首先尝试使用上述导入行,并且如果不起作用,然后可以考虑使用rxjs / Rx。


4

我有一个解决这个问题的方案。

安装这个软件包:

npm install rxjs@6 rxjs-compat@6 --save

然后导入这个库。

import 'rxjs/add/operator/map'

最后重新启动您的Ionic项目。

ionic serve -l

4

您使用的这个map不是JavaScript中的.map(),而是Angular中作用于Observables的Rxjs map函数。

所以在这种情况下,如果您想对结果数据使用map,则需要导入它。

map(project: function(value: T, index: number): R, thisArg: any): Observable<R>将给定的project函数应用于源Observable发出的每个值,并将生成的值作为Observable发出。

所以只需像这样导入:

import 'rxjs/add/operator/map';

3

Angular版本6 "0.6.8" rxjs版本6 "^6.0.0"

此解决方案适用于:

  "@angular-devkit/core": "0.6.8",
  "rxjs": "^6.0.0"

众所周知,Angular每天都在不断发展,因此每天都会有许多变化。这个解决方案是为Angular 6和RxJS 6设计的。
首先,要使用http,您应该从中导入它: 最后,您需要在app.module.ts中声明HttpModule。

import { Http } from '@angular/http';

您需要将HttpModule添加到Ngmodule的imports中。
  imports: [
    HttpModule,
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(appRoutes)
  ],

要使用地图,您首先应导入管道:

import { pipe } from 'rxjs';

第三步,您需要导入 map 函数:

import { map } from 'rxjs/operators';

您需要在管道中像这个例子一样使用map:
 constructor(public http:Http){  }

    getusersGET(){
        return this.http.get('http://jsonplaceholder.typicode.com/users').pipe(
         map(res => res.json()  )  );
    }

祝你好运,希望那个能完美工作!


2

Angular 6 - 只有 import 'rxjs/Rx' 对我起了作用


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接