Call a function on a dynamically loaded component - javascript

I have a MainComponent with a <router-outlet> in which child components are loaded.
On the /messages-url the messagesComponent is loaded. I have added an evenlistener on the MainComponent which fires when the user scrolls in the container in which the <router-outlet> resides like this:
#Component({
selector: "main-component",
template: `
<div (scroll)="onContainerScrollEvent($event)">
<router-outlet></router-outlet>
</div>
`
})
export class MainComponent {
private messagePage: number = 0;
onContainerScrollEvent(event: any) {
this.messagePage += 1;
}
}
When the onContainerScrollEvent fires I want to call a function on the messagesComponent to get some new messages.
I have added an EventEmitter on the messagesComponent which fires on the onInit and which passes itself to the parent event, but <router-outlet> doesn't support that.
UPDATE
Below the answer to my question incorporating Ahmed's answer:
MessageService:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class MessageService {
private messagesPageSource = new Subject<number>();
messagesPage$ = this.messagesPageSource.asObservable();
public setPage(page: number)
{
this.messagesPageSource.next(page);
}
}
MainComponent:
import { Component, OnInit } from '#angular/core';
import { MessageService } from './messages.service';
#Component({
selector: "main-component",
template: `
<div (scroll)="onContainerScrollEvent($event)">
<router-outlet></router-outlet>
</div>
`
})
export class MainComponent {
private pageNumber: number = 1;
constructor(private messageService: MessageService) {
messsageService.messagesPage$.subscribe(p => { });
}
onContainerScroll(event: any) {
this.pageNumber += 1;
this.messageService.setPage(this.pageNumber);
}
}
messagesComponent
import { Component, OnInit } from '#angular/core';
import { MessageService } from './messages.service';
import { Subscription } from "rxjs/Subscription";
#Component({
selector: "messages",
templateUrl: "messages.view.html"
})
export class messagesComponent implements OnInit {
private pageNumber: number = 1;
subscription: Subscription;
constructor(private messageService: MessageService) {
this.subscription = messageService.messagesPage$.subscribe(p => {
this.pageNumber = p;
this.getMessages();
});
}
ngOnInit() {
this.getMessages();
}
private getMessages() {
//Call service to retrieve messages
}
}

Use a bi-directional service as described in the section Parent and children communicate via a service of this angular cookbook:
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
Create a message service which you'll use to communicate between your parent and child component and any other component you want as well.
I'll skip providing a code example as the angular cookbook above has a pretty good example.

Related

How do I push data from a Subject observable to an array in the component?

I am trying to push a message into an array that is already declared as a variable in the component. I am using a service and have created a subject observable to take data from one component and inject it into another component. When I try to push the data onto the array after subscribing to the variable, it's updated temporarily but when I open that component, the data is not pushed. The array updates when I console log from inside the subscribe method but it's reset once I open that component. I don't know what is the problem. This is the code:
Service.ts
import { Injectable } from '#angular/core';
import { User } from './user';
import { Subject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SerService {
private message = new Subject<string>();
sourceMessage$ = this.message.asObservable();
constructor() { }
sendMessage(message: string) {
this.message.next(message);
}
}
Receiver component
import { Component, OnInit } from '#angular/core';
import { SerService } from '../ser.service';
import { User } from "../user";
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
public messages = ['hi', 'hello', 'bye'];
constructor(private _service: Service) { }
ngOnInit() {
this._service.message$
.subscribe(
message => {
this.messages.push(message);
}
);
}
}
Sender Component
import { Component, OnInit } from '#angular/core';
import { SerService } from '../ser.service';
import { User } from '../user';
#Component({
selector: 'app-sign-up',
templateUrl: './sign-up.component.html',
styleUrls: ['./sign-up.component.css']
})
export class SignUpComponent {
userModel = new User('', '', '', '', false);
constructor (private _service : SerService) {}
onSubmit(){
this._service.sendMessage(this.userModel.message);
}
}
I can't update the message array. How do I do this with minimal changes?
You can create a service to send data from one component to another by using BehaviourSubject
Service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable({
providedIn: 'root'
})
export class DataService {
private userDetails = new BehaviorSubject<any>('');
currentUserDetails = this.userDetails.asObservable();
constructor() { }
sendUserDetails(message){
this.userDetails.next(message)
}
}
Sender Component:
import { DataService } from '/services/data.service';
export class SignupComponent implements OnInit {
public userDetails;
constructor(private _dataService: DataService) {}
ngOnInit(){
userDetails = new User('', '', '', '', false);
this._dataService.sendUserDetails(this.userDetails);
}
}
Receiver Component
import { DataService } from '/services/data.service';
export class LoginComponent implements OnInit {
public userDetails;
constructor(private _dataService: DataService) {}
ngOnInit(): void {
this._dataService.currentUserDetails.subscribe(userDetails => this.userDetails = userDetails);
}
Blockquote

Trigger function with event emitter

Is it possible to trigger a function in another component from the current component with EventEmitter, as sort of a callback? For example, after I finish the API request a success function occurs, like so:
#Output() afterAPIRequest = new EventEmitter();
handleSuccess() {
this.afterAPIRequest.emit();
}
Now, can I catch that somehow in another component and trigger another function, something like this?
// when emitted, run this
refreshListIfEmitted() {
this.refreshMyList();
}
use a service
import { Injectable } from '#angular/core';
import { Observable, Subject } from 'rxjs';
#Injectable()
export class MessageService {
private _message: Subject<any>;
constructor() {
this._message = new Subject();
}
get changes(): Observable<any> {
return this._message.asObservable();
}
set message(message: any) {
this._message.next(message);
}
}
component one
import { Component } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-one',
templateUrl: './one.component.html',
styleUrls: ['./one.component.scss'],
})
export class OneComponent {
constructor(private _http: HttpClient, private _message: MessageService) { }
apiRequest(): void {
this._http.get('end-point').subscribe(value => this._message.message = value);
}
}
component two
import { Component } from '#angular/core';
#Component({
selector: 'app-two',
templateUrl: './two.component.html',
styleUrls: ['./two.component.scss'],
})
export class TwoComponent {
constructor(private _message: MessageService) {
this._message.changes.subscribe(value => console.log(value));
}
}

Angular 4 pass data between 2 not related components

I have a questions about passing data in Angular.
First, I don't have a structure as <parent><child [data]=parent.data></child></parent>
My structure is
<container>
<navbar>
<summary></summary>
<child-summary><child-summary>
</navbar>
<content></content>
</container>
So, in <summary /> I have a select that do send value to <child-summary /> and <content />.
OnSelect method is well fired with (change) inside <summary /> component.
So, I tried with #Input, #Output and #EventEmitter directives, but I don't see how retrieve the event as #Input of the component, unless to go on parent/child pattern. All examples I've founded has a relation between component.
EDIT : Example with BehaviorSubject not working (all connected service to API works well, only observable is fired at start but not when select has value changed)
shared service = company.service.ts (used to retrieve company data)
import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class SrvCompany {
private accountsNumber = new BehaviorSubject<string[]>([]);
currentAccountsNumber = this.accountsNumber.asObservable();
changeMessage(accountsNumber: string[]) {
this.accountsNumber.next(accountsNumber);
}
private _companyUrl = 'api/tiers/';
constructor(private http: Http) { }
getSociete(): Promise<Response> {
let url = this._companyUrl;
return this.http.get(url).toPromise();
}
}
invoice.component.ts (the "child")
import { Component, OnInit, Input } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { SrvInvoice } from './invoice.service';
import { SrvCompany } from '../company/company.service';
#Component({
selector: 'invoice',
templateUrl: 'tsScripts/invoice/invoice.html',
providers: [SrvInvoice, SrvCompany]
})
export class InvoiceComponent implements OnInit {
invoice: any;
constructor(private srvInvoice: SrvInvoice, private srvCompany: SrvCompany)
{
}
ngOnInit(): void {
//this.getInvoice("F001");
// Invoice data is linked to accounts number from company.
this.srvCompany.currentAccountsNumber.subscribe(accountsNumber => {
console.log(accountsNumber);
if (accountsNumber.length > 0) {
this.srvInvoice.getInvoice(accountsNumber).then(data => this.invoice = data.json());
}
});
}
//getInvoice(id: any) {
// this.srvInvoice.getInvoice(id).then(data => this.invoice = data.json());
//}
}
company.component.ts (the trigerring "parent")
import { Component, Inject, OnInit, Input } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { SrvCompany } from './company.service';
#Component({
selector: 'company',
templateUrl: 'tsScripts/company/company.html',
providers: [SrvCompany]
})
export class CompanyComponent implements OnInit {
societes: any[];
soc: Response[]; // debug purpose
selectedSociete: any;
ville: any;
ref: any;
cp: any;
accountNumber: any[];
constructor(private srvSociete: SrvCompany)
{
}
ngOnInit(): void {
this.getSocietes();
}
getSocietes(): void {
this.srvSociete.getSociete()
.then(data => this.societes = data.json())
.then(data => this.selectItem(this.societes[0].Id));
}
selectItem(value: any) {
this.selectedSociete = this.societes.filter((item: any) => item.Id === value)[0];
this.cp = this.selectedSociete.CodePostal;
this.ville = this.selectedSociete.Ville;
this.ref = this.selectedSociete.Id;
this.accountNumber = this.selectedSociete.Accounts;
console.log(this.accountNumber);
this.srvSociete.changeMessage(this.accountNumber);
}
}
This is a case where you want to use a shared service, as your components are structured as siblings and grandchildren. Here's an example from a video I created a video about sharing data between components that solves this exact problem.
Start by creating a BehaviorSubject in the service
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
Then inject this service into each component and subscribe to the observable.
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-parent',
template: `
{{message}}
`,
styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
You can change the value from either component and the value will be updated, even if you don't have the parent/child relationship.
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-sibling',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Sibling")
}
}
if component are not related than you need use Service
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
There are two solutions for this.
This can be done through shared service by using observable's.
You can use ngrx/store for this. This is similar to Redux arch. You will be getting data from state.
Here is the simplest example of sharing data between two independent components, using event emitter and service
https://stackoverflow.com/a/44858648/8300620
When you mention non related components, I'm gonna assume that they don't have any parent component. If assumption isn't correct, feel free to read another of my answers where both cases are addressed.
So, as there's no common parent, we can use an injectable service. In this case, simply inject the service in the components and subscribe to its events.
(Just like the next image shows - taken from here - except that we'll inject the service in two Components)
The documentation explains it quite well how to Create and register an injectable service.

Angular2 call a Component from a Service

I am trying to call a Method in my Component from a a Service. What is the proper way to do this? I have tried to use rxjs Subject to create an Observable, but I cannot get it to fire.
import {Subject} from 'rxjs/Subject';
export class MyService {
callComponent = function(value) {
let invokeEvent = new Subject();
invokeEvent.next({some:value})
}
}
and in my Component
export class MyComponent {
constructor(private _myService: MyService) {
this._myService.invokeEvent.subscribe(value => console.log(value))
}
}
Here's the plunker: http://plnkr.co/edit/WKSurRJMXo5JZOPrwSP5?p=preview
Change your service like this
import {Subject} from 'rxjs/Subject';
#Injectable()
export class MyService {
invokeEvent:Subject<any> = new Subject();
callComponent(value) {
this.invokeEvent.next({some:value})
}
}
Don't forget to provide it in your component
#Component({
selector: 'my-component',
template: `
`,
providers: [MyService]
})
export class MyComponent {
constructor(private _myService: MyService) {
this._myService.invokeEvent.subscribe(value => console.log(value));
setTimeout(()=>{
this._myService.callComponent(1);
},1000);
}
}
Also, If you want this service to be a global shared service; put(provide) it in your bootstrap(old) or ngModule so it will share the same singleton instance throughout your app.
you can define Observable in service so that you can subscribe to that Observable from component.
//service
import { Injectable, Inject } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class MyService {
private notify = new Subject<any>();
/**
* Observable string streams
*/
notifyObservable$ = this.notify.asObservable();
constructor(){}
public notifyOther(data: any) {
if (data) {
this.notify.next(data);
}
}
callComponent(value){
this.notify.next({some:value});
}
}
//Component
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Subscription } from 'rxjs/Subscription';
import { MyService } from './my.service';
export class MyComponent {
private subscription: Subscription;
constructor( private _myService: MyService ){
}
ngOnInit() {
this.subscription = this._myService.notifyObservable$.subscribe((value) => {
console.log(value);
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
import {Subject} from 'rxjs/Subject';
export class MyService {
private invokeEvent = new Subject();
invokeEvent$ = this.missionConfirmedSource.asObservable(); //<<< this is important to declare invokeEvent with asObservable();
callComponent = function(value) {
invokeEvent.next({some:value})
}
}
export class MyComponent {
constructor(private _myService: MyService) {
this._myService
.invokeEvent$ //<<< subscribe to invokeEvent$ to get the result
.subscribe(value => console.log(value))
}
}

Passing value between two angular2 component typescript files

I have two components that are not parent and child components but i need to pass value from component A to component B.
example:
src/abc/cde/uij/componentA.ts has variable CustomerId = "ssss"
need to pas that variable customerID to src/abc/xyz/componentB.ts
Simple example:
Component A:
#Component({})
export class ComponentA {
constructor(private sharedService : SharedService) {}
sendMessage(msg : string) {
this.sharedService.send(msg);
}
}
Component B:
#Component({})
export class ComponentB {
constructor(private sharedService : SharedService) {
this.sharedService.stream$.subscribe(this.receiveMessage.bind(this));
}
receiveMessage(msg : string) {
console.log(msg); // your message from component A
}
}
Shared service:
#Injectable()
export class SharedService {
private _stream$ = new Rx.BehaviorSubject("");
public stream$ = this._stream$.asObservable();
send(msg : string) {
this._stream$.next(msg);
}
}
Shared service have to be placed in the same NgModule.
On your service define a setMyProperty() and a getMyProperty(). Then setMyProperty with a value from Component A. ComponentB then getMyProperty where you return the value...
You have to inject the service into both components.
You could give this a shot. Its pretty simple and straight forward.
I just followed THIS example and made some changes so that it could be siblings talking instead of parent/child.
my-service.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class MyService {
// Observable string sources
private myAnnouncedSource = new Subject<string>();
// Observable string streams
myAnnounced$ = this.myAnnouncedSource.asObservable();
// Service message commands
announceItem(item: string) {
this.myAnnouncedSource.next(item);
}
}
my-comp1.component.ts
import { Component } from '#angular/core';
import { MyService } from './my-service.service';
#Component({
selector: 'my-compA',
template: `...`,
providers: [MyService]
})
export class MyComponentA {
constructor(private myService: MyService) {
}
announceToOtherComps() {
let sharedItem = "shibby";
this.myService.announceItem(sharedItem);
}
}
my-comp2.component.ts
import { Component, Input, OnDestroy } from '#angular/core';
import { MyService } from './my-service.service';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'my-compB',
template: `...`,
providers: [MyService]
})
export class MyComponentB implements OnDestroy {
sharedItem = '<no data>';
subscription: Subscription;
constructor(private myService: MyService) {
this.subscription = myService.myAnnounced$.subscribe(
item => {
this.sharedItem = item;
});
}
ngOnDestroy() {
// prevent memory leak when component destroyed
this.subscription.unsubscribe();
}
}
<component-a [id]="product.id"></component-a>
In the component-a ts file .Use it like below
export class ComponentA implements OnInit {
#Input() // <------------
id: number;
(...)
}

Categories