如果我直接输入 url 组件,如何在其他组件中获取 BehaviourSubject 值?

How to get BehaviourSubject value in other component if I type the component url directly?

让我们简化问题。假设 angular 应用程序只有几个组件。在应用组件 html、

<app-nav></app-nav>
<router-outlet></router-outlet>

我们注入一个服务来获取导航菜单的ts文件。该服务包含一个 BehaviourSubject 来处理菜单项。

@Injectable({
     providendIn: 'root'
})
export class AppService {
    public items = new BehaviourSubject<Item[]>([]);
    public getMenu() {
         return this.http.get<Item[]>(this.url);
    }
    public getItems() {
        return this.items.value;
    }
}

在app组件ts文件中。

ngOnInit() {
   this.service.getMenu().subscribe(
      res => {
          this.items = res;
          this.service.items.next(this.items);
      });
}

到目前为止一切顺利。然后在我的导航组件中我可以接收这些项目。

export class NavComponent implements OnInit {
    items: any;
    constructor(private service: AppService){}
    ngOnInit() {
          this.items = this.service.getItems();
    }
}

然后在导航中html我有

   <span *ngFor="let item of items">
     <div>{{item.name}}</div>
   </span>

所以正常的步骤是访问http://localhost:4200 然后导航栏就可以正确显示了。但是,如果我直接输入另一个组件的 url 。说

http://localhost:4200/app/one

页面似乎首先加载组件 one。导航栏在获取菜单项之前呈现。实际上我在组件一中记录了项目,它是空的。最终它也转到应用程序组件,但为时已晚。

所以我的问题是我想首先获得菜单项。无论我输入 url 什么,我总能得到导航栏菜单项并正确呈现。我怀疑我用错了BehaviouSubject。有什么提示吗?

您需要订阅组件一中的 BehaviourSubject 项。

这是我的解决方案:

   items: any;
   constructor(private service: AppService){}
   ngOnInit() {
      this.service.items.subcribe(items => this.items = items);
   }

因为 Component One 在 get 请求完成之前被初始化。此时,BehaviourSubject 没有收到任何新值,因此它发出了种子值(一个空数组)。所以我认为 subscribe() 会起作用。

您希望菜单栏立即呈现在任何页面上吗? 如果那是真的,我认为将您的初始化代码放入 app.component.t 并不是您想要的。

我会说在您的应用程序启动之前使用 APP_INITIALIZER 加载菜单项可能是更好的选择。参见例如

您遇到的问题是您的导航组件只设置了一次 items 的值。由于您的行为主题的默认值为空数组,因此当 OneComponent.ngOnInit 调用 service.getItems() 时,它会收到一个空数组,并且该值永远不会在进一步的排放中更新。

有一个 question here 探索如何适当地使用 BehaviorSubject.value。我不鼓励在你的情况下使用 .value

它实际上使代码变得更加复杂,并且破坏了数据流的“可观察性”。

既然NavComponent直接使用了AppService,那么AppComponent根本不需要参与!直接让NavComponent订阅物品就可以了

此外,让 AppService 调用一个服务方法来获取项目,然后另一个服务方法让服务推出新值似乎不必要地复杂。如果服务负责推出最新的值并且服务的消费者只需订阅可观察对象,事情就会简单得多。

服务:

export class AppService {
  private items = new BehaviorSubject<Item[]>([]);
  public items$ = this.items.asObservable();

  public getMenu() {
    return http.get().pipe(
      tap(items => this.items.next(items))
    );
  }
}

上面的代码允许消费者简单地订阅 items$ 并自动接收最新值。每当调用 getMenu() 时,它将通过主题推送最新值。

NavComponent可以直接用这个,AppComponent不需要做任何事情:

导航组件:

export class NavComponent {
  items$ = this.service.items$;

  constructor(private service: AppService){ }

  ngOnInit() {
    this.service.getMenu().subscribe();
  }
}
<span *ngFor="let item of items$ | async">
  <div>{{ item.name }}</div>
</span>

应用组件:

export class AppComponent  {

}

大声笑,应用程序组件中没有任何内容。这是一个有效的 StackBlitz

所以,回到你原来的问题:

How to get BehaviorSubject value in other component if I type the component url directly?

总体思路是将 BehaviorSubject 引用为可观察对象,因此您始终会收到最新的值;不要使用 .value 在某一时刻提取单个值。