Javascript 如何在Angular中重现Angular的JS.broadcast()/.on()行为?
我正在用Angular8重写AngularJS应用程序。我读过组件间通信的不同方式,但鉴于我目前的需求,似乎找不到实现这一点的正确方法 我目前有两个同级组件,它们都使用处理基本crud功能的公共服务。表单组件调用服务中的create方法,列表组件调用fetch方法Javascript 如何在Angular中重现Angular的JS.broadcast()/.on()行为?,javascript,angular,Javascript,Angular,我正在用Angular8重写AngularJS应用程序。我读过组件间通信的不同方式,但鉴于我目前的需求,似乎找不到实现这一点的正确方法 我目前有两个同级组件,它们都使用处理基本crud功能的公共服务。表单组件调用服务中的create方法,列表组件调用fetch方法 <page-component> <form component></form component> <list-component></list-component>
<page-component>
<form component></form component>
<list-component></list-component>
</page-component>
但我对如何实现事件侦听器感到困惑,因为我读过的所有文档似乎都指向父母和孩子之间传递的事件,而不是兄弟姐妹之间传递的事件。此外,我看到的示例似乎都集中于将实际数据传递给组件,而不是简单地让组件监视事件并按照我的要求自行完成工作
我还见过使用
@Input
的方法,这些方法要求在声明时将参数传递给它们。不知何故,我觉得这些组件应该能够独立工作,而不是依赖于此。这个问题的答案可能是一个好的holywar的主题,你知道)
根据经验,我的看法如下:
假设用户输入了一些内容,然后执行刷新页面。问问自己会发生什么
如果用户的更改应恢复到“编辑前”状态,则父组件足以共享此类事件
如果用户的更改应该与刷新页面操作之前一样,那么如果您需要将其存储在本地存储或数据库或其他位置,则最好为其包含服务。您可以在父级中创建一个函数,用于设置所需子组件中的值。 在组件中
<page-component>
<form component (value change)="formValueFunction($event)" emitedformValue="formEmitiedValue"></form component>
<list-component (list change)="listValueFunction($event)" emitedListValue="listEmitedValue"></list-component>
</page-component>
使用日志检查事件值,如果表单输入中有更改,请在组件中使用OnChange事件挂钩我相信有多种方法可以实现这一点 对于不相关的组件,如同级组件,一种方法是在公共服务中使用可观察的RxJS模块,表单组件将向其推送一个新值,列表组件可以订阅该值 服务:
import { Subject } from 'rxjs/Subject';
@Injectable()
export class SampleService {
public triggerSource = new Subject<boolean>();
public trigger$ = this.triggerSource.asObservable();
}
列表组件:
import { SampleService } from '../services/sample.service';
@Component({
selector: 'form-component',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css'],
})
export class FormComponent implements OnInit {
constructor(private _sampleService: SampleService) {
}
private setTriggerState(state: boolean) {
this._sampleService.triggerSource.next(state);
}
}
import { SampleService } from '../services/sample.service';
@Component({
selector: 'list-component',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css'],
})
export class ListComponent implements OnInit {
constructor(private _sampleService: SampleService) {
this._sampleService.trigger$.subscribe(value => {
if (value) {
// value is true (refresh?)
} else {
// value is false (don't refresh?)
}
});
}
}
由于我们返回的是一个可观测的数据,因此需要对数据的流出进行细化。例如,在您的情况下,可以使用运算符distinctUntilChanged()
来避免推送冗余信息。那么可观测的声明将是
public trigger$ = this.triggerSource.asObservable().distinctUntilChanged();
在ListComponent中创建一个
@Input
属性,并传递列表组件将从PageComponent呈现的所有项目。每当创建组件发出一个值时,获取PageComponent中的新列表并更新传递的属性
在ListComponent中实现OnChange生命周期钩子,并在ngOnChange(changes)
方法中捕获属性更改
如果您不想从PageComponent传递列表,那么可以使用很少的选项来解决此问题
选项1:
而不是传递列表:数组
@组件({
//组件元数据
})
导出类ListComponent实现OnChange{
@Input()listOfAny:Array=[];
ngOnChanges(更改:SimpleChanges){
this.listofay=changes.listofay.currentValue
}
}
@组成部分({
//组件元数据
})
导出类FormComponent{
@Output()valueChange=neweventemitter();
onUpdate(){
this.valueChange.emit(true);
}
}
@组成部分({
//组件元数据
模板:`
`
})
导出类页面组件{
列表=[]
值更改(新值){
//如果需要,您可以从API获取项目。
this.list.push(newValue)
}
}
请看一下。主题使组件间的交流更容易
由于表单和列表组件在您的案例中使用公共CRUD服务,所以您可以拥有一个主题(比如newRecordSubject),并将创建的任何新记录推送到该主题中。现在,列表组件应该订阅这个主题,以便在表单组件创建任何新记录时收到通知
请看下面的CRUD服务示例。从下面的代码片段中,我认为一切都是非常自我解释的
crud.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { Record } from './record.model';
@Injectable({providedIn: 'root'})
export class CrudService {
private newRecordSubject = new Subject<Record>();
get newRecordListener() {
return this.newRecordSubject.asObservable();
}
public insertRecord(record: Record): void {
// logic to pass the record to the backend
this.newRecordSubject.next(record);
}
public fetch(): Record[] {
// logic to fetch the inital records from the backend
return [];
}
}
import { OnInit, Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { CrudService } from './crud.service';
import { Record } from './record.model';
@Component({
selector: 'list-component',
template: ''
})
export class ListComponent implements OnInit, OnDestroy {
records: Record[];
private sub: Subscription;
constructor(private service: CrudService) {}
ngOnInit() {
this.records = this.service.fetch();
this.sub = this.service.newRecordListener.subscribe(record => this.records.push(record));
}
ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}
}
}
注意:
我正在将Subject转换为Observable,这样Subject就可以作为Crud服务之外的只读方法使用
取消订阅主题和可观察对象始终是最佳实践,以避免内存泄漏
希望这有帮助!干杯,快乐编码。当我将我的应用程序从AngularJS迁移到Angular2+时。我选择使用RxJS,超级容易使用,但有学习曲线
创建服务和RxJS行为主体。如果需要,任何组件现在都可以订阅更改,并且任何组件都可以使用.next()方法发送更新的数据
这是您的服务:
import {Injectable} from "@angular/core";
import { BehaviorSubject } from "rxjs/BehaviorSubject";
@Injectable()
export default class yourService {
valueChange = new BehaviorSubject<object>({});
//add service functions if needed
...
}
同意。我试图把问题的焦点拉近您还应该提到Subject是从rsjx模块导入的,另外一个模块也有Subject组件,当我尝试导入Subject时,VSC让我感到困惑…@thenetimp:谢谢您的提示。相应地修改了答案。谢谢你的回答。很高兴看到你加入我们的社区。这只是一条建议:在你的回答中坚持原来的问题可能是有益的。例如,使用FormComponent
和ListComponent
保留原始域。在这种情况下,提问者更容易使用你的建议。
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { Record } from './record.model';
@Injectable({providedIn: 'root'})
export class CrudService {
private newRecordSubject = new Subject<Record>();
get newRecordListener() {
return this.newRecordSubject.asObservable();
}
public insertRecord(record: Record): void {
// logic to pass the record to the backend
this.newRecordSubject.next(record);
}
public fetch(): Record[] {
// logic to fetch the inital records from the backend
return [];
}
}
import { OnInit, Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { CrudService } from './crud.service';
import { Record } from './record.model';
@Component({
selector: 'list-component',
template: ''
})
export class ListComponent implements OnInit, OnDestroy {
records: Record[];
private sub: Subscription;
constructor(private service: CrudService) {}
ngOnInit() {
this.records = this.service.fetch();
this.sub = this.service.newRecordListener.subscribe(record => this.records.push(record));
}
ngOnDestroy() {
if (this.sub) {
this.sub.unsubscribe();
}
}
}
import {Injectable} from "@angular/core";
import { BehaviorSubject } from "rxjs/BehaviorSubject";
@Injectable()
export default class yourService {
valueChange = new BehaviorSubject<object>({});
//add service functions if needed
...
}
import { Component, OnInit, OnDestroy } from "@angular/core";
import YourService from "../path to your service";
@Component({
selector: "name of component",
template: require("your.html")
})
export class nameOfComponent implements OnInit, OnDestroy {
private sub;
privat val = "Your value";
constructor( private YourService: YourService) {
}
ngOnInit() {
this.sub = this.YourService.valueChange.subscribe( (value) => {
this.YourService.valueChange.next(this.val); // If you need to pass some value to the service.
this.val = value;
})
}
ngOnDestroy() {
this.sub.unsubscribe();
}