I am trying to use Publish and Subscribe from ionic-angular.
subscribe.js
this.events.subscribe('done:eventA', (userEventData) => {
//Perform some operations
this.startEventB();
this.events.unsubscribe('done:eventA'); <---
}
this.events.subscribe('done:eventB', (userEventData) => {
//Perform some operations
this.startEventA();
}
this.events.subscribe('done:eventA', (userEventData) => {
//Perform some operations
this.startEventC();
}
startEventB(){
this.events.publish('done:eventB', data);
}
startEventA(){
this.events.publish('done:eventA', data);
}
The first time Event A is publish I want to perform startEventB()
The second time Event A is publish I want to perform startEventC() so I tried to unsubscribe from the first part.
But when I unsubscribe, all my subscriptions are gone.
Can I know whats a good way to subscribe and unsubscribe from events?
If you don't pass a second parameter to the unsubscribe, all subscribers are removed. If you want to unsubscribe a given subscriber, you must subscribe and unsubscribe the very same object, as explained here.
Another solution I prefer, is to share a service between the components that has one of the RxJS subject types. The source class can publish using a service function that does next on the subject, and the target class can observe the stream obtaining the subject as an observable.
Here you have an example of this kind of service:
import {Injectable} from '#angular/core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {Booking} from '../../common/model/booking';
#Injectable()
export class BookingEventsService
{
private _newChannel: Subject<Booking> = new Subject<Booking>();
constructor()
{
}
publishNew( booking: Booking )
{
this._newChannel.next( booking );
}
newChannel(): Observable<Booking>
{
return this._newChannel.asObservable();
}
}
Hope it helps. Regards,
Xavi
Related
I've got a component A, which sends a value to a service method (via calling it), and then I tried to create a subject and 'observe' it from component B, thus triggering an action on component B from component A.
This is what I did:
Component A sends the data:
this.service.method(id_estado)
Which is recieved by service:
import { Injectable } from '#angular/core';
import { BehaviorSubject, of, Observable, Subscription, Subject } from 'rxjs';
export class service {
estado: any;
subject = new Subject<any>();
constructor( private api: ApiService ) {
}
service ( id_estado ){
let subject = new Subject<any>();
this.subject.next(estado)
}
}
And in component B I'm trying to observe it like this:
this.service.subject.subscribe( (data) => {
console.log(data);
});
Component B's part takes place inside a method. Is this the cause of the problem? I can't get any data to show on my console.log
let subject = new Subject<any>();
this.subject.next(estado)
- should be moved to constructor to be initialized.
or just execute service in constructor:
import { Injectable } from '#angular/core';
import { BehaviorSubject, of, Observable, Subscription, Subject } from 'rxjs';
export class service {
estado: any;
subject = new Subject<any>();
constructor( private api: ApiService ) {
service(true); // or some value you need.
}
service (id_estado){
this.subject.next(estado);
}
}
In this case you can remove initialization subject = new Subject<any>(); before constructor.
In your case you've just got Subject, but .next() wasn't executed.
Component B's part takes place inside a method. Is this the cause of
the problem?
Yes, that's the problem. If that method isn't called, then you aren't subscribing to the Subject and that is why no data is received.
Move this subscribing part to ngOnInit of Component B.
Also in the service, you are creating a new Subject whenever that method is called.
Subject is already defined at the beginning, no need to do it again.
After this change, your service should look like:
export class service {
estado: any;
subject = new Subject<any>();
constructor( private api: ApiService ) {
}
service(id_estado) {
this.subject.next(estado);
}
}
Check out this StackBlitz where you can see components communicating in real time using Subject.
you should try event emitter service of angular, in which you subscribe your data in pass that data value in any component using event emitter service.
Disclaimer: I'm super new to both Angular and RXJS.
I have a simple form from which I'm trying to create an observable. This will look for submit events and update some value in the component. However, I'm getting a this._subscribe is not a function error.
<form (submit)='submitForm()'>
<button type='submit'>Submit</button>
</form>
My component
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs';
import UtilsHelperService from '../services/utils-helper.service';
#Component({...stuffs...})
export class HomeComponent implements OnInit {
formSubmit: Observable<any>;
counter = 0;
constructor() { }
ngOnInit() {
const form = document.getElementsByTagName('form')[0];
this.formSubmit = Observable.create(form, 'submit');
}
submitForm() {
this.formSubmit.subscribe(
UtilsHelperService.formSubmitObserver(this.counter));
}
}
And my utils-helper.service.ts helper class...
import {Injectable} from '#angular/core';
#Injectable({
providedIn: 'root'
})
export default class UtilsHelperService {
static formSubmitObserver(counter) {
return {
next: (value) => {
counter++;
},
error: err => console.log(err),
complete: () => console.log('complete')
}
}
}
I see that the formSubmit observer is created fine.
I have the UtilsHelperService.formSubmitObserver method that returns an observer object with the 3 necessary methods.
So, I'm not sure whether if it's the Angular stuffs I'm doing wrong (which I guess not) or its the RXjs stuff. Thank you for your time reading it :)
Take a look at FormGroup. Its 'valueChanges' property is an observable you can subscribe to.
FormGroup formGroup;
// populate your formGroup (https://angular.io/guide/reactive-forms#step-1-creating-a-formgroup-instance)
formGroup.valueChanges.subscribe(// do whatever you want);
There were 2 things I did to solve the issue:
Using fromEvent instead of Observable.create to create observable from submit event. This way the subscription didn't threw error (investigating why..)
Updating component property from service won't work as the services are singletons. You either have to use eventemitter or use AngularJS styles dot rule. For this case, I added all the helper logic in the component itself.
ngOnInit() {
const form = document.getElementsByTagName('form')[0];
this.formSubmit = fromEvent(form, 'submit');
this.formSubmit.subscribe((submitEvent) => {
this.counter++;
this.formSubmitted.emit(this.counter);
})
}
With this I can remove the submitForm method from the component and template and the helper method from the service.
Searched for a solution in other questions but nothing helped me..
I wish to redirect to url like,
this.router.navigateByUrl('/products');
In which i need to pass the array and need to get it it in the component which has the active link products using skip location change without showing anything in url.
Array will be like,
products = [{"id":1,"name":"Product One","id":2,"name":"Product Three","id":3,"name":"Product Six"}]
I need to pass this entire array in router link and need to retrieve it in another component (products) active link using skipLocation Change true..
Tried with sharedService but i am getting issue of data loading at right point of time and hence i decided to use via router link..
If this is not a good approach, kindly suggest other alternative without using sharedservice..
You can use Angular Services for a large data.
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class ExampleService {
private subject = new Subject<any>();
updateRouteData(data) {
this.subject.next(data);
}
routeData(): Observable<any> {
return this.subject.asObservable();
}
}
In your components;
For set route data;
import { ExampleService } from '/example.service'
export class ComponentOne{
constructor(private exampleService:ExampleService){
this.exampleService.updateRouteData(data)
}
You can pass data like;
import { ExampleService } from '/example.service'
export class ComponentTwo{
constructor(private exampleService:ExampleService){
this.exampleService.routeData().subscribe(data => {
console.log(data)
})
}
I have a ShareService in angular 2,
******************************shareService*************************
import { BehaviorSubject , Subject} from 'rxjs/Rx';
import { Injectable } from '#angular/core';
#Injectable()
export class shareService {
isLogin$:BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
CheckUser = this.isLogin$.asObservable();
public isLogin (bool){
this.isLogin$.next(bool);
}
}
and its my another component and subscibe the CheckUser;
***********************another Component*******************************
_shareService.CheckUser.subscribe((val) =>{
*********all of this scope execute for several times just i have one another component and one next function*******
this.isLogin = val;
alert(val);
if(this.isLogin){
console.log("req req req");
this.MyBasket();
}
else if(this.ext.CheckLocalStorage("ShopItems")){
this.ShopItems = JSON.parse(localStorage.getItem("ShopItems"));
setTimeout(() => {
_shareService.sendShopItems(this.ShopItems);
},100);
}
});
my problem is i execute once this.isLogin$.next(bool) but subscribe function execute twice or several times !!!! my basket function is an xhr request this means when user loged in i get the several request to server!!!i cant fix it...i dont know this problem is for angular 2 or not,Anyone have this problem??
last a few days i Involved in this problem!
The problem is that your shareService is getting multiple instances.
One of the solutions is forcing the service to be a singleton.
Something like this should work:
import { provide, Injectable } from '#angular/core';
#Injectable()
export class shareService {
private static instance: shareService = null;
// Return the instance of the service
public static getInstance(/*Constructor args*/): shareService {
if (shareService.instance === null) {
shareService.instance = new shareService(/*Constructor args*/);
}
return shareService.instance;
}
constructor(/*Constructor args*/) {}
}
export const SHARE_SERVICE_PROVIDER = [
provide(shareService, {
deps: [/*Constructor args dependencies*/],
useFactory: (/*Constructor args*/): shareService => {
return shareService.getInstance(/*Constructor args*/);
}
})
];
Everything that is required on your current constructor should be placed where it says constructor args
Now on your components you use the service like this:
#Component({
providers: [SHARE_SERVICE_PROVIDER]
})
And then you can call it like you usually do.
Another solution would be injecting your current service on the main component of the app. See here for more info.
The problem is that the service is singleton and the component subscribe to it each time it created or (I don't see the full code) at the point the
_shareService.CheckUser.subscribe
is placed , so CheckUser should be a method that returns an Observable . if you have plunkr I can edit it .
Another semantic problem is that the observable should end with $ and not the BehaviorSubject.
I know how to raise an event with the EventEmitter. I can also attach a method to be called if I have a component like this:
<component-with-event (myevent)="mymethod($event)" />
When I have a component like this, everything works great. I moved some logic into a service and I need to raise an event from inside the Service. What I did was this:
export class MyService {
myevent: EventEmitter = new EventEmitter();
someMethodThatWillRaiseEvent() {
this.myevent.next({data: 'fun'});
}
}
I have a component that needs to update some value based on this event but i can't seem to make it work. What I tried was this:
//Annotations...
export class MyComponent {
constructor(myService: MyService) {
//myService is injected properly and i already use methods/shared data on this.
myService.myevent.on(... // 'on' is not a method <-- not working
myService.myevent.subscribe(.. // subscribe is not a method <-- not working
}
}
How do i make MyComponent subscribe to the event when the service that raises it is not a component?
I'm on On 2.0.0-alpha.28
EDIT: Modified my "working example" to actually work, so focus can be put on the not-working part ;)
Example code:
http://plnkr.co/edit/m1x62WoCHpKtx0uLNsIv
Update: I have found a better/proper way to solve this problem using a BehaviorSubject or an Observable rather than an EventEmitter. Please see this answer: https://stackoverflow.com/a/35568924/215945
Also, the Angular docs now have a cookbook example that uses a Subject.
Original/outdated/wrong answer: again, don't use an EventEmitter in a service. That is an anti-pattern.
Using beta.1... NavService contains the EventEmiter. Component Navigation emits events via the service, and component ObservingComponent subscribes to the events.
nav.service.ts
import {EventEmitter} from 'angular2/core';
export class NavService {
navchange: EventEmitter<number> = new EventEmitter();
constructor() {}
emitNavChangeEvent(number) {
this.navchange.emit(number);
}
getNavChangeEmitter() {
return this.navchange;
}
}
components.ts
import {Component} from 'angular2/core';
import {NavService} from '../services/NavService';
#Component({
selector: 'obs-comp',
template: `obs component, item: {{item}}`
})
export class ObservingComponent {
item: number = 0;
subscription: any;
constructor(private navService:NavService) {}
ngOnInit() {
this.subscription = this.navService.getNavChangeEmitter()
.subscribe(item => this.selectedNavItem(item));
}
selectedNavItem(item: number) {
this.item = item;
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
#Component({
selector: 'my-nav',
template:`
<div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
<div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>
`,
})
export class Navigation {
item = 1;
constructor(private navService:NavService) {}
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navService.emitNavChangeEvent(item);
}
}
Plunker
Using alpha 28, I accomplished programmatically subscribing to event emitters by way of the eventEmitter.toRx().subscribe(..) method. As it is not intuitive, it may perhaps change in a future release.
Sometime quick fix of library cause that added event import like
import { EventEmitter } from 'events';
You must change it with core libray using subscribe
import { EventEmitter } from '#angular/core';