初始化两个相同组件时,服务对象正陷入混乱之中

问题描述 投票:0回答:1

我创建了一个称为边栏的组件。假设该组件有2个输入,sideBarMode和sideBarSide。在app.component中,我像这样添加该组件<sidebar [sideBarMode]="'sliding'" [sideBarSide]="'right'"></sidebar>

[当我将以上内容用作一个标签时,它可以正常工作。但是,当我做类似的事情

<div>
<sidebar [sideBarMode]="'sliding'" [sideBarSide]="'right'"></sidebar>
</div>

<div>
<sidebar [sideBarMode]="'docked'" [sideBarSide]="'left'"></sidebar>
</div>

它们被渲染,但是具有相同的类,尽管如果我要控制台控制台记录正在传递的ngOnInit中的值,它们将正确显示。例如,一个div应该显示为类滑动,而另一个应为停靠。相反,它们都显示为停靠两者。

这是HTML模板。

<div class="container-fluid">
<div [ngClass]="barSliding ? 'sliding' : 'sidebar-docked'">
    <div class="row k-sidebar {{theme}}"
        [ngClass]="[sidebarLeft ? 'position-left' : 'position-right', barSliding ? 'sliding-content-bar' : 'sidebar-docked', isDesktopWidth.value ? 'largeView' : 'mobileView']"
        [@slideAnimation]= 'animationState'
        [@dockMobileSidebar]= 'animationStateMobile'
        [ngStyle]="{'width.px': isDesktopWidth.value ? sideBarWidth : '' }"
        >
        <div class="col-md-12">
            <ng-container *ngIf="sidebarTemplate">
                <ng-container *ngTemplateOutlet="sidebarTemplate">

                </ng-container>
            </ng-container>

            <ng-container *ngIf="!sidebarTemplate">
                <p>This is a paragraph, no template has been passed</p>
            </ng-container>
        </div>
</div>
</div>

这是我检查模式的方式,无论它是否停靠在TS文件中,我都从ngOnInit调用此方法。

 private checkSidebarMode(mode) {
console.log('checkSidebarMode ', mode);
if (mode === 'sliding') {
  this.sidebarService.sideBarSliding.next(true);
  console.log('checkSidebarMode ', this.sidebarService.sideBarSliding.getValue());
  this.sideBarDock.next(false);
} else if (mode === 'docked' && this.sidebarService.innerWidth.getValue() <= 767) {
  this.sideBarDock.next(false);
  this.sidebarService.sideBarSliding.next(true);
} else if (mode === 'docked' && this.sidebarService.innerWidth.getValue() > 767) {
  this.sideBarDock.next(true);
  this.sidebarService.sideBarSliding.next(false);
  this.sidebarService.animationState.next('docked');
  console.log('checkSidebarMode ', this.sidebarService.sideBarSliding.getValue());
}
  }

如您所见,如果它正在滑动,则将observable设置为true,然后在ngOninit中,将该值分配给barSliding,该值在HTML模板中用于确定类名。

停靠的是正确的,但是滑动的正在获取停靠的属性...为什么?

enter image description here

angular angular-components angular-component-life-cycle angular-input
1个回答
1
投票

由于您的BehaviorSubject位于sidebarService中,因此该值很可能是providedIn: 'root',这使其成为单例。因此,对于组件的两个实例,您的BehaviorSubject相同。

您有多种解决方案:

  • 直接在组件中声明BehaviorSubject,而不是在服务中使用共享的BehaviorSubject。 next()输入更改后,我将通过在ngOnChanges method中调用ngOnChanges来更新它。
mode
  • 在服务中使用工厂方法,该方法将返回要由组件使用的新@Component({...}) export class MyComponent implements OnInit { @Input() mode: 'sliding' | 'docked' = 'sliding'; private subject = new BehaviorSubject<string>(null); ngOnChanges() { // here you should check that 'mode' changed before calling next() this.subject.next(this.mode); } } 实例。如果它是简单的BehaviorSubject,但未附加逻辑,则也许比第一个解决方案没有真正的好处。
BehaviorSubject

但是仅当您从服务返回的// service @Injectable() export class MyService { public getNewBehaviorSubject() { return new BehaviorSubject<String>(null); } } // component @Component({...}) export class MyComponent implements OnInit { @Input() mode: 'sliding' | 'docked' = 'sliding'; private subject = this.myService.getNewBehaviorSubject(); ngOnChanges() { // here you should check that 'mode' changed before calling next() this.subject.next(this.mode); } } 必须使用服务中的其他信息(其他数据,静态变量,例如屏幕大小等)时,这才有意义。

    通过在组件级别(而不是模块级别或根级别)提供服务,从而使服务成为单例。但是您可能更喜欢其他解决方案,因为拥有非单点服务可能会更加混乱,而不是您想要的。BehaviorSubject请记住,默认情况下,Angular的服务被设计为单例。这是最常见的用例。
  • 您应该问自己以下一个问题:该行为应该在组件之间共享还是由其他组件知道?在这种情况下,您可以将其委托给全局单例服务。示例:每个组件可能需要一次了解应用程序的主滑动菜单是否打开。或者,此行为是否特定于组件的实例?在这种情况下,状态应存储在组件实例本身中。示例:我有两个可折叠元素,每个元素都必须跟踪它是否折叠,但是没有其他组件需要知道此状态。
您可以采用许多策略,没有适合所有情况的答案。
© www.soinside.com 2019 - 2024. All rights reserved.