Мне нужно установить значение в одном компоненте (компонент A) и получить значение в другом компоненте (компонент B). Компонент A и компонент B не являются родительским и дочерним.
Я создал общий сервис для обмена данными между двумя компонентами. Я могу установить значение из компонента B, но когда я подписываюсь, чтобы получить значение в компоненте A, оно не запускается.
Вот что я пробовал:
Я добавил Сервис в массив @NgModuleproviders в файл appmodule.ts, чтобы он был доступен для всех компонентов в проекте.
Код услуги:
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;
}
}
Компонент Б: Значение настройки здесь
@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");
}
Компонент А: Получить значение здесь
@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.info('Trigger from ComponentB');
console.info(data);
});
}
Триггер в ComponentA вызывается, когда ComponentA загружается с данными null. Но он не вызывается, когда buttonClick вызывается из ComponentB.
ОБНОВИТЬ:
Я понял проблему, я должен был правильно сформулировать свой вопрос. Когда ComponentA и ComponentB находятся на одной странице (URL), код работает отлично.
Как отправить данные между двумя компонентами, когда они находятся на двух разных страницах (URLS).
Пример: пользователь открывает URL1 (рендеринг ComponentA), у которого есть кнопка, пользователь нажимает кнопку и перенаправляется на URL2 (рендеринг ComponentB) на новой вкладке с кнопкой, когда пользователь нажимает на кнопку, мне нужно передать некоторые данные в ComponentA и закрыть URL2. Мне нужно получить данные, переданные в ComponentB, и использовать их в URL1. Можно ли использовать Сервисы? Если нет, то какой подход мне следует использовать?
Вы можете сделать это stackblitz.com
@trichetriche, я обновил свой вопрос, не могли бы вы проверить.
@TusharWalzade, не могли бы вы указать мне какой-нибудь пример кода или учебник?
@RafaelLima, пожалуйста, посмотрите обновление в моем вопросе.
может это поможет angular.io/guide/component-interaction
Вы пробовали ReplaySubject?
это сработало и для меня в ionic4





В вашем случае вам нужно подписаться на this.valueObs, а не на метод, включающий это, поскольку ваша переменная valueObs имеет наблюдаемый тип, поэтому вы можете напрямую подписаться на это следующим образом:
constructor(private socialService: SocialService, private http: HttpClient) {
this.socialService.getValue().subscribe(data=>{
console.info('Trigger from ComponentB');
console.info(data);
});
this.socialService.valueObs.subscribe(data=>{
console.info('Trigger from ComponentB on Next called');
});
}
valueObs.subscribe метод вообще не вызывается. Также не вызывается метод getValue().subscribe.
getValue method should return observable.
public getValue():Observable<string> {
return this.valueObs.asObservable();
}
Вы можете удалить getValue(), который вызывается в методе setValue().
Компонент:
this.socialService.getValue().subscribe(data=>{
console.info('Trigger from ComponentB');
console.info(data);
});
StackBlitz: https://stackblitz.com/edit/angular-e6uxlo?file=src%2Fapp%2Fhello.component.ts
Тоже пробовал, не работает. Есть ли что-то еще, что я могу упустить?
вместо вызова конструктора() внутри ngOnInit
Обновлен стекблиц
Если вы хотите обмениваться данными между компонентами в Tab. Лучше сохранять данные в локальном хранилище и получать к ним доступ в других местах.
You could effectively use
Subjectalong with the router. So, Here is a live solution for your scenario usingRouter&Subject.
Ваш сервис будет просто содержать -
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.info('Trigger from ComponentB');
console.info(data);
})
Я думаю, вам понадобится ReplaySubject для этого. Ваш компонент, подписавшийся на наблюдаемое, подписывается после того, как значение было испущено, и, следовательно, наблюдаемое больше не испускается, нет никакого значения, которое можно было бы получить от этого наблюдаемого. ReplaySubject заботится о том, чтобы предоставить подписчику все выбросы, которые произошли еще до того, как подписка будет завершена.
Решение @Tushar Walzade работает, потому что оба компонента находятся внутри компонента приложения, поэтому оба наблюдателя подписываются до того, как событие было отправлено. (Подписка происходит внутри конструктора для обоих компонентов, который вызывается перед нажатием кнопки). Но если у вас есть лениво загруженный модуль, например, один компонент будет создан только при загрузке, а затем вы получите данные только в том случае, если вы используете ReplaySubject или оператор rxjs для публикации или совместного использования.
Посмотрите здесь:
публиковать и делиться операторамисубъекты, субъекты поведения и субъекты воспроизведения
Ваш комментарий только что спас мой день. Спасибо!
Если я не ошибаюсь, вам нужно либо subsribe, либо использовать async канал в шаблоне для компонента a, чтобы вы всегда получали последнее значение наблюдаемого потока.
Я создал простое приложение, чтобы продемонстрировать вам это.
Допустим, у нас есть 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)
}
}
Проблема в вашем сервисе. Когда вы устанавливаете данные, субъект уже выдал значение. Делай так.
В вашем сервисе сделайте BehaviorSubject общедоступным и вам больше не нужен getValue метод.
В ComponetA, где вы получаете данные от CompoenetB, сделайте подписку на BehaviorSubject, который является valueObs.
Модифицированный класс обслуживания следующим образом.
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable()
export class SocialService {
public valueObs: BehaviorSubject<string> = new BehaviorSubject<string>(null);
constructor() {}
public setValue(value: string):void {
this.valueObs.next(value);
// this.getValue();
}
// public getValue():Observable<string> {
// return this.valueObs;
// }
}
Укажите минимальный воспроизводимый пример, так как это должно работать.