【问题标题】:How to retrieve EventEmitter response Angular 6如何检索EventEmitter响应Angular 6
【发布时间】:2023-04-01 16:16:01
【问题描述】:

我有以下父组件:

<h1>Vehicle Inventory</h1>

<p *ngIf="!vehicles"><em>No vehicle entries found</em></p>

<div *ngIf="vehicles">

    <ul class="nav nav-pills nav-fill">
        <li class="nav-item">
          <a class="nav-link" routerLink="/home/price" routerLinkActive="active">Search By price</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="/home/make-model" routerLinkActive="active">Search By Make Or Model</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="/home/engine-capacity" routerLinkActive="active">Search By Engine Capacity</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" routerLink="/home/cylinder-variant" routerLinkActive="active">Search By Cylinder Variant</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" routerLink="/home/cylinder-capacity" routerLinkActive="active">Search By Cylinder Capacity</a>
        </li>
    </ul>

  <router-outlet></router-outlet>
</div>

<table class="table" *ngIf="vehicles">
    <thead class="thead-dark">
      <tr>
        <th scope="col">Make</th>
        <th scope="col">Model</th>
        <th scope="col">Engine Capacity</th>
        <th scope="col">Cylinder Variant</th>
        <th scope="col">Top Speed</th>
        <th scope="col">Price (R)</th>
        <th scope="col">Cylinder Capacity</th>
        <th scope="col">Air Pressure/second</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let vehicle of vehicles">
        <td>{{ vehicle.Make }}</td>
        <td>{{ vehicle.Model }}</td>
        <td>{{ vehicle.EngineCapacity }}</td>
        <td>{{ vehicle.CylinderVariant }}</td>
        <td>{{ vehicle.TopSpeed }}</td>
        <td>{{ vehicle.Price }}</td>
        <td>{{ vehicle.IndividualCylinderCapacity }}</td>
        <td>{{ vehicle.AirPressurePerSecond }}</td>
      </tr>
    </tbody>
  </table>

从上面可以看出,我这里有一些导航,会决定子组件被加载到&lt;router-outlet&gt;中。

我的子组件通过EventEmitter 发出一个事件,如下所示:

import { Component, OnInit, Input, Output } from '@angular/core';
import { VehicleService } from '../../Services/VehicleService';
import { EventEmitter } from '@angular/core';
import { Vehicle } from '../../Models/Vehicle';

@Component({
  selector: 'app-search-cylinder-capacity',
  templateUrl: './search-cylinder-capacity.component.html',
  styleUrls: ['./search-cylinder-capacity.component.css']
})
export class SearchCylinderCapacityComponent implements OnInit {

  @Input() cylinderCapacity: any;
  @Output() dataNotifier: EventEmitter<Vehicle[]> = new EventEmitter();

  constructor(private service: VehicleService) { }

  ngOnInit() {
  }

  searchVehicle() {
    this.service
          .SearchVehiclesByCylinderCapacity(this.cylinderCapacity)
            .subscribe(response => this.dataNotifier.emit(response));

  }

}

如何捕获此事件的响应,以便我的父组件的车辆:Vehicle[] 可以填充事件的响应?

【问题讨论】:

  • 我不会为这种行为引入 vincecampanale 所描述的额外服务。您已经有一个应该可用的VehicleService。您如何使用SearchVehiclesByCylinderCapacity 发出的数据?
  • @DavidWalschots 我认为 vincecampanale 意味着我应该使用我现有的服务。他只是以 DateNotifierService 为例。我将它实现到我的服务中
  • 我仍然对您的使用感兴趣。您可能不需要这些事件。
  • @DavidWalschots 添加了一个 aswer,所以你可以看到我是如何实现的

标签: angular eventemitter angular6


【解决方案1】:

无法监听来自router-outlet 的事件,因为它只是一个占位符。您可以使用“共享服务”,如此答案中所述:https://stackoverflow.com/a/41989983/5932590

为了模仿dateNotifier 事件,共享服务可能如下所示:

@Injectable()
export class DateNotifierService {
    private source = new Subject<Vehicle[]>();
    public event$ = this.source.asObservable();
    emit(eventData: Vehicle[]): void {
        this.source.next(eventData);
    }
}

然后您可以将其注入您的子组件并发出事件:

export class SearchCylinderCapacityComponent {
  vehicles: Vehicle[];

  constructor(private dateNotifier: DateNotifierService) {}

  onClick(){
    this.dateNotifier.emit(this.vehicles);
  }
}

以及将其注入您的父组件并捕获事件:

export class ParentComponent {
  constructor(private dateNotifier: DateNotifierService) {
    dateNotifier.event$.subscribe(vehicles => console.log(vehicles));
  }
}

【讨论】:

  • 我读到这个例子,在单例服务中声明一个 EventEmitter 是不好的做法?
  • 我仍然相信我会使用这种方法,因为它适用于我的场景,只是试图坚持良好的习惯
  • 啊,是的,看起来您需要在 app.module 或更高级别的模块中提供它以确保它是单例。我会说这不一定是不好的做法,但也不是理想的。不幸的是,鉴于router-outlet 没有此功能,这是您最好的选择。
  • 谢谢。鉴于这种情况,我认为这将不得不这样做;-),希望有所不同。
  • 我知道这种感觉 :) -- 祝你好运。
【解决方案2】:

此答案基于 Monstertjie_za 自己对该问题的回答。

鉴于VehicleService 似乎提供了获取一系列车辆的不同方式。我会公开vehicles 本身。

@Injectable({
    providedIn: "root"
})
export class VehicleService {
    vehicles: Vehicle[] | undefined;

    constructor(private http: HttpClient, private configService: ConfigService) { }

    searchVehiclesByCylinderCapacity(cylinderCapacity: any): void {
        var finalEndPoint = this.configService.SearchVehiclesByCylinderCapacityEndpoint 
            + cylinderCapacity;
        this.makeRequest(finalEndPoint);
    }

    searchTop10ByAirPressure(airPressure: any): void {
        var finalEndPoint = this.configService.SearchVehiclesByAirPressureEndpoint 
            + airPressure;
        this.makeRequest(finalEndPoint);
    }

    private makeRequest(endpoint: string): void {
        this.http.get<Vehicle[]>(endpoint)
            .subscribe(vehicles => this.vehicles = vehicles);
    }
}

然后子组件只是开始调用,实际上并没有做任何其他事情:

@Component({
  selector: 'app-search-engine-capacity'
})
export class SearchEngineCapacityComponent {  
  constructor(private vehicleService: VehicleService) { }

  searchVehicle(): void {
      this.vehicleService.searchVehiclesByEngineCapacity(this.engineCapacity);
  }
}

而您的 HomeComponent 只是公开了服务,该服务公开了要在您的视图中使用的车辆:

@Component({
  selector: 'app-home',
})
export class HomeComponent implements OnInit {
  constructor(vehicleService: VehicleService) {}

  ngOnInit() {
    this.vehicleService.getAllVehicles();
  }
}
<table class="table" *ngIf="vehicleService.vehicles">
    <thead class="thead-dark">
      <tr>
        <th scope="col">Make</th>
        <th scope="col">Model</th>
        <th scope="col">Engine Capacity</th>
        <th scope="col">Cylinder Variant</th>
        <th scope="col">Top Speed</th>
        <th scope="col">Price (R)</th>
        <th scope="col">Cylinder Capacity</th>
        <th scope="col">Air Pressure/second</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let vehicle of vehicleService.vehicles">
        <td>{{ vehicle.Make }}</td>
        <td>{{ vehicle.Model }}</td>
        <td>{{ vehicle.EngineCapacity }}</td>
        <td>{{ vehicle.CylinderVariant }}</td>
        <td>{{ vehicle.TopSpeed }}</td>
        <td>{{ vehicle.Price }}</td>
        <td>{{ vehicle.IndividualCylinderCapacity }}</td>
        <td>{{ vehicle.AirPressurePerSecond }}</td>
      </tr>
    </tbody>
</table>

【讨论】:

  • VehicleService.vehicles 发生变化时,父组件中的表格是否会一直更新?没有事件告诉它需要更新?如果这可行,它甚至可能比使用 EventEmitter 更好
  • 是的,它会的。请注意,即使您想要基于事件的解决方案。我不会使用您提供的代码作为答案;因为它使每个子组件负责订阅然后在服务中设置数据。管理其数据应该是服务自己的责任。
  • 第二个注意事项是,也许最好修改您的问题,或添加您在问题答案中提供的信息。其他读者可能很难理解这里发生的事情:-)。
  • 谢谢。我会审查并接受您的问题:-)
【解决方案3】:

根据@vincecampanale 提到的内容,这就是我想出的:

在我的VehicleService 课程中,我有以下代码用于根据不同的搜索检索车辆。 VehicleService 类还包括一个 dataChangedEvent: EventEmitter&lt;Vehicle[]&gt;,当它被发送到时,它将保存 Vehicle 的最新结果。

这是我的VehicleService

import { Injectable, ErrorHandler, EventEmitter } from "@angular/core";
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';

import { ConfigService } from "./ConfigService";
import { Vehicle } from "../Models/Vehicle";
import { HttpClient } from "@angular/common/http";
import { HttpResponse } from "selenium-webdriver/http";


@Injectable({
    providedIn: "root"
})
export class VehicleService {

    dataChangedEvent: EventEmitter<Vehicle[]> = new EventEmitter();

    constructor(private http: HttpClient, private configService: ConfigService){

    }

    SearchVehiclesByCylinderCapacity(cylinderCapacity: any): Observable<Vehicle[]> {
        var finalEndPoint = this.configService.SearchVehiclesByCylinderCapacityEndpoint + cylinderCapacity;
        return this.makeRequest(finalEndPoint);
    }
    SearchTop10ByAirPressure(airPressure: any): Observable<Vehicle[]> {
        var finalEndPoint = this.configService.SearchVehiclesByAirPressureEndpoint + airPressure;
        return this.makeRequest(finalEndPoint);
    }

    private makeRequest(endpoint: string): Observable<Vehicle[]> {
        return this.http.get<Vehicle[]>(endpoint);
    }
}

每个子组件一旦在subscribe() 处理程序中收到响应,就会将数据发送到K 中的dataChangedEvent

这是一个子组件:

import { Component, OnInit } from '@angular/core';
import { VehicleService } from '../../Services/VehicleService';

@Component({
  selector: 'app-search-engine-capacity',
  templateUrl: './search-engine-capacity.component.html',
  styleUrls: ['./search-engine-capacity.component.css']
})
export class SearchEngineCapacityComponent implements OnInit {

  engineCapacity: any;

  constructor(private vehicleService: VehicleService) { 

  }

  ngOnInit() {
  }

  searchVehicle(): void {
    this.vehicleService
          .SearchVehiclesByEngineCapacity(this.engineCapacity)
            .subscribe(response => this.vehicleService.dataChangedEvent.emit(response));
  }
}

最后,在父组件中,我将subscribe()VehicleService中的dataChangedEvent,当搜索到的车辆的数据发生变化时会通知父组件。 下面是我的父组件,它从constructor 中向VehicleService 中的dataChangedEvent 晒太阳

import { Component, OnInit, Input } from '@angular/core';
import { VehicleService } from '../../Services/VehicleService';
import { Vehicle } from '../../Models/Vehicle';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  vehicles: Vehicle[];

  constructor(private vehicleService: VehicleService) {
    this.vehicleService.dataChangedEvent.subscribe(response => this.vehicles = response)
   }

  ngOnInit() {
    this.vehicleService
      .GetAllVehicles()
        .subscribe(response => this.vehicles = response, 
                    error => console.log(error));
  }

  populateGrid(vehicleData: Vehicle[]){
    this.vehicles = vehicleData;
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多