0

我创建了一个名为侧边栏的组件。该组件有 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 中的值,它们会正确显示。EG 一个 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 模板中用于确定类名。

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

在此处输入图像描述

4

1 回答 1

1

正如您BehaviorSubject在 中一样sidebarService,这可能providedIn: 'root'是使其成为单例的原因。因此,您BehaviorSubject的组件的两个实例是相同的。

您有多种解决方案:

  • 直接在组件中声明 aBehaviorSubject而不是在服务中使用共享的 BehaviorSubject。一旦输入发生变化,我会通过调用next()一个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 的服务默认设计为单例。这是最常见的用例。

您应该问自己的一个问题是:这种行为应该在组件之间共享还是由其他组件知道?在这种情况下,您可以将其委托给全局单例服务。示例:每个组件可能都需要知道应用程序的主滑动菜单是否打开。或者,这种行为是否特定于组件的实例?在这种情况下,状态应该存储在组件实例本身中。示例:我有两个可折叠元素,每个元素都必须跟踪它是否已折叠,但没有其他组件需要知道此状态。

您可以采用许多策略,但没有适合所有情况的答案。

于 2020-03-26T09:46:29.930 回答