本文介绍了Angular Observable 不实时显示值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有 2 个组件(菜单和标题)和一个服务.我想要做的是,当他们从菜单中选择一个项目时,菜单将文本发送到服务,服务将其发送到标题并更改它.我已经设法将它发送到服务,但我不知道为什么在带有 Observable 的标头中它没有更新.

服务:

import { Injectable } from '@angular/core';从 'rxjs' 导入 { Observable };导入 'rxjs/add/observable/from';从 'rxjs' 导入 { of };@Injectable()导出类 passVarHelper{当前索引:字符串;构造函数(){this.currentIndex = "标题";}getIndex(): Observable {返回(this.currentIndex);}更改索引(索引:字符串){this.currentIndex = 索引;console.log(this.currentIndex);}}

标题:

import { Component, OnInit } from '@angular/core';从 'src/app/helpers/passVar.helper' 导入 { passVarHelper };@成分({选择器:'app-headerpage',templateUrl: './headerpage.component.html',styleUrls: ['./headerpage.component.css'],提供者:[passVarHelper]})导出类 HeaderpageComponent 实现 OnInit {索引:字符串;构造函数(私有_passVarHelper:passVarHelper){}ngOnInit() {this.loadIndex();}加载索引(){this._passVarHelper.getIndex().subscribe(响应=>{如果(响应){控制台日志(响应);this.index = 响应;}},错误 =>{控制台日志(错误);});}}

菜单:

import { Component, OnInit, Input, OnDestroy } from '@angular/core';从 'src/app/helpers/passVar.helper' 导入 { passVarHelper };@成分({选择器:'app-asidenavbar',templateUrl: './asidenavbar.component.html',styleUrls: ['./asidenavbar.component.css'],提供者:[passVarHelper]})导出类 AsidenavbarComponent 实现 OnInit {构造函数(私有_passVarHelper:passVarHelper){}ngOnInit() {}菜单点击(){var indexMenu = document.getElementById('menu').innerHTML;this._passVarHelper.changeIndex(indexMenu);}}

标题 HTML

<div class="container-fluid"><div class="row mb-2"><div class="col-sm-6"><h1 class="m-0 text-dark">{{index}}</h1>

<div class="col-sm-6"><ol class="breadcrumb float-sm-right"><li class="breadcrumb-item"><a href="#">Volver</a></li></ol>

解决方案

Observable.of 是一次性事件,根据文档:

创建一个 Observable,它发出一些你指定为参数的值,一个接一个地发出,然后发出一个完整的通知.

关于完成通知的最后一部分很重要,因为一旦完成(第一次执行)它会发出 complete 事件并且不再发出.您需要做的是使用 SubjectBehaviorSubject 以获得恒定的事件流.

尝试这样的事情:

@Injectable()导出类 PassVarHelper {当前索引:字符串;currentIndexObs: BehaviorSubject;构造函数(){this.currentIndex = "标题";this.currentIndexObs = new BehaviorSubject(this.currentIndex);}更改索引(索引:字符串){this.currentIndex = 索引;this.currentIndexObs.next(this.currentIndex);}}

然后loadIndex方法:

loadIndex() {this._passVarHelper.currentIndexObs.subscribe(响应=>{如果(响应){控制台日志(响应);this.index = 响应;}},错误 =>{控制台日志(错误);});}

这是演示此行为的 stackblitz 示例.

EDIT:好的,我知道问题出在哪里...它在 providers 数组中,您不能在 header 中提供服务,也不能menu 组件,您必须在它们上方的组件中提供它(例如 app.moduleapp.component).如果您在这两个组件中都提供服务,您将获得不同的服务实例,而您想要实现的功能将不起作用.

因此,在其中一个(如果另一个是第一个的孩子)或 app.module.ts providerspassVarHelper> 数组(检查我提供的 stackblitz 示例,我在 app.module.ts 中提供服务)并且在那个地方(按顺序)获得相同的实例).

嗯,Angular 有一个叫做 provider scope 的东西.例如,看看这个定义:

当您在根级别提供服务时,Angular 会创建一个共享的服务实例,并将其注入到任何需要它的类中.

这意味着当您在根级别提供服务时,无论您在何处使用 DI 注入该服务,您都将获得该服务的单个实例(例如在构造函数中注入它时).

但是,您也可以在任何其他组件中提供服务.通过在组件中提供服务,您实际上将服务仅限于该组件和该组件内的组件.本质上,通过在组件中提供服务,当您将其注入该组件时,您将获得该服务的一个新实例(并且其所有子级都将拥有该实例,除非他们自己提供该服务).

因此,该服务的单个实例用于组件及其所有子组件,但如果在任何子组件中提供,则该子组件及其所有子组件都将获得新实例.

希望这能让它更清楚一点.

有趣的阅读材料:

I have 2 components (Menu and Header) and one Service. What I want to do is that when they select an item from the menu, the menu sends the text to the service and the service sends it to the header and changes it. I already managed to send it to the service, but I do not know why in the header with the Observable it does not update.

Service:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import 'rxjs/add/observable/from';
import { of } from 'rxjs';

@Injectable()
export class passVarHelper{

    currentIndex: string;
    constructor(

    ){
        this.currentIndex = "Title";
    }


    getIndex(): Observable <string> {
        return of(this.currentIndex);
    }

    changeIndex(index:string){
        this.currentIndex = index;
        console.log(this.currentIndex);
    }



}

Header:

import { Component, OnInit } from '@angular/core';
import { passVarHelper } from 'src/app/helpers/passVar.helper';

@Component({
  selector: 'app-headerpage',
  templateUrl: './headerpage.component.html',
  styleUrls: ['./headerpage.component.css'],
  providers: [passVarHelper]
})
export class HeaderpageComponent implements OnInit {
  index:string;
  constructor(private _passVarHelper: passVarHelper) {

   }

  ngOnInit() {
    this.loadIndex();
  }

  loadIndex(){
    this._passVarHelper.getIndex().subscribe(
      response  => {
        if(response){
          console.log(response);
          this.index = response;
        }
      },
      error => {
        console.log(<any>error);
      }
    );
  }

}

Menu:

import { Component, OnInit, Input, OnDestroy } from '@angular/core';
import { passVarHelper } from 'src/app/helpers/passVar.helper';

@Component({
  selector: 'app-asidenavbar',
  templateUrl: './asidenavbar.component.html',
  styleUrls: ['./asidenavbar.component.css'],
  providers: [passVarHelper]
})
export class AsidenavbarComponent implements OnInit {


constructor(private _passVarHelper: passVarHelper) {
  }

  ngOnInit() {

  }


  menuClick(){
    var indexMenu = document.getElementById('menu').innerHTML;
    this._passVarHelper.changeIndex(indexMenu);

  }


}

Header HTML

<div class="content-header">
  <div class="container-fluid">
    <div class="row mb-2">
      <div class="col-sm-6">
        <h1 class="m-0 text-dark">{{index}}</h1>
      </div>
      <div class="col-sm-6">
        <ol class="breadcrumb float-sm-right">
          <li class="breadcrumb-item"><a href="#">Volver</a></li>
        </ol>
      </div>
    </div>
  </div>
</div>
解决方案

Observable.of is one time event, as per documentation:

The last part about complete notification is important since once it's done (it's executed for the first time) it emits complete event and doesn't emit anymore. What you need to do is use Subject or BehaviorSubject in order to get constant stream of events.

Try something like this:

@Injectable()
  export class PassVarHelper {
  currentIndex: string;
  currentIndexObs: BehaviorSubject<string>;

  constructor() {
    this.currentIndex = "Title";
    this.currentIndexObs = new BehaviorSubject<string>(this.currentIndex);
  }

  changeIndex(index: string) {
    this.currentIndex = index;
    this.currentIndexObs.next(this.currentIndex);
  }
}

Then loadIndex method:

loadIndex() {
  this._passVarHelper.currentIndexObs.subscribe(
    response => {
      if (response) {
        console.log(response);
        this.index = response;
      }
    },
    error => {
      console.log(<any>error);
    }
  );
}

Here is stackblitz example demonstrating this behavior.

EDIT: Ok, I see where the problem is... It is in providers array, you cannot provide service in header nor menu components, you have to provide it in component that is above them (like app.module or app.component). If you provide services in both of these components you will get different instance of the service and what you want to achieve will not work.

Therefore, provide passVarHelper in either one of them (if another is child of the first one) or in app.module.ts providers array (check the stackblitz example I provided, I provided service in app.module.ts) and only in that one place (in order to get the same instance).

Hmm, Angular has something called provider scope. For example, take a look at this definition:

That means that when you provide service at the root level, you will get a single instance of that service wherever you inject it using DI (like when you inject it in constructor).

But, you can also provide services in any other component. By providing a service in the component, you are actually limiting the service only to that component and components inside that component. Essentially, by providing a service in component, you will get a new instance of that service when you inject it in that component (and all its children will have that one instance, unless they themselves provide the service).

So, single instance of the service is used for the component and all its children, but if provided in any child component, that child and all its children will get the new instance.

Hope this makes it a bit more clearer.

Interesting reading material:

这篇关于Angular Observable 不实时显示值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 13:30