I want to send the value from one component to another, they are not related so all solutions are saying that I must use shared service to do that. But these services are using templates (if I'm right). Is there a way to do this sharing without services?
I want to send the BMI value from homepage.component.ts to result.component.ts.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight
console.log(this.bmi); //this is the calculated value to send
}
}
result.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
constructor() { }
//I want to get the bmi here
ngOnInit(): void {
}
}
There are Two ways to communicate between unrelated components in angular:
1 - Through services, you have to understand where to inject it, in your case I think it should be injected in root, so try this with your service ( follow this tutorial to implement your service, just add my code instead of theirs )
#Injectable({
providedIn: 'root',
})
2 - Through a store ( a lot of boilerplate coding, to use if you have complexe states to keep synchronized through the whole app, by the way the store is basically a service )
If your components are not related then you can create a shared service between them. Then, you need to use dependency injection to communicate between these components. So, there is a great Angular tutorial which describes how to do it.
The service code would look like this:
#Injectable()
export class FooService {
constructor( ) { }
private yourData;
setData(data){
this.yourData = data;
}
getData(){
let temp = this.yourData;
this.clearData();
return temp;
}
}
and sender component:
import { Router } from '#angular/router';
import { FooService} from './services/foo.service';
export class SenderComponent implements OnInit {
constructor(
private fooService: FooService,
private router:Router) {}
somefunction(data){
this.fooService.setData(data);
this.router.navigateByUrl('/reciever');//as per router
}
}
and subscriber:
import { Router } from '#angular/router';
import { TransfereService } from './services/transfer.service';
export class RecieverComponent implements OnInit {
data;
constructor(
private fooService: FooService){
}
ngOnInit() {
data = this.transfereService.getData();
console.log(`data: `, data)
}
}
Solution: To pass the data from one component to another we can store it in a session storage or a local storage and then access it in other components from that storage. Here I have provided a sample code using local storage for your reference.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
data:string='';
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight;
this.data=localStorage.setItem('bmi',this.bmi);
console.log(this.bmi); //this is the calculated value to send
}
}
resultcomponent.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
data:any;
constructor() { this.data=localstorage.getItem('bmi')}
//Access the bmi using the data variable here
ngOnInit(): void {
}
}
Related
Below are the files of a library named posts-lib which makes http call inside posts.services.ts file and receives a list of posts and display them onto screen. It also consists a component named title.component.ts which is dependent on posts.services.ts and is responsible for displaying content on screen.
All of this works fine, but incase I want to move posts.service.ts folder out of the library and put it inside the app then how can I transfer the data from file which is outside of the library to the file title.component.ts which is dependent on it.
title.component.html
<h1>Testing titles api call</h1>
<ul>
<li *ngFor="let item of data">{{item.title}}</li>
</ul>
title.component.ts
import { Component, OnInit } from '#angular/core';
import { PostsService } from '../posts.service';
#Component({
selector: 'lib-tilte',
templateUrl: './tilte.component.html',
styleUrls: ['./tilte.component.css']
})
export class TilteComponent implements OnInit {
data: any;
constructor(private postData: PostsService) { }
ngOnInit() {
this.postData.getPosts().subscribe((result) => {
console.warn("reult",result);
this.data = result;
})
}
}
posts-lib.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'lib-posts-lib',
template: `
<p>
posts-lib works!
</p>
`,
styles: [
]
})
export class PostsLibComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
posts-lib.module.ts
import { NgModule } from '#angular/core';
import { PostsLibComponent } from './posts-lib.component';
import { TilteComponent } from './tilte/tilte.component';
import { HttpClientModule } from "#angular/common/http";
import { CommonModule } from '#angular/common'
#NgModule({
declarations: [
PostsLibComponent,
TilteComponent
],
imports: [
HttpClientModule,
CommonModule
],
exports: [
PostsLibComponent,
TilteComponent
]
})
export class PostsLibModule { }
posts.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from "#angular/common/http";
#Injectable({
providedIn: 'root'
})
export class PostsService {
url = "https://jsonplaceholder.typicode.com/posts";
constructor(private http: HttpClient) { }
getPosts() {
return this.http.get(this.url);
}
}
public-api.ts
export * from './lib/tilte/tilte.component';
export * from './lib/posts-lib.service';
export * from './lib/posts-lib.component';
export * from './lib/posts-lib.module';
export * from './lib/posts.service';
Ignoring all the issues the commenters are making - all valid - it sounds like you want to just remove the dependency on the service.
Or not, actually.
Yay, options!
Remove usage of the service
Just turn the component around from getting its own data, to being given its data. I.e. #Input.
Still with #Input, but instead, input the service itself rather than the values.
So either:
#Input() public data: any;
or
#Input() public set service(value: PostsService) {
this.postsService = value;
this.getData();
}
private getData(): void {
this.postsService.getPosts().subscribe(...);
}
Either way if you're moving the service out and no longer expecting the service and component to work as a functional pair within a system, you have to extract the component and feed it information instead with #Inputs.
Whether that's just feeding it the data from [a wrapper] service, or feeding it the service itself from wherever it now lives, you still need to give it to it.
I recently ran into a problem and can't really figure out what's wrong with my code at this point, hopefully someone of you can help me.
All I am trying to do is changing the value of my BehaviorSubject with a function but it isn't working out.
chat.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class ChatService {
chatId = new BehaviorSubject<number>(0);
constructor() {
this.chatId.next(1);
}
changeChatId(chatId: number) {
console.log(chatId);
this.chatId.next(chatId);
}
}
So the subscribers get the default as well as the changed chatId from the constructor. But as soon as I try to change it with the changeChatId function nothing happens at all. The right id's get passed into the function I already debugged that but the line this.chatId.next(chatId) doesn't seem to do anything.
ADD
These are the other components the service is currently used in.
chat-message-list
import { Component, OnInit, Input} from '#angular/core';
import { ChatService } from "../../../shared/services/chat.service";
#Component({
selector: 'app-chat-message-list',
templateUrl: './chat-message-list.component.html',
styleUrls: ['./chat-message-list.component.css'],
providers: [ChatService]
})
export class ChatMessageListComponent implements OnInit {
chatId: number;
constructor(private chat: ChatService) { }
ngOnInit() {
this.chat.chatId.subscribe(
chatId => this.updateMessageList(chatId)
);
}
}
chat-item
import { Component, OnInit, Input} from '#angular/core';
import { User } from '../../../shared/models/user.model';
import { ChatService } from '../../../shared/services/chat.service';
#Component({
selector: 'app-chat-list-item',
templateUrl: './chat-list-item.component.html',
styleUrls: ['./chat-list-item.component.css'],
providers: [ChatService]
})
export class ChatListItemComponent implements OnInit {
#Input()
user: User;
constructor(private chat: ChatService) { }
ngOnInit() {
}
onChatItemSelected(){
this.chat.changeChatId(this.user.id);
}
}
You need to make your ChatService a singleton (shared) service. Add it to the providers of your ngModule. This allows all the components that use the ChatService to share the same service instance.
#NgModule({
providers: [ChatService]
})
And remove it from your components providers. When you are adding it to your components providers, that component gets its own instance of ChatService which can not be used by other components.
I wanted to send data using subject to another component (for a earning purpose). I am not able to fetch back the data. Here is my code:
app.component.ts
import { Component } from '#angular/core';
import { shareService } from './share.service';
#Component({
selector: 'my-app',
template: `
<hello></hello>
<button (click)="passData()">
Start
</button>
`,
styleUrls: [ './app.component.css' ],
providers:[shareService]
})
export class AppComponent {
constructor(private service : shareService){}
passData(){
this.service.send("hello");
}
}
hello.component.ts
import { Component, Input } from '#angular/core';
import { shareService } from './share.service';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'hello',
template: `<h1>Hello!</h1>`,
styles: [`h1 { font-family: Lato; }`],
providers:[shareService]
})
export class HelloComponent {
subscription: Subscription;
constructor(private share : shareService){
this.subscription = share.subj$.subscribe(val=>{
console.log(val);
})
}
}
share.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class shareService{
private sub = new Subject();
subj$ = this.sub.asObservable();
send(value: string) {
this.sub.next(value);
}
}
I am not getting the value in console.
Here is the working Demo : DEMO
By putting:
#Component({
.....
providers: [sharedService]
})
in both components, you are creating two distinct instances of the shared service.
Each instance is not 'aware' of the data from each component.
Provide it at module level and create a singleton service:
#NgModule({
....
providers: [sharedService]
})
This way, you inject the service as a single instance in the both components, so they can share it as they will share the data.
Or using the Angular's preferred new way :
Beginning with Angular 6.0, the preferred way to create a singleton
service is to specify on the service that it should be provided in the
application root. This is done by setting providedIn to root on the
service's #Injectable decorator:
#Injectable({
providedIn: 'root',
})
Demo
See also
I dont know why sub$ is used but you dont need that
// just push data to subject. you can use BehavourSubject to initiatte a value.
#Injectable()
export class shareService{
private sub = new Subject();
confirmMission(astronaut: string) {
this.sub.next(astronaut);
}
}
And then in your 2nd component sub scribe it
#Component({
selector: 'hello',
template: `<h1>Hello!</h1>`,
styles: [`h1 { font-family: Lato; }`],
providers:[shareService] // this can be shared in module lebel or componenet level
})
export class HelloComponent {
subscription: Subscription;
constructor(private share : shareService){
this.subscription = share.subj.subscribe(val=>{
console.log(val);
})
}
}
make sure to provide your service in module level or provide it in both the component.
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.
I had created some code that would tell me if the users window scrolled beyond a certain point. I quickly realised that this needed to be a service as other components needed this info as well.
I had used #HostListener in the logic and would get results. Now that this is all in a service, I only know if the window was scrolled beyond a certain point when I call the service.
The issue is, how do I constantly call the service? The service has a Boolean in it that I need real-time info on.
Service
import { Injectable, OnInit, HostListener, Inject } from '#angular/core';
import { DOCUMENT } from '#angular/platform-browser';
#Injectable()
export class WindowScrollService {
public navIsFixed: boolean = false;
constructor(#Inject(DOCUMENT) private document: Document) {
}
#HostListener("window:scroll", [])
onWindowScroll() {
let number = this.document.body.scrollTop;
if (number > 20) {
this.navIsFixed = true;
console.log(this.navIsFixed);
} else if (this.navIsFixed && number < 10) {
this.navIsFixed = false;
console.log(this.navIsFixed);
}
}
}
Component
import { Component, OnInit } from '#angular/core';
import { WindowScrollService } from '../window-scroll.service';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
public navIsFixed: boolean;
constructor( private windowscrollservice : WindowScrollService) {
this.navIsFixed = this.windowscrollservice.navIsFixed;
}
ngOnInit(){
this.windowscrollservice.onWindowScroll()
}
}
Use setInterval to repeatedly run some code. One possible implementation
import { Component, OnInit } from '#angular/core';
import { WindowScrollService } from '../window-scroll.service';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
public navIsFixed: boolean;
public interval;
constructor( private windowscrollservice : WindowScrollService) {
this.navIsFixed = this.windowscrollservice.navIsFixed;
}
ngOnInit(){
// run every 500ms
this.interval = setInterval(() => this.windowscrollservice.onWindowScroll(), 500);
// use clearInterval(this.interval) to stop
}
}