【问题标题】:Update visibility of HTML element through directive通过指令更新 HTML 元素的可见性
【发布时间】:2020-03-31 08:49:43
【问题描述】:

我有一个显示或隐藏 HTML 元素 StackBlitz 的指令:

<div *authorize="'A'">
  Visible when letter is A
</div>

授权指令是:

@Directive({
  selector: '[authorize]'
})

export class AuthorizeDirective implements OnInit {

  letter: string;

  @Input() set authorize(letter: string) {
    this.letter = letter;
  }

  constructor(private element: ElementRef, private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, private authorizationService: AuthorizationService) { }

  ngOnInit() { 

    this.authorizationService.authorize(this.letter).subscribe(x => {
      x ? this.viewContainer.createEmbeddedView(this.templateRef) : this.viewContainer.clear();
    });    

  }

}

授权服务及其授权方法是:

export class AuthorizationService {

  private notifier: Subscription;

  private data$: Observable<string[]>;

  constructor(private noteService: NoteService) {

     this.data$ = of(['A', 'B']);
     this.data$.subscribe(x => console.log(x)); 

     this.notifier = this.noteService.get().subscribe((code: number) => { 

       if (code == 0) {
         this.data$ = of(['C', 'D']);
         this.data$.subscribe(x => console.log(x)); 
       }

    });

  }        

  authorize(letter: string) : Observable<boolean> {

    return this.data$.pipe(
      map(data => data.indexOf(letter) > -1)
    );

  }

}

在真实场景中,data$ 是使用 HTTPClient 从 API 获取的。

NoteService 是:

export class NoteService {

  private subject = new Subject<number>();

  send(code: number) {
    this.subject.next(code);
  }

  clear() {
    this.subject.next();
  }

  get(): Observable<number> {
    return this.subject.asObservable();
  }

}

当发出带有代码 0 的 note 时,data$ 也会更新...

这应该会更新使用该指令的元素的可见性。

在 StackBlitz 示例中,通过单击应显示带有 C 的 Div 按钮。

但它并没有这样做......如何触发它?

【问题讨论】:

  • 我真的不清楚!你想从服务中访问指令吗?要不然是啥?你能提供示例工作演示吗?
  • 否...在服务中,我正在更新if (note.code == 0) { } 内的data$。发生这种情况时,我希望指令更​​新元素的可见性。就像再次调用授权一样......因为使用新数据$授权的结果可能会有所不同。
  • 这是help吗?我的意思是试试ngOnChanges()
  • 以哪种方式?使用BehaviorSubject?不确定如何将其集成到指令中,或者AuthorizationService 上是否会发生某些变化。
  • 给我一个 stackblitz 试试!

标签: angular typescript rxjs angular8


【解决方案1】:

将 data$ 更改为行为主题,当 data$ 为 of() 时,它不是热可观察对象,并且只会在订阅时发出一次。顺便说一句,您应该稍微简化一下结构。真的不需要那么多文件

 this.data$ = new BehaviorSubject(['A', 'B']);
 this.data$.subscribe(x => console.log(x)); 

 this.notifier = this.noteService.get().subscribe((code: number) => { 

   if (code == 0) {
     this.data$.next(['C', 'D']);
     this.data$.subscribe(x => console.warn(x)); 
   }

【讨论】:

  • 你能举个例子吗?我试过你的代码,但它似乎不起作用。
【解决方案2】:

为您解决了一些问题。最大的问题是你如何设置你的模拟身份验证服务......由于可观察对象的工作方式,它并没有真正完成工作,如果你使用of,那是一个静态可观察对象,所以你无法调用接下来是它并更新订阅者。您需要一个静态主题来利用,如下所示:

private dataSource = new ReplaySubject<string[]>(1);
private data$: Observable<string[]> = this.dataSource.asObservable();

constructor(private noteService: NoteService) {

  this.dataSource.next(['A','B']);
  this.data$.subscribe(x => console.log(x)); // this will fire everytime next is called now

  this.notifier = this.noteService.get().subscribe((code: number) => { 
    if (code == 0) {
      this.dataSource.next(['C', 'D']);
    }
  });
}     

这样您就可以调用 next 并且订阅者会收到更新。仅此修复即可解决问题。

但我也修改了您的指令,以允许字母动态更改并提高效率:

private hasView = false; // this variable will prevent unneeded template clearing / creating
private letterSource = new Subject<string>()
private sub: Subscription
@Input() set authorize(letter: string) {
  this.letterSource.next(letter); // call next here on this subject to reauth if the letter input changes
}

constructor(private element: ElementRef, private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, private authorizationService: AuthorizationService) {
  this.sub = this.letterSource.pipe(
    switchMap(letter => this.authorizationService.authorize(letter))
  ).subscribe(x => {
    if (x && !this.hasView) { // only recreate the view if it's not already there
      this.viewContainer.createEmbeddedView(this.templateRef)
      this.hasView = true;
    } else if (!x && this.hasView) { // only clear it if it is
      this.viewContainer.clear();
      this.hasView = false;
    }
  })
}
  1. 指令通常期望您可以更改它们的输入,并且它们会相应地更新,即使您在这种情况下没有预料到,最好考虑一下。

  2. 如果在包含许多子组件的元素上使用此指令,那么在不需要时阻止视图清除/重新创建实际上会产生巨大的性能影响,而这很可能有一天会发生。最好使用结构指令尽可能高效。

固定闪电战:https://stackblitz.com/edit/angular-wlvlkr?file=src%2Fapp%2Fauthorize.directive.ts

【讨论】:

  • 它无法正常工作。如果您单击按钮两次 C 将在第二次消失。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
相关资源
最近更新 更多