Javascript 如何在Angular中重现Angular的JS.broadcast()/.on()行为?

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>

我正在用Angular8重写AngularJS应用程序。我读过组件间通信的不同方式,但鉴于我目前的需求,似乎找不到实现这一点的正确方法

我目前有两个同级组件,它们都使用处理基本crud功能的公共服务。表单组件调用服务中的create方法,列表组件调用fetch方法

<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();
      }