【问题标题】:Correct way to retrieve single item rxjs in Angular2在Angular2中检索单个项目rxjs的正确方法
【发布时间】:2016-06-28 13:50:44
【问题描述】:

首先,我是 Angular2 Rxjs(以及一般的 observables)和 ES6/ES2015 的新手,所以问题可能是其中任何一个的结果。

我在 Angular 2 中有一个服务,它创建了一个商店,并且有一个 loadAll 和 load 单个函数以及一个暴露的 getter。它目前连接到虚拟数据,但最终将是一个 http 请求(在下面注释掉)

import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs/Rx';
import { Http, Response } from '@angular/http';

export interface Article {
  id: number;
  title: string;
  content: string;
  author: string;
}

@Injectable()
export class ArticleService {
  private _articles$: Subject<Article[]>;
  private baseUrl: string;
  private dataStore: {
    articles: Article[]
  };

  private dummyData = [
      {
        id: 1,
        title: 'Title 1',
        content: 'content 1',
        author: 'author 1'
      },
      {
        id:2,
        title: 'Title 2',
        content: 'content 2',
        author: 'author 1'
      }
    ];

  constructor(private http: Http) {
    this.baseUrl = 'http://localhost:3000'
    this.dataStore = { articles: [] };
    this._articles$ = <Subject<Article[]>>new Subject();
  }
  get articles$(){
    return this._articles$.asObservable();
  }

  loadAll(){
    //Observable.fromPromise(Promise.resolve(this.dummyData))
    this.http.get(`${this.baseUrl}/articles`)
      // map for actual http requests
    .map(response => response.json())
    .subscribe(data => {
      //debugger;
      this.dataStore.articles = data;
       // Push a new copy of our article list to all Subscribers.
      this._articles$.next(this.dataStore.articles)
    }, error => console.log('Could not load Articles'));
  }

  load(id: any) {
    Observable.fromPromise(Promise.resolve(this.dummyData[id-1]))
    //this.http.get(`${this.baseUrl}/articles/${id}`)
      // map for actual http requests
    //.map(response => response.json())
    .subscribe(data => {
      let notFound = true;

      this.dataStore.articles.forEach((item, index) => {
        if (item.id === data.id) {
          this.dataStore.articles[index] = data;
          notFound = false;
        }
      });

      if (notFound) {
        this.dataStore.articles.push(data);
      }

      this._articles$.next(this.dataStore.articles);
    }, error => console.log('Could not load todo.'));
  }
}

然后我可以在组件中按预期显示列表:

import { Component, OnInit } from '@angular/core';
import { ROUTER_DIRECTIVES } from '@angular/router';
import { ArticleService } from '../shared';

@Component({
  moduleId: module.id,
  selector: 'app-home',
  directives :[
    ROUTER_DIRECTIVES
  ],
  providers:[
    ArticleService
  ],
  template: `
    <h1>My List</h1>
    <ul>
      <li *ngFor="let item of items$ | async; let i = index">
        <a [routerLink]="['/article', item.id]" > {{item.title}} </a>
      </li>
    </ul>
  `,
  styleUrls: ['home.component.css']
})
export class HomeComponent implements OnInit {
  items$ = null;
  constructor(private articleService: ArticleService) {
    this.articleService = articleService;
  }

  ngOnInit() {
    this.items$ = this.articleService.articles$;
    this.articleService.loadAll();
  }
}

但是如何在 Item 组件中显示单个 Item,这是我尝试过的:

import { Component, OnInit } from '@angular/core';
import { ROUTER_DIRECTIVES, ActivatedRoute, Router } from '@angular/router';
import { ArticleService } from '../shared';

@Component({
  moduleId: module.id,
  selector: 'app-article',
  providers: [
    ArticleService
  ],
  template: `
      <h1>{{item$.title}}</h1>
      <p>{{item$.content}}</p>
  `,
  styleUrls: ['article.component.css']
})
export class ArticleComponent implements OnInit {
  private sub: any;
  private id: number;
  private item$ = null;
  //  private all = null;

  constructor(private route: ActivatedRoute,
    private router: Router, private articleService: ArticleService) {
    this.route = route;
    this.articleService = articleService;
  }

  ngOnInit() {
    this.sub = this.route.params.subscribe(params => {
      let id = +params['id']; // (+) converts string 'id' to a number
      //this.service.getHero(id).then(hero => this.hero = hero);
      this.id = id
    });

    this.articleService.load(this.id);
    //this.all = this.articleService.articles$;
    this.articleService.articles$
    .reduce((c, n) => {
      return n["id"] === this.id ? n : c;
     })
     .subscribe(data => {
        this.item$ = JSON.stringify(data);
      });
  }

}

我也试过过滤:

this.articleService.articles$
    .filter((n) => {
        n["id"] === this.id ;

       })
      .subscribe(data => {
        this.item$ = data;
      });

抛出错误Argument of type '(n: Article[]) =&gt; void' is not assignable to parameter of type '(value: Article[], index: number) =&gt; boolean'

并选择单个项目:

.subscribe(data => {
    this.item$ = data[0];
});

最令人困惑的事情是如果我这样做this.item$ = JSON.stringify(data[0]);当我在模板中引用 {{item$}} 时,它将显示为普通字符串:{"id":1,"title":"Title 1","content":"content 1","author":"author 1"}[object Object] 没有 stringify 但是当我引用 {{item$.id}} 或任何其他属性时,我收到错误 TypeError: Cannot read property 'id' of null

我注意到的其他可能相关的问题包括:

  • 我无法在 reduce 或 filter 函数中停止调试器
  • 在几个场合(虽然现在很难重现)如果引用 n.id 而不是 n["id"] 我收到错误,因为它不存在,但 n["id"] 确实存在

【问题讨论】:

  • 问题是什么?

标签: angular ecmascript-6 rxjs


【解决方案1】:

尝试Elvis 运算符?. 内插。

{{item$?.id}}

如果item$ 是虚假的(未定义、null 等),它将无法访问id 成员。

item$ 将是 null,直到您收到回复。

注意:您实际上可以在filter 和类似函数中停止调试器。只需将其设置为多行即可。 此外,当箭头函数为多行时,您可以发送console 消息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-14
    • 2017-06-06
    • 2011-02-21
    • 2015-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多