通过指令更新HTML元素的可见性 [英] Update visibility of HTML element through directive

查看:115
本文介绍了通过指令更新HTML元素的可见性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a directive that shows or hides an HTML element StackBlitz :

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

而Authorize指令是:

And the Authorize directive is:

@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();
    });    

  }

}

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

The authorization service and its authorize method is:

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获得的.

On a real scenario data$ is obtained from API using HTTPClient.

NoteService是:

And the NoteService is:

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$也将更新...

When a note with code 0 is emitted data$ is also updated ...

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

That should update the visibility of the elements that uses the directive.

在StackBlitz示例中,通过单击按钮,应显示Div with C.

On StackBlitz example by clicking the button the Div with C should appear.

但是它没有那样做...如何触发呢?

But it is not doing that ... How to trigger that?

推荐答案

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

fixed up a few things for you. The biggest issue was how you were setting up your mock auth service... it wasn't really getting the job done due to how observables work, if you use of, that's a static observable, so you have no way of calling next on it and updating subscribers. you needed a static subject to leverage, like so:

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']);
    }
  });
}     

这样,您可以拨打下一个电话,订阅者可以获取更新.仅此修复程序就能解决问题.

this way you can call next and subscribers get the update. This fix alone will fix the problem.

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

But I also touched up your directive to allow the letter to change dynamically and be more efficient:

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. 指令通常期望您可以更改其输入,并且它们会相应地更新,即使您在这种情况下不期望这样做,也最好考虑一下.

  1. directives usually have an expectation that you can change their input and they will update accordingly, even if you don't anticipate it in this case, it's best to account for it.

防止在不需要时清除/重新创建视图实际上可能会对性能产生巨大影响,这很有可能会在某一天出现.最好通过结构性指令来尽可能地提高效率.

preventing the view from clearing / recreating when not needed can actually have huge performance implications if this directive in used on an element that contains many sub components, which it very well may someday. Best to be as efficient as possible with a structural directive.

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

这篇关于通过指令更新HTML元素的可见性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆