Angular 4:订阅Observable

时间:2017-12-10 15:33:37

标签: javascript angular typescript

我正在开发一个项目,需要加载和选择客户(不是用户,更像客户)。

不幸的是,我似乎无法订阅我在组件中加载的Observable。我已经尝试了我可以在网上找到的所有可能的解决方案,但也许我对Angular缺乏经验阻止我找到合适的解决方案。

现在正在发挥作用:
- 在选择框中加载客户端
- 在选择框中将客户'id的值赋予选项
- 将客户端的id发送到client.service并将所选客户端保存为Observable

所以我唯一的问题是组件没有注意到client.service中Observables的变化。

这是我的client.service.ts:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { ApiService } from '../api.service';
import { Client } from './client';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';

@Injectable()
export class ClientService {

    clients$: Observable<Client[]>;
    client: Client;
    selectedClient$: Observable<Client[]>;

    constructor(private api: ApiService) {
        this.clients$ = this.getAll();
    }

    public getAll(): Observable<Client[]> {
        return this.api.get<Client[]>('clients');
    }

    public setSelectedClient(clientId: number) {
        this.clients$ = this.getAll();
        if (clientId == null) {
            // Do nothing
        } else {
            this.selectedClient$ = this.clients$.map(arr =>
            { return arr.find(client => client.id === clientId) });
        }
    }

    public getSelectedClient() : Observable<Client> {
        return this.selectedClient$;
    }

}

我的组件,带有注释位以显示解决问题的一些尝试:

import { Component, OnInit } from '@angular/core';
import { Observable } from "rxjs/Observable";
import { ClientService } from "../client/client.service";
import { Client } from "../client/client";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';

@Component({
    selector: 'app-left-menu',
    templateUrl: './left-menu.component.html',
    styleUrls: ['./left-menu.component.css']
})

export class LeftMenuComponent implements OnInit {

    selectedClient$ : Observable<Client>;
    client: Client = new Client();
    clients$ : Observable<Client[]>;

    constructor(private clientService: ClientService) {

    }

    ngOnInit() {
        // this.clientService.getSelectedClient().subscribe(selectedClient$ => {
        //     this.selectedClient$ = Observable.of(selectedClient$);
        // });
        // //
        // this.clientService.getAll().subscribe(clients$ => {
        //     this.clients$ = Observable.of(clients$);
        // });

        this.selectedClient$ = this.clientService.getSelectedClient();
        this.clients$ = this.clientService.getAll();
    }

    public setSelectedClient(clientId: number) {
        this.clientService.setSelectedClient(clientId);
    }

}

我用来显示和选择客户端的html部分:

 <select #selectClient [ngModel]="selectedClient$ | async" 
   (ngModelChange)="setSelectedClient(selectClient.value)">
        <option *ngFor="let client of clients$ | async" [value]="client.id">
            {{ client.firstName }}
            {{ client.preposition }}
            {{ client.lastName }}
        </option>
 </select>

 <!-- Displaying selected client -->
 <h2 *ngIf="selectedClient$">{{(selectedClient$ | async)?.firstName}}
 </h2>

如果有人能帮我解决这个问题,那将非常感谢,谢谢!

2 个答案:

答案 0 :(得分:1)

<强> Demo

使用[ngValue]代替[value],它允许您将客户端对象本身绑定到[ngModel]指令,我还使用Subject轻松地通知其他订阅者新选择的客户:

client.service.ts:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

import { Client } from './client';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';
@Injectable()
export class ClientService {

    clients$: Observable<Client[]>;
    client: Client;
    selectedClient$: Subject<Client> = new Subject<Client>();

    //...
    public setSelectedClient(client: Client) {
        if (client)
            this.selectedClient$.next(client);
    }
    //...
}

component.ts:

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

import { ClientService } from './client.service';
import { Client } from './client'


@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

    selectedClient : Client;
    client: Client = new Client();
    clients$ : Observable<Client[]>;

    constructor(private clientService: ClientService) {

    }

    ngOnInit() {
        //...
        this.clients$ = this.clientService.getAll();
        this.clientService.selectedClient$.subscribe(console.log)
    }

    setSelectedClient(client : Client){
      this.selectedClient = client;
      this.clientService.setSelectedClient(client)
    }
}

模板:

<select #selectClient [ngModel]="selectedClient" 
(ngModelChange)="setSelectedClient($event)">
     <option *ngFor="let client of clients$ | async" [ngValue]="client">
         {{ client.firstName }}
         {{ client.preposition }}
         {{ client.lastName }}
     </option>
</select>

答案 1 :(得分:0)

您的问题来自于您将clients$selectedClient$可观察属性分配给LeftMenuComponent的方式。

setSelectedClient()上的ClientService方法将更改服务的属性。但是,您的LeftMenuComponent ngOnInit()只会对属性进行一次分配,而setSelectedClient()的更改不会反映在LeftMenuComponent上。

要使其工作,您无需将属性重新分配到组件。您可以直接在模板中绑定服务的属性,如下所示:

 <select #selectClient [ngModel]="clientService.selectedClient$ | async" 
   (ngModelChange)="setSelectedClient(selectClient.value)">
        <option *ngFor="let client of clientService.clients$ | async" [value]="client.id">
            {{ client.firstName }}
            {{ client.preposition }}
            {{ client.lastName }}
        </option>
 </select>

另一个可能更清晰的选择是简单地在组件上使用getter属性而不是赋值,如下所示:

export class LeftMenuComponent implements OnInit {

    client: Client = new Client();
    get selectedClient$(): Observable<Client> {
        return this.clientService.selectedClient$;
    }
    get clients$(): Observable<Client> {
        return this.clientService.client$;
    }

    constructor(private clientService: ClientService) {

    }

    ngOnInit() {
        // init things, but no assignment needed now
    }