【问题标题】:Implement toggle-based page rotation rxjs Angular实现基于切换的页面旋转 rxjs Angular
【发布时间】:2021-09-08 23:59:29
【问题描述】:

对于一个项目,我有一个包含许多卡片的简单容器。由于卡片很多,所以有多个页面,当屏幕显示时,它会每隔 X 秒自动旋转到下一页当用户点击空格键时。

对于一项新功能,我们添加了一个切换按钮来关闭自动旋转。我为toggle-button 创建了一个EventEmitter,如下面的toggleEmitter 所示,但我对rxjs 很陌生,不知道如何使用它来实际停止旋转。有人可以帮忙吗?

@Component({
  selector: 'rotator-container',
  templateUrl: './rotator-container.component.html',
})
export class RotatorContainerComponent implements AfterViewInit, OnDestroy {
  @ContentChildren(RotatorItemComponent, { read: ElementRef })
  rotatorItems: QueryList<ElementRef>;

  @Input() rotationInterval = 30 * 1000;

  @Output() toggleEmitter: EventEmitter<MatSlideToggleChange> =
    new EventEmitter();
  toggle(event: MatSlideToggleChange) {
    this.toggleEmitter.emit(event);
  }

  timer$ = this.activatedRoute.queryParams.pipe(
    map(params => params['rotate']),
    switchMap(rotate =>
      rotate === 'false' ? of(0) : timer(0, this.rotationInterval)
    )
  );

  spaceCounter$ = fromEvent<KeyboardEvent>(document, 'keydown').pipe(
    filter(({ code }) => code === 'Space'),
    tap(e => e.preventDefault()),
    map(() => 1),
    scan((acc, curr) => acc + curr, 0),
    startWith(0)
  );

  rotationCounter$ = combineLatest([this.timer$, this.spaceCounter$]).pipe(
    map(([index, offset]) => index + offset)
  );

  rotatorSubscription: Subscription;

  constructor(private activatedRoute: ActivatedRoute) {}

  ngAfterViewInit() {
    const rotatorItemsLength$ = this.rotatorItems.changes.pipe(
      map(() => this.rotatorItems.length),
      startWith(this.rotatorItems.length)
    );

    const visibleIndex$ = combineLatest([
      this.rotationCounter$,
      rotatorItemsLength$,
    ]).pipe(
      map(([index, length]) => index % length),
      startWith(0)
    );

    this.rotatorSubscription = visibleIndex$.subscribe(visibleIndex =>
      this.rotatorItems.forEach((item, index) => {
        (<HTMLElement>item.nativeElement).style.visibility =
          visibleIndex === index ? 'visible' : 'hidden';
        (<HTMLElement>item.nativeElement).style.position =
          visibleIndex === index ? 'relative' : 'absolute';
      })
    );
  }

  ngOnDestroy() {
    this.rotatorSubscription && this.rotatorSubscription.unsubscribe();
  }
}

【问题讨论】:

  • 你能在 stackblitz 中重新创建一些东西吗?我建议您可能应该使用BehaviorSubject 来保持切换状态,然后在您的流中使用它。我只是想写一个答案,但我认为如果你有一个我可以玩的最小复制品会很有帮助。

标签: javascript angular typescript rxjs rxjs-observables


【解决方案1】:

我认为您不需要 EventEmitter。相反,您需要更改 timer$ Observable 的工作方式。

我会尝试以下内容。

首先定义一个Subject,它会在切换按钮状态发生变化时发出truefalse。特别是如果切换按钮打开,它会发出true,否则会发出false

这段代码看起来像这样

  // use a BehaviorSubject to specify a value which gets emitted at the start, in this case false
  toggleSubject = new BehaviourSubject<bool>(false)
  ....
  toggle(event: MatSlideToggleChange) {
    this.toggleSubject.next(event.checked);
  }

然后更改timer$ Observable。一种处理方式可能如下。

// first we just rename the current timer$ to _timer$
_timer$ = this.activatedRoute.queryParams.pipe(
  map(params => params['rotate']),
  switchMap(rotate =>
    rotate === 'false' ? of(0) : timer(0, this.rotationInterval)
  )
);

// then we redefine timer$ stream starting from toggleSubject
timer$ = toggleSubject.pipe(
  // any time toggleSubject emits we switch to a new stream
  switchMap(toggleVal => {
    // if toggleButton is on then the automatic rotation is off otherwise is on
    // this is accomplished by returning an Observable that emits 0 in the first case
    // or the old definition of timer$ in the second case
    return toggleVal ? of(0) : this._timer$
    
  })
)

【讨论】:

  • 谢谢,这正是我需要的!
猜你喜欢
  • 2013-01-21
  • 2017-10-12
  • 2017-11-24
  • 1970-01-01
  • 2012-07-13
  • 1970-01-01
  • 2017-10-18
  • 2011-07-17
  • 2020-01-13
相关资源
最近更新 更多