简体   繁体   English

角圆依赖性解决方案

[英]Angular Circular Dependency Solution

  • I have popupservice that opens popup components for me like this: 我有popupservice可以为我打开弹出组件,如下所示:

     export class PopupService { alert() { this.matdialog.open(PopupAlertComponent); } yesno() { this.matdialog.open(PopupYesNoComponent); } custom() { this.matdialog.open(PopupCustomComponent); } } 
  • Then I open my custom popup with this.popupservice.custom() . 然后,我使用this.popupservice.custom()打开自定义弹出窗口。

     export class HomeComponent { constructor(private popupservice: PopupService) {} openCustomPopup() { this.popupservice.custom(); } } 
  • Then, in my custom popup component I want to call my own alert popup (to report an error or something): 然后,在我的custom弹出窗口组件中,我想调用自己的alert弹出窗口(报告错误或其他内容):

     export class CustomPopup { constructor(private popupservice: PopupService) {} doHttpCall() { ...do http calls... if (callFailed) this.popupservice.alert('Call Failed'); } } 

How can I solve this circular dependency problem? 我该如何解决循环依赖问题?

Notes: 笔记:

  • I have read the other questions but I feel that my problem is a specific "help me out" problem. 我已经阅读了其他问题,但我认为我的问题是一个特定的“帮助我”问题。 Though you are still welcome to refer me to other questions. 虽然仍然欢迎您转介我其他问题。
  • The this.popupservice.alert() is not just a javascript alert , it is my own custom popup that has a theme and everything. this.popupservice.alert()不仅是一个JavaScript alert ,它还是我自己的自定义弹出窗口,具有主题和所有内容。

Your service should not know about components, that is bad design if your services have knowledge of components. 您的服务不应了解组件,如果您的服务具有组件知识,那将是不好的设计。 You should have an observable like a behaviour subject in your service and have your component subscribe to the observable to know when to popup a new message. 您应该在服务中拥有一个类似于行为主题的可观察对象,并使您的组件订阅该可观察对象,以知道何时弹出新消息。

In your service 为您服务

message$ = new BehaviourSubject<string>(null);

and a function to send a message down the pipeline. 以及向管道发送消息的功能。

nextMessage(message: string) {
  this.message$.next(message);
}

Then in your component you subscribe to the message$ observable and then do you popup. 然后在您的组件中订阅$ observable消息,然后弹出。

this.messageSubscription = this.service.message$.subscribe(message => { this.popup(message); });

Making sure to takeUntil or unscbscribe on ngDestroy. 确保对ngDestroy进行until或unscbscribe。

ngOnDestroy() {
  if (this.messageSubscription ) {
    this.messageSubscription.unsubscribe();
  }
}

What you can do is to take away popup creation logic from PopupService . 您可以做的是从PopupService弹出创建逻辑。 Here is a little refactor for you. 这是给您的一些重构。

Use PopupService to only create events from different parts of the application. 使用PopupService只能从应用程序的不同部分创建事件。

@Injectable()
export class PopupService {
    private _alert: Subject<any> = new Subject();
    private _yesno: Subject<any> = new Subject();
    private _custom: Subject<any> = new Subject();

    // since you didn't like the on- methods, you can do following
    public readonly alert$ = this._alert.asObservable();
    public readonly yesno$ = this._yesno.asObservable();
    public readonly custom$ = this._custom.asObservable();

    onAlert() { return this._alert.asObservable(); }

    onYesno() { return this._yesno.asObservable(); }

    onCustom() { return this._custom.asObservable(); }

    alert(payload) { this._alert.next(payload); }
    yesno(payload) { this._yesno.next(payload); }
    custom(payload) { this._custom.next(payload); }
}

So, we have a PopupService which only emits some events with some payload s. 因此,我们有一个PopupService ,它只发出带有一些payload的事件。 I used Subject here because later subscribers won't need to know if there was a alert or yesno event earlier. 我在这里使用Subject是因为以后的订户不需要知道之前是否有alertyesno事件。 If you want to have such logic, you can change Subject to BehaviorSubject . 如果您想拥有这样的逻辑,则可以将Subject更改为BehaviorSubject

Create a component called PopupManager and use it in app.component 创建一个名为PopupManager的组件,并在app.component使用它

app.component.ts

@Component({
    selector: 'app-root',
    template: `
        <!-- some template -->
        <app-popup-manager></app-popup-manager>
    `
})
export class AppComponent {}
@Component({
   selector: 'app-popup-manager',
   template: '' // don't need to have template
})
export class PopupManagerComponent implements OnInit {
   constructor(private matDialog: MatDialog, private popupService: PopupService) {}

   ngOnInit() {
       this.popupService.onAlert().subscribe(payload => {
       //   this.matDialog.open...
       });

       this.popupService.onYesno().subscribe(payload => {
       //   this.matDialog.open...
       });

       this.popupService.onCustom().subscribe(payload => {
       //   this.matDialog.open...
       });
   }
}

With this way, you can use PopupService in any component you want since it is a singleton, standalone service now. 通过这种方式,您可以在所需的任何组件中使用PopupService,因为它现在是单例的独立服务。

Why you should not expose Subject s to outside world? 为什么不应该将Subject暴露给外界?

You can think of this encapsulating class fields. 您可以想到这种封装类字段。 You could indeed expose _alert to outside world but then you will have no control over who uses this subject in what way. 您确实可以将_alert暴露给外界,但是您将无法控制谁以何种方式使用此主题。 Methods are always great way to provide functionality while maintaining some control over the fields. 在保持对字段的某些控制的同时,方法总是提供功能的好方法。 In the future, you may want to change internals of the service, maybe some of the subjects. 将来,您可能想更改服务的内部,也许是某些主题。 If you let other parts of the application access directly on fields, you would have to refactor a lot. 如果让应用程序的其他部分直接在字段上访问,则必须进行大量重构。 But this way, since you are giving them some methods, as long as you don't break those methods you'll be fine. 但是这样,由于您为他们提供了一些方法,所以只要您不破坏这些方法,就可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM