【问题标题】:Angular 2 Observable for custom componentAngular 2 Observable 用于自定义组件
【发布时间】:2017-04-13 04:50:20
【问题描述】:

我已经制作了一个组件 - 我们称之为 CUSTOMSELECT,它需要一些输入,例如下拉选项列表、默认选择的选项、标签等。

我想在某些页面上多次使用这个组件。例如,我需要一个 CUSTOMSELECT 员工,并且在页面上的嵌套组件中,我需要一个 CUSTOMSELECT 部门和另一个角色。

好的,到目前为止一切都很好。每个实例都会正确显示所需的数据。

现在我想在 CUSTOMSELECT 的实例中发布选择的任何更改,以便我可以订阅它。例如,当一个员工被选中时,我想刷新页面的其余部分。

我为此创建了一项服务。我将 CUSTOMSELECT 中的单击事件链接到将所选值发布到服务的函数。在父组件中,我订阅了“主题”,以便我可以更改员工。

这行得通。

但是,如果我随后使用嵌套组件的部门 CUSTOMSELECT 更改选择,则会发布此值,并且父组件的员工订阅者会获取更改并对其进行处理。不是我想要发生的。一点也不!

那么,我怎样才能告诉观察者只关注他们感兴趣的消息呢?即,我怎样才能发布到适当的观察者:员工实例不需要知道部门实例已经改变了值,等等。

感谢您的帮助 - 到目前为止一整天都在尝试解决这个问题。

这样查看组件树可能会有所帮助:

employee-roles.component
  select-dropdown.component (employees)
  employee-roles-form.component
    select-dropdown.component (departments)
    select-dropdown.component (roles)

这是代码。

select-dropdown.component.html

<div style="margin-top: 12px" class="input-group">
<span class="input-group-addon" style="height:30px">{{label}}</span>
<input type="text" class="form-control" style="height:30px" 
(change)="filterTextChanged($event.target.value)" placeholder="Enter a 
value to search for ..." />
</div>
<div style="margin-top: -6px" class="input-group">
<select class="form-control" (click)="publishChange($event)" size="5">
<option>Please select an option ...</option>
<option *ngFor="let item of list" 
  [selected]="item.dropdownValue==selected"
  [value]="item.dropdownValue"
  >{{item.dropdownText}}</option>
</select>
</div>

select-dropdown.component.ts

import { Component, EventEmitter, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { SelectDropdownService } from 'common/services/select-dropdown.service';
import { SelectModule } from 'ng2-select';
import * as _ from 'lodash';

@Component({
  inputs: ['enablefilter', 'label', 'list', 'selected'],
  outputs: ['filterTextChanged'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => SelectDropdownComponent),
      multi: true
    }
  ],
  selector: 'app-select-dropdown',
  templateUrl: './select-dropdown.component.html',
  styleUrls: ['./select-dropdown.component.scss']
})
export class SelectDropdownComponent implements ControlValueAccessor {

  propagateChange = (_: any) => { };
  private enablefilter: boolean;
  private label       : string;
  private list        : string[];
  private originalList: string[];
  private value       : any = {};

  /**
   * TODO: Need to review the methods in this class which have been copied from the web
   */
  constructor(
    public selectDropdownService: SelectDropdownService
  ) {
    this.enablefilter = false; // please do not change as would affect many forms!
  }

  /**
   * Let the parent component know that an option has been selected
   * @param event 
   */
  public publishChange(event) {
    // let target = event.target;
    let selectedValue = event.target.value;
    this.propagateChange(selectedValue);
    this.selectDropdownService.sendSelectedValue(selectedValue);
  }

select-dropdown.service.ts

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

@Injectable()
export class SelectDropdownService {

  className: string = this.constructor['name'];
  private dropdownValueSubject: Subject<number> = new Subject<number>();

  constructor() { }

  sendSelectedValue(id: number) {
    this.dropdownValueSubject.next(id);
  }

  getServiceObservable(): Observable<number> {
    return this.dropdownValueSubject.asObservable();
  }
}

employee-roles.component.ts - 提取(这是父组件)

import { SelectDropdownService } from 'common/services/select-dropdown.service';

constructor(
  private activatedRoute       : ActivatedRoute,
  private sabreAPIService      : SabreAPIService,
  private selectDropdownService: SelectDropdownService
) { }

ngOnInit() {   
  this.enableEmployeeFilter = true;
  this.apiEmployeeProfile   = new EmployeeProfile(this.sabreAPIService, null);
  this.readParameters();
  this.listEmployees();
  this.observeEmployeeDropdown();
}

/**
 * Read URL parameters
 */
private readParameters() {
  this.activatedRoute.params
    .subscribe((params: Params) => {
      this.selectedEmployeeID = params['id'];
    });
}

/**
 * subscribe to change of emplyee dropdown
 */
observeEmployeeDropdown() {
  this.selectDropdownService.getServiceObservable()
    .subscribe(selectedEmployeeID => {
      this.selectedEmployeeID = selectedEmployeeID;
      this.refreshRequired();
    })
}

employee-roles.component.html - 提取

<app-select-dropdown [enablefilter]="enableEmployeeFilter" label="Employees" [selected]="selectedEmployeeID" [list]="employeeList">
</app-select-dropdown>

employee-roles-form.component.html - 提取(自定义组件的 2 个实例)

<app-select-dropdown label="Department" [list]="payrollDepartmentsList" [selected]="selectedDeptID"></app-select-dropdown>
<app-select-dropdown label="Role" [list]="businessRolesList" [selected]="selectedBroleID"></app-select-dropdown>

employee-roles-form.component.ts(摘录)

import { SelectDropdownService } from 'common/services/select-dropdown.service';


constructor(
  private formBuilder: FormBuilder,
  private sabreAPI: SabreAPIService,
  private selectDropdownService: SelectDropdownService
) { }

ngOnInit() {
  this.apiEmployeeRole = new EmployeeRole(this.sabreAPI, null);
  this.generateFormControls();
  this.listBusinessRoles();
  this.listPayrollDepartments();
  this.observeDepartmentDropdown();
}

observeDepartmentDropdown() {
  this.selectDropdownService.getServiceObservable()
    .subscribe(selectedDeptID => {
      this.selectedDeptID = selectedDeptID;
    })
}

【问题讨论】:

  • 除非你分享你的代码我的朋友,否则你可能希望得到一点帮助 :)
  • 啊,很公平,谢谢
  • @AJT_82 怎么样?
  • 更好,不幸的是我没有时间深入研究那么多代码,这是重现问题的最少代码吗?不要试图变得困难,只是想提供帮助;)希望你现在可以得到一些帮助,当你有你的代码时。
  • @AJT_82 用这个answer 通知你很抱歉在这里,因为我只能从这里通知

标签: angular rxjs observable


【解决方案1】:

根据您的先决条件,有几个选项:

如果您只能通过 ID 区分所选下拉值的类型 然后在每个.subscribe 前面放一个.filter。例如如果员工 ID 始终小于 100:

this.selectDropdownService.getServiceObservable()
    .filter((selectedId: number) => selectedId <= 100)
    .subscribe(selectedEmployeeID => {
        this.selectedEmployeeID = selectedEmployeeID;
        this.refreshRequired();
    });

如果您不能仅通过 ID 确定类型。那么您需要发出的不仅仅是 ID。在服务中创建一个接口,该接口也持有一个类型:

interface SelectedItem {
    id: number;
    itemType: "employee" | "department";
}

然后将您的 Subject&lt;number&gt; 更改为 Subject&lt;SelectedItem&gt; 并将您的服务方法更改为:

sendSelectedValue(id: number, type: string) {
    this.dropdownValueSubject.next({id: id, itemType: type});
}

getServiceObservable(type: string): Observable<number> {
    return this.dropdownValueSubject.asObservable()
           .filter((item: SelectedItem) => item.itemType === type) //filter by type
           .map((item: SelectedItem) => item.id); // only return the id
}

现在当您运行this.selectDropdownService.getServiceObservable() 时,只需将相应的itemType 放入参数中:this.selectDropdownService.getServiceObservable("employee") 或this.selectDropdownService.getServiceObservable("department")

当然,您必须知道选择组件中的类型,您可以通过将类型作为输入传递...

【讨论】:

  • 谢谢,我认为这(带界面)巧妙地为我解释了一个很好的解决方案。非常感激。 :)
【解决方案2】:

我不确定您对不同选择的潜在价值究竟是什么,但是您是否可以对价值进行某种逻辑检查以区分您的场景?

例如

subscribe(selectedDeptID => {
    if (regex.test(selectedDeptID)) {
        this.selectedDeptID = selectedDeptID;
    }
})

regex 可能是您的匹配条件,或者用某种值映射等替换该逻辑。

【讨论】:

    猜你喜欢
    • 2016-05-06
    • 2018-04-02
    • 2017-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-12
    • 2019-06-13
    相关资源
    最近更新 更多