【问题标题】:problem with angular subscribing an observable角度订阅可观察的问题
【发布时间】:2021-11-26 13:59:08
【问题描述】:

我正在尝试创建一个从包含帖子数组的 json 文件中获取帖子的服务。 我已经有一个使用 HttpClient 返回 json 文件内容的服务。 目标是显示完整的帖子内容。

获取 json 文件的服务:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class GetJsonFileService {

  constructor(private http: HttpClient) {}

  getJsonFile(jsonFile: string /* the file path is set by components */){
    return this.http.get(jsonFile,{observe: 'body', responseType: 'json'});
  }
}

通过 id 获取帖子的服务:

import { Injectable } from '@angular/core';
import { GetJsonFileService } from './get-json-file.service';

@Injectable({
  providedIn: 'root'
})
export class GetPostService {

  constructor(private getJsonFileservice: GetJsonFileService) {}

  getPost(id: number) :object{
    var post!: object;
    this.getJsonFileservice.getJsonFile('assets/posts.json').subscribe((data :any)=>{
      post = data["posts"][id];
    });
    return post;
  }

}

显示帖子的组件:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { GetPostService } from '../services/get-post.service';

@Component({
  selector: 'app-post-view',
  templateUrl: './post-view.component.html',
  styleUrls: ['./post-view.component.scss']
})
export class PostViewComponent implements OnInit {

  _post!: object;
  id!: number;

  constructor(private route: ActivatedRoute, private getPost: GetPostService){}

  ngOnInit(){
    var id!: number;
    this.route.params.subscribe(params=>{
      id = params.id;
    });
    this._post = this.getPost.getPost(id);
  }
}

当我尝试在组件模板中显示以下内容时: {{_post.title}}

我收到此错误: Errors while compiling. Reload prevented.

在 vscode 中打字稿告诉我: Property 'title' does not exist on type 'object'

我尝试了@msanford 的解决方案。 在后视图中还有一件事要改变,但我真的不知道该怎么做: posts["posts"][id] 打字稿给我一个错误:

Element implicitly has an 'any' type because expression of type '"posts"' can't be used to index type 'Object'.
  Property 'posts' does not exist on type 'Object'

有什么想法吗?

【问题讨论】:

  • posts["posts"][id] 给你一个访问器错误:数据结构实际上是什么样的,你能分享一下吗?我只是从您的问题中重复了data["posts"][id];
  • (请注意,还有其他几项需要更改。)
  • @msanford ,是的,我通过检查 observable link 的文档发现了几个问题,看来我必须改变很多事情。但你的回答帮助我理解了可观察的东西。谢谢

标签: angular typescript observable


【解决方案1】:

您已将您的帖子声明为对象类型:_post!: object;,而且,属性“标题”存在于类型“object”上。

您应该使用帖子实际包含的内容字段声明an interface or class。举个例子:

interface Post {
  id: number;
  title: string;
  body: string;
  author: string;
  // etc
}

并在你的类型声明中使用它:_post: Post;

也就是说,正如其他人指出的那样,您的服务和组件逻辑需要工作。

您通常不会在服务中使用 .subscribe(),而是在服务的使用者(即组件)中使用 .subscribe()。所以,而不是

// Service
getPost(id: number) :object{
    var post!: object;
    this.getJsonFileservice.getJsonFile('assets/posts.json').subscribe((data :any)=>{
      post = data["posts"][id];
    });
    return post;
  }

简单使用

// Service
getPost = (): Post  => this.getJsonFileservice.getJsonFile<Post[]>('assets/posts.json')

(编辑:我已经通过在调用中添加&lt;Post[]&gt; 以习惯方式将您的服务返回到上面,但是由于我还没有看到您的数据结构,它可能需要一些不同的东西。)

然后订阅你的组件

ngOnInit(){
    let id: number;

    this.route.params.subscribe(
        params => id = params.id,
        error => console.error,
        complete => {
          this.getPost.getPost().subscribe(
            posts => this._post = posts["posts"][id],
            error => console.error
          );
        }
    );    
  }

有一个竞争条件,您可以在路由器向您发送带有 id 的参数之前请求发布,这可能是您添加 non-null-assertion operatorid !: number 的原因,因为编译器告诉你它可能是空的。这是有原因的,不要覆盖它。

【讨论】:

  • 在服务中订阅完成之前不会返回吗?
  • 当然,除此之外。已编辑。
【解决方案2】:

您有一个问题,您在分配之前退回帖子:

post = data["posts"][id];

最好的方法是返回一个 observable 并在您想要提供的值时订阅它。

请务必仔细阅读文档以了解 observables 的工作原理。

https://angular.io/guide/observables

【讨论】:

    【解决方案3】:

    由于 Angular 使用 Typescript,您必须将 _post 标记为 any 或使用您期望的字段为其创建一个 typescript 接口/类型。

    哦,你也可以这样做:

    export class PostViewComponent implements OnInit {
    
      _post!: {
          title: string
      };
      id!: number;
    
      constructor(private route: ActivatedRoute, private getPost: GetPostService){}
    
      ngOnInit(){
        var id!: number;
        this.route.params.subscribe(params=>{
          id = params.id;
        });
        this._post = this.getPost.getPost(id);
      }
    }
    

    【讨论】:

    • 但是route.params.subscribe() 是异步的,所以你不能保证在你调用getPost() 时你已经设置了id。为此,您需要在 oncomplete 处理程序中将 getPost() params.subscribe() (或者,不太常见,在 onnext 中)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 1970-01-01
    • 2018-07-31
    • 2019-11-15
    • 1970-01-01
    • 2016-09-10
    • 1970-01-01
    相关资源
    最近更新 更多