collectionCount不在服务中的template / meteor-rxjs中显示值

时间:2017-02-16 15:43:44

标签: angular meteor angular2-meteor

这是我第一次提交给SO,所以,如果有什么不对的地方,请随时告诉我。

现在问我的问题:

我尝试在基于the Angular2 meteor-base boilerplate的简单待办事项中实施服务。

考虑以下代码,我尝试做两件事:

  • 显示一堆待办事项列表(< - This works)
  • 显示.collectionCount()所显示的列表数量(< - 这不起作用)

todolist.service.ts:

import { Injectable } from '@angular/core';
import { Subscription, Observable } from 'rxjs';
import { MeteorObservable, ObservableCursor } from 'meteor-rxjs';

import { Todolist } from '../../../../../both/models/todolist.model';
import { Todolists } from '../../../../../both/collections/todolists.collection';

@Injectable()
export class TodolistService {

  todolistSubscription: Subscription;
  todoLists$: Observable<Todolist[]>;
  numLists$: Observable<number>;

  constructor() {
    this.initListSubscription();
  }

  initListSubscription() {
    if (!this.todolistSubscription) {
      this.todolistSubscription =     MeteorObservable.subscribe("todolists").subscribe(() => {
        // Code to be executed when the subscription is ready goes here
        // This one works
        this.todoLists$ = Todolists.find({}).zone();
        this.todoLists$.subscribe((lists) => {
          console.log(lists);
        });
        // This one doesn't
        this.numLists$ = Todolists.find({}).collectionCount();
        this.numLists$.subscribe((numberOfLists) => {
          console.log(numberOfLists);
        })
      });
    }
  }

  getLists(selector?, options?) {
    // Just in case anyone is wondering what those params are for...
    // return Todolists.find(selector || {}, options || {});
    return this.todoLists$;
  }

  getListsCount() {
    return this.numLists$;
  }

  unsubscribeFromLists() {
    this.todolistSubscription.unsubscribe();
  }

}

这个,我在 app.module.ts 中导入并将其添加到providers-array。

然后,在我的 list.component.ts 中,我使用这样的服务:

import { Component, OnInit } from '@angular/core';
import { TodolistService } from '../../shared/todolist.service'
// + all other relevant imports, e.g. Todolist (the model), Todolists (collection)

@Component({
  selector: 'list-component',
  template,
  styles: [style]
})

export class ListComponent implements OnInit{

  lists$: Observable<Todolist[]>;
  numLists$: Observable<number>;

  constructor(private _todolistService: TodolistService){}

  ngOnInit(){
    // Again, this one works...
    this._todolistService.getLists().subscribe((lists) => {
      console.log(lists);
    });

    // ...and this does not
    this._todolistService.getListsCount().subscribe((number) => {
      console.log(number);
    });

    // This I can also use in my template, see below...
    this.lists$ = this._todolistService.getLists();

    // This one I can't
    this.numLists$ = this._todolistService.getListsCount();
  }

}

todolist.component.html:

在我的模板中,我举例如下:

<!-- This one works... -->
<div *ngFor="let list of lists$ | async">{{list._id}}</div>

<!-- This one doesn't... -->
<span class="badge">{{ numLists$ | async }}</span>

我尝试的事情:

  • 将.zone() - 运算符添加到我的服务中定义的方法,例如
      getListsCount() {
        return this.numLists$.zone();
      }
  • 在initListSubscription() - 服务方法中尝试相同(即添加.zone() - 运算符),我在订阅准备就绪时执行操作
  • 当我致电
  • 时,在我的组件中尝试了相同的操作
    // with the addition of .zone()
    this.numLists$ = this._todolistService.getListsCount().zone();

=====

从我的观点来看,添加.zone()作为一个业余爱好者,这是显而易见的事情。可悲的是,没有任何影响。根据我的理解,这附加了对angulars区域发生的异步任务,基本上与说法相同

constructor(private _zone: NgZone){}

ngOnInit(){
  this._zone.run(() => {
  //...do stuff here that's supposed to be executed in angulars zone
  })
}

例如。

有人可以帮帮我吗?我真的试图了解发生了什么,但我无法理解,为什么我无法从该可观察数据中获得实际的列表数量。

我想知道的另一件事:

如果我要在我的组件中直接执行所有这些操作,并且我希望在添加新的待办事项后我的列表会自动更新,我会执行以下操作以使事情变得无用:

MeteorObservable.subscribe("todolists").subscribe(() => {
  // The additional part that's to be executed, each time my data has changed
  MeteorObservable.autorun().subscribe(() => {
    this.lists$ = Todolists.find({}).zone();
    // with/without .zone() has the same result - that being no result ...
    this.listCount$ = Todolists.find({}).collectionCount();
  });
});

在这里,我也无法理解如何从我的服务中获得反应。我尝试了这个,并且再次为待办事项列表它正在工作,但对于.collectionCount()它没有。

如果有人能指出我在正确的方向,我真的很感激。也许我错过了一些东西,但从理论上讲,我觉得这应该有用,因为我能够显示列表(甚至在我从组件内部做事情时反应性更新)。

提前致谢!

更新

感谢 @ghybs ,我终于找到了一个有效的解决方案。您可以在下面找到最终代码。

todolist.service.ts:

import { Injectable } from '@angular/core';
import { Observable, Subscription, Subject } from 'rxjs';
import { MeteorObservable, ObservableCursor } from 'meteor-rxjs';

import { Todolist, Task } from '../../../../../both/models/todolist.model';
import { Todolists } from '../../../../../both/collections/todolists.collection';

@Injectable()
export class TodolistService {

  todolistSubscription: Subscription;
  todoLists$: ObservableCursor<Todolist> = Todolists.find({});
  numLists$: Observable<number>;
  numLists: number = 0;
  subReady: Subject<boolean> = new Subject<boolean>();

  init(): void {
    if(!this.todolistSubscription){
      this.subReady.startWith(false);
      this.todolistSubscription =     MeteorObservable.subscribe("todolists").subscribe(() => {
        this.todoLists$ = Todolists.find({});
        this.numLists$ = this.todoLists$.collectionCount();
        this.numLists$.subscribe((numberOfLists) => {
          console.log(numberOfLists)
        });
        this.todoLists$.subscribe(() => {
          this.subReady.next(true);
        });
      });      
    }
  }

  isSubscriptionReady(): Subject<boolean> {
    return this.subReady;
  }

  getLists(selector?, options?): ObservableCursor<Todolist> {
    return this.todoLists$;
  }

  getListsCount(): Observable<number> {
    return this.numLists$;
  }

  addList(name: string, description: string): Observable<string> {
    return MeteorObservable.call<string>("addTodoList", name, description);
  }

  addTask(listId: string, identifier: string, description: string, priority: number, start: Date, end: Date): Observable<number> {
    return MeteorObservable.call<number>("addTask", listId, identifier, description, priority, start, end);
  }

  markTask(listId: string, task: Task, index: number) : Observable<number> {
    return MeteorObservable.call<number>("markTask", listId, task, index);
  }

  disposeSubscription() : void {
    if (this.todolistSubscription) {
      this.subReady.next(false);
      this.todolistSubscription.unsubscribe();
      this.todolistSubscription = null;
    }
  }

}

dashboard.component.ts:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { routerTransition } from '../shared/animations'
import { Observable, Subject } from 'rxjs';
import { ObservableCursor } from 'meteor-rxjs';
import { Todolist } from '../../../../both/models/todolist.model';
import { TodolistService } from '../shared/services/todolist.service';

import template from './dashboard.component.html';
import style from './dashboard.component.scss';

@Component({
  selector: 'dashboard',
  template,
  styles: [style],
  animations: [routerTransition()]
})

export class DashboardComponent implements OnInit, OnDestroy {

  todoLists$: ObservableCursor<Todolist>;
  numTodoLists$: Observable<number>;
  numTodoLists: number = 0;

  constructor(private _router: Router, private todolistService: TodolistService) {}

  ngOnInit() {
    this.todolistService.init();
    this.todolistService.isSubscriptionReady().subscribe((isReady) => {
      if(isReady){
        this.todolistService.getListsCount().subscribe((numTodoLists) => {
          this.numTodoLists = numTodoLists;
        });            
      }
    });
  }

  sideNavShown: boolean = true;
  toggleSideNav() {
    this.sideNavShown = !this.sideNavShown;
  }

  ngOnDestroy() {
    this.todolistService.disposeSubscription();
  }

}

dashboard.component.html:

在订阅从服务返回的Observable并接收到值之后,我将值赋给变量并使用它:

<span class="badge badge-accent pull-right">{{ numTodoLists }}</span>

导致

the number of lists displayed

此外,只要我添加新列表,该值就会自动更新 - 所有内容都按预期工作。

非常感谢,特别是 @ghybs ,你很棒。

1 个答案:

答案 0 :(得分:1)

我注意到ObservableCursor(由myCollection.find()返回)需要订阅才能生效。我想我们将其描述为 Cold Observable。

在简单的情况下(比如通过AsyncPipe直接将光标传递给模板),Angular会自己进行订阅(作为async管道进程的一部分)。

因此,在您的情况下,您需要获取find()返回的中间对象的引用,然后才能在其上应用collectionCount(),以便您可以订阅它:

const cursor = Todolists.find({});
this.numLists$ = cursor.collectionCount();
this.numLists$.subscribe((numberOfLists) => {
  console.log(numberOfLists);
});
cursor.subscribe(); // The subscribe that makes it work.

然后,您可以在模板中使用numLists$通过AsyncPipe:

{{ numLists$ | async}}

或者您可以使用在numLists$.subscribe()

中指定的简单中间占位符
private numLists: number;

// ...
this.numLists$.subscribe((numberOfLists) => {
  this.numLists = numberOfLists;
});

并在您的模板中:{{numLists}}

至于反应性,你不需要MeteorObservable.autorun()来包装只重新分配Observable的函数:AsyncPipe将正确使用Observable并做出相应的反应。

findOne()的情况不同,它不会直接返回Observable而是返回对象。