Angular 6数据共享服务订阅未触发

时间:2019-02-18 07:24:05

标签: angular typescript callback singleton angular-components

我需要在一个组件(组件A)中设置“值”,并在另一个组件(组件B)中接收该值。组件A和组件B不是父级和子级。

我创建了一个共享服务以在两个组件之间共享数据。我可以从组件B设置值,但是当我订阅以在组件A中获取值时,它不会被触发。

这是我尝试过的:

我在@NgModule文件的providers appmodule.ts数组中添加了Service,以便该服务可用于项目中的所有组件。

服务代码:

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from '../../node_modules/rxjs';

@Injectable({
  providedIn: 'root'
})
export class SocialService {

  constructor() { }

  private valueObs: BehaviorSubject<string> = new BehaviorSubject<string>(null);

 public setValue(value: string):void {
     this.valueObs.next(value);
     this.getValue();
 }

 public getValue():Observable<string> {
     return this.valueObs;

 }

}

组件B:在此处设置值

@Component({
  selector: 'app-componentb',
  templateUrl: './componentb.component.html',
  styleUrls: ['./componentb.component.scss']
})


constructor(private socialService: SocialService, private http: HttpClient) {



}

 buttonClick()
 {

    this.socialService.setValue("linked successfully");
 }

组件A:在此处获得价值

@Component({
  selector: 'app-componenta',
  templateUrl: './componenta.component.html',
  styleUrls: ['./componenta.component.scss']
})


constructor(private socialService: SocialService, private http: HttpClient) {

       this.socialService.getValue().subscribe(data=>{

    console.log('Trigger from ComponentB');
    console.log(data);

});

}

当ComponentA装入数据为null时,将调用ComponentA中的触发器。但是,当从ComponentB调用buttonClick时,不会调用它。

更新

我发现了问题,我应该正确说出我的问题。 当ComponentA和ComponentB在相同的Page(URL)中时,代码可以完美工作。

当两个组件位于2个不同的页面(URLS)中时,如何在两个组件之间发送数据。

示例:用户打开具有按钮的URL1(渲染组件A),用户单击按钮,然后在具有按钮的新标签页中重定向到URL2(渲染组件B),当用户单击按钮时,我需要将一些数据传递给ComponentA并关闭URL2。我需要检索在ComponentB中传递的数据并在URL1中使用它。可以使用服务吗?如果没有,我应该采用哪种方法?

6 个答案:

答案 0 :(得分:4)

  

您可以有效地与路由器一起使用Subject。因此, Here 是使用RouterSubject的实时解决方案。

您的服务将仅包含-

public setValue(value: string): void {
    this.valueObs.next(value);
}

public getValue(): Observable < string > {
    return this.valueObs;
}

然后,您的ComponentB将包含-

buttonClick() {
    this.socialService.setValue("linked successfully");
    this.router.navigate(['/a']);
}

而且,您的ComponentA的构造函数将包含-

this.socialService.getValue().subscribe(data => {
    this.dataFromB = data;
    console.log('Trigger from ComponentB');
    console.log(data);
})

答案 1 :(得分:1)

我认为您需要一个ReplaySubject。您的订阅可观察对象的组件在发出值之后又不再发出可观察对象,因此该可观察对象没有任何价值。 ReplaySubject负责为订阅者提供即使在订阅完成之前发生的所有发射。

@Tushar Walzade解决方案有效,因为两个组件都在app组件内,所以两个观察者都在事件发出之前进行了订阅。 (订阅发生在两个组件的构造函数中,在您单击按钮之前调用)。但是,例如,如果您有一个延迟加载的模块,则只有在加载时才创建一个组件,然后仅当您使用ReplaySubject或rxjs运算符publish或share时才能获取数据

在这里看看:

publish and share operators subjects, behavior subjects and replay subjects

答案 2 :(得分:0)

在您的情况下,您需要订阅this.valueObs而不是包含此内容的方法,因为您的变量valueObs是可观察的类型,因此您可以像这样直接订阅-

constructor(private socialService: SocialService, private http: HttpClient) {

  this.socialService.getValue().subscribe(data=>{
    console.log('Trigger from ComponentB');
    console.log(data);
  });
  this.socialService.valueObs.subscribe(data=>{
    console.log('Trigger from ComponentB on Next called');
  });

}

答案 3 :(得分:0)

getValue method should return observable.

public getValue():Observable<string> {
     return this.valueObs.asObservable();
}

您可以删除在setValue()方法中被调用的getValue()。

组件:

this.socialService.getValue().subscribe(data=>{
    console.log('Trigger from ComponentB');
    console.log(data);
  });

StackBlitz:https://stackblitz.com/edit/angular-e6uxlo?file=src%2Fapp%2Fhello.component.ts

答案 4 :(得分:0)

如果我没有记错的话,您需要在组件a的模板中使用subsribe或在模板中使用async管道,以便始终获取可观察流的最新值。

我已经创建了一个简单的应用程序向您演示。

比方说,我们有2条路线,每条路线都呈现了相关的成分(a,b)。

我们使用一项服务在两个组件之间共享数据。

Injectable()
export class MyService {

  private _data = new BehaviorSubject<string>(null);

  setValue(value: string): void {
    this._data.next(value)
  }

  get data(): Observable<string> {
    return this._data;
  }

}

然后在组件a中,我们侦听服务的可观察流的变化。

@Component({
  selector: 'a-component',
  template: `Component A <br> Data: <pre>{{data}}</pre>`,
})
export class AComponent {
  @Input() name: string;

  data: Observable<string>;

  constructor(private myService: MyService) {
    this.myService.data.subscribe(data => this.data = data);
  }

在组件b中,我们仅通过服务setValue方法编辑数据。

@Component({
  selector: 'b-component',
  template: `Component B <br> 
  <input type="text" placeholder="set data for component a" [(ngModel)]="modifiedData" (ngModelChange)="editServiceData($event)"/>`,
})
export class BComponent  {
  @Input() name: string;
  modifiedData;

  constructor(private myService: MyService) {}

  editServiceData(data: string) {
    this.myService.setValue(data)
  }
}

Here's the snippet which demonstrates the above flow

答案 5 :(得分:0)

问题在于您的服务。设置数据时,主题已发出该值。这样做吧。

  1. 在您的服务中将import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private ArrayList<String> data = new ArrayList<String>(); private RecyclerViewWithHeaderFooterAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initializeData(); initializeRecyclerView(); } private void initializeRecyclerView() { mRecyclerView = findViewById(R.id.dummy_rv); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); adapter = new RecyclerViewWithHeaderFooterAdapter(this, data); mRecyclerView.setAdapter(adapter); } private void initializeData() { for (int i = 0; i < 10; i++) data.add("Position :" + i); } } 设为公开,并且您不再需要BehaviorSubject方法。

  2. getValue中,您从ComponetA接收数据,订阅CompoenetB的{​​{1}}。

修改后的Service类如下。

BehaviorSubject