I have created two components and one shared service, i want pass data from one component to another, but i am getting empty object
bellow is 1st component
import { Component, OnInit } from '#angular/core';
import {SharedService} from './../shared.service';
import { Router, NavigationStart } from '#angular/router';
#Component({
selector: 'app-cone',
templateUrl: './cone.component.html',
styleUrls: ['./cone.component.css'],
providers: [SharedService]
})
export class ConeComponent implements OnInit {
req = <any>{};
constructor(public service:SharedService,private router:Router) { }
send(){
this.req.fname= "ketan";
this.req.lname= "pradhan";
this.service.saveData(this.req);
console.log('str');
this.router.navigate(['/apps/ctwo']);
}
ngOnInit() {
}
}
Bellow is the 2nd component where i need to pass the data from 1st comonent, i am getting empty object is this.myName
import { Component, OnInit } from '#angular/core';
import {SharedService} from './../shared.service';
import { Router, NavigationStart } from '#angular/router';
#Component({
selector: 'app-ctwo',
templateUrl: './ctwo.component.html',
styleUrls: ['./ctwo.component.css'],
providers: [SharedService]
})
export class CtwoComponent implements OnInit {
myName= <any>{};
constructor(public service:SharedService,private router:Router) {
this.myName=this.service.getData();
console.log(this.myName);
}
ngOnInit() {
}
}
Bellow is shared service which is for communicating between 2 components
import {Component, Injectable,Input,Output,EventEmitter} from '#angular/core'
// Name Service
export interface myData {
name:string,
lname:string
}
#Injectable()
export class SharedService {
sharingData: myData=<any>{};
saveData(str){
console.log('save data function called' + str.fname);
console.log(this.sharingData);
this.sharingData = str;
// this.sharingData.lname=str.lname;
console.log(this.sharingData)
}
getData()
{
console.log('get data function called');
return this.sharingData;
}
}
When you are setting providers arrays at component level, it means that you have two separate instances of the service in this case.
You need to declare the service in your NgModule providers array instead, then the two components (and any other components in that module) will have the same instance of the service.
So remove the providers arrays in your components, and instead add the service to providers array in your NgModule.
#Component({
selector: 'app-ctwo',
templateUrl: './ctwo.component.html',
styleUrls: ['./ctwo.component.css'],
// providers: [SharedService] // remove these!
})
and instead....
#NgModule({
imports: [ ... ],
declarations: [ .. ],
bootstrap: [ ... ],
providers: [ SharedService ] // here!
})
Related
I am working on angular app. I want to array of objects from one component to another using service. I am using the following link Pass array of int in Angular Route
PassData.html
<div>
<button type="button" [routerLink]="['/receive-data']">Pass Data</button>
</div>
PassData.ts
import ....
#Component({
selector: 'app-PassData',
templateUrl: './PassData.component.html',
styleUrls: ['./PassData.component.css'],
providers: [DataService]
})
constructor( private dataService: DataService) { }
export class PassData {
passObjects : any[] = [{'name': 'John', 'city': 'paris'},{'name': 'Bob', 'city': 'london'}, {'name': 'Grim', 'city': 'paris'}];
passDataToService() {
this.dataService.storePassedObject(this.passObjects);
}
}
ReceiveData.ts
#Component({
selector: 'app-ReceiveData',
templateUrl: './ReceiveData.component.html',
styleUrls: ['./ReceiveData.component.css'],
providers: [DataService]
})
export class ReceiveData implements OnInit {
let selectedProducts = this.DataService.retrievePassedObject();
console.log(JSON.stringify(selectedProducts)); // prints empty array
}
This is angular service
DataService.ts
import { Injectable } from '#angular/core';
#Injectable()
export class DataService {
allPassedData: any[] = [];
constructor() {}
storePassedObject(passedData) {
this.allPassedData = passedData;
}
retrievePassedObject() {
return this.allPassedData;
}
}
Here there are two components, passedData and RecieveData and a service connecting them so data can be passed b/w them. My goal is to pass the data and print the passed data in ReceiveData Component. I am not sure how to structure the angular service when I retrieve the data I find it is empty.
I have registered in app.module.ts
This is app.module.ts
import ...
#NgModule({
declarations: [
PassData,
ReceieveData
],
providers : [
DataService
]
})
export class AppModule { }
I know allPassedData: any[] = []; is making the data empty when I try to access the objects from receiveData it is reassigned to []. But how do I solve this problem?
Demo use BehaviorSubject
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class DataService {
private paramSource = new BehaviorSubject(null);
sharedParam = this.paramSource.asObservable();
constructor() { }
changeParam(param: any[]) {
this.paramSource.next(param)
}
}
import to components
constructor(private _dataService: DataService) { }
to change param
this._dataService.changeParam("your parameter")
to read param
this._dataService.sharedParam.subscribe(param=>console.log(param))
Use the Subjects and Behaviour Subjects in Service. like below example. so in that case both the component can subscribe the service object and emit the data as well. so whenever one changes other will get that data.
import { Injectable } from '#angular/core';
#Injectable()
export class DataService {
allPassedData: BehaviourSubject<any> = new BehaviourSubject<any>([]);
constructor() {}
storePassedObject(passedData) {
this.allPassedData.next(passedData);
}
// here instead of retrieve like this you can directly subscribe the property in your components
retrievePassedObject() {
return this.allPassedData;
}
}
// Passed Component
import ....
#Component({
selector: 'app-PassData',
templateUrl: './PassData.component.html',
styleUrls: ['./PassData.component.css'],
providers: [DataService] // inject in module for singleton instance
})
export class PassData {
passObjects : any[] = [{'name': 'John', 'city': 'paris'},{'name': 'Bob',
'city': 'london'}, {'name': 'Grim', 'city': 'paris'}];
constructor(private dataService DataService){};
passDataToService() {
this.dataService. allPassedData.next(this.passObjects); // here you emit
the objects
}
}
// Recieved Component
#Component({
selector: 'app-ReceiveData',
templateUrl: './ReceiveData.component.html',
styleUrls: ['./ReceiveData.component.css'],
providers: [DataService] // instead of injecting service in component inject
in module for sigleton instance.
})
export class ReceiveData implements OnInit {
selectProducts: any;
constructor(private dataService DataService){};
ngOnInit(){
this.dataService.allPassedData.subscribe((allPassedData)=>{
this.selectProducts = allPassedData;
console.log(JSON.stringify(this.selectedProducts)); // print the data
})
}
import ...
#NgModule({
declarations: [
PassData,
ReceieveData
],
providers : [
DataService
]
})
Hope it will help.
I am new to angular and I am trying to pass data from one component(HomeComponent) to another component(ProfileComponent) after navigation.
I created a shared service(DataService).
I injected the service in both the HomeComponent and ProfileComponent but when I set the value of the message property in HomeComponent and try to retrieve it in the ProfileComponent the value is undefined because the DataService is not the same instance.
The DataService was registered in the AppModule in the providers array so it should be a shared service and always the same instance right?
Thanks in advance
DataService.ts
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class DataService {
message:string;
constructor() { }
}
HomeComponent.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../services/data/data.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor(private data:DataService) { }
ngOnInit() {
this.data.message = "hello world";
}
}
ProfileComponent.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../services/data/data.service';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
private message : string;//undefined
constructor(private data:DataService) { }
ngOnInit() {
this.message = this.data.message;
}
}
AppModule.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DataService } from './services/data/data.service';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
#NgModule({
declarations: [
AppComponent,
HomeComponent,
ProfileComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [DataService],
bootstrap: [AppComponent]
})
export class AppModule { }
I know it's a 2 year question, but Google puts it at the top of search results
Now, Angular docs are clearer about this (or just we can find out easier), it's called "Singleton Services"
The section that explains this "bug" is The ForRoot Pattern and it says:
"If a module defines both providers and declarations (components, directives, pipes), then loading the module in multiple feature modules would duplicate the registration of the service. This could result in multiple service instances and the service would no longer behave as a singleton."
To sum up, if you define this in your services (DataService.ts) the providedIn: root as follows
#Injectable({ providedIn: 'root' })
you need to avoid define the service as a provider on your components or modules.
AppModule.ts
...
imports: [
BrowserModule,
AppRoutingModule
],
providers: [DataService], // This line is the problem
bootstrap: [AppComponent]
....
Hope that helps to somebody and if need more documentation refer to Singleton Services' link
Each time you inject the service to your component, new instance is generated. However in this case i would recommend you to use BehaviorSubject as follows,
#Injectable()
export class SharedService {
private messageSource = new BehaviorSubject<string>("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
STACKBLITZ DEMO
Im having some trouble figuring this out, basically I have a headerTitleService which I want to be able to dynamically set the title in my header component but for some reason when I set the title nothing shows up? Im not getting any errors so I can seem to figure out what the problem is..
headerTitle.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class HeaderTitleService {
title = new BehaviorSubject('');
constructor() { }
setTitle(title: string) {
this.title.next(title);
}
}
header.component.ts
import { Component, OnInit } from '#angular/core';
import { HeaderTitleService } from '../../../services/headerTitle.service'
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
providers: [HeaderTitleService]
})
export class HeaderComponent implements OnInit {
title: string;
constructor(
private headerTitleService: HeaderTitleService
) { }
ngOnInit() {
this.headerTitleService.title.subscribe(updatedTitle => {
this.title = updatedTitle;
});
}
}
header.component.html
<h1>{{title}}</h1>
home.component.ts
import { Component, OnInit } from '#angular/core';
import { HeaderTitleService } from '../../services/headerTitle.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
providers: [HeaderTitleService]
})
export class HomeComponent implements OnInit {
constructor(
private headerTitleService: HeaderTitleService
) { }
ngOnInit() {
this.headerTitleService.setTitle('hello');
}
}
The line providers: [HeaderTitleService] in each component means that they will be given one HeaderTitleService each, rather than one between them.
To fix this remove providers: [HeaderTitleService] from your components, and place it in your module definition instead:
#NgModule({
providers: [HeaderTitleService]
})
Move HeaderTitleService in providers of your module. With your implementation you receive new instance of the HeaderTitleService in each component.
Hope this helps.
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 am trying to create a reusable component that serves as a processing overlay when making asynchronous calls across my site. I have a service in place but the OverlayComponent doesn't seem to get invoked when showOverlay is invoked:
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HashLocationStrategy, LocationStrategy } from '#angular/common';
import { HttpModule } from '#angular/http';
import { AppRoutingModule } from './app-routing.module';
import { MainComponent } from './app.mysite.component';
import { OverlayComponent } from './app.mysite.overlay.component';
import { TrackerComponent } from './pages/tracker/mysite.tracker.component';
import { OverlayService } from "./overlay.service";
#NgModule({
imports: [ BrowserModule, AppRoutingModule, HttpModule ],
declarations: [
MainComponent,
OverlayComponent,
NavbarComponent,
TrackerComponent,
],
providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}, OverlayService],
bootstrap: [ MainComponent ]
})
export class AppModule { }
TrackerComponent.ts
import { Component, OnInit } from '#angular/core';
import { OverlayService } from '../../overlay.service.js';
#Component({
moduleId: module.id,
selector: 'tracker-component',
templateUrl: '/public/app/templates/pages/tracker/mysite.tracker.component.html',
providers: [ OverlayService]
})
export class TrackerComponent implements OnInit{
constructor(private http: Http, private overlayService: OverlayService) {
}
ngOnInit(): void {
this.overlayService.showOverlay('Processing...'); //This kicks everything off but doesn't show the alert or overlay
this.overlayService.test(); //does exactly what i'd expect
}
}
overlay.service.ts
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class OverlayService {
private message: string;
private subject: Subject<any> = new Subject<any>();
showOverlay(msg: string) : void { //When this gets invoked, shouldn't it be invoking a change to this.subject and therefore invoking getMessage()
this.message = msg;
this.subject.next(msg);
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
test() {
return 'test good'; //if I call this function, it works
}
}
app.mysite.overlay.component
import { Component, OnInit } from '#angular/core';
import { OverlayService } from './overlay.service';
#Component({
selector: 'overlay-component',
templateUrl: '/public/app/templates/mysite.overlay.component.html',
styleUrls: ['public/app/scss/overlay.css'],
providers: [OverlayService]
})
export class OverlayComponent implements OnInit {
private processingMessage: string;
constructor(private overlayService: OverlayService) {}
ngOnInit() {
this.overlayService.getMessage().subscribe((message: string) => { //since i'm subscribed to this, i'm expecting this to get called. It doesn't
this.processingMessage = message;
alert(this.processingMessage); //never gets hit
$('.overlay-component-container').show(); // never gets hit
},
error => {
alert('error');
})
}
}
Specifying providers in the Component metadata actually creates a new injectable, scoped to that component tree.
If you want to share the overlay service across the app, you'll need to declare the overlay provider in the NgModule, and not in the components. Alternatively, you can declare it only as a provider on the top-level entry component (eg. AppComponent), though it may cause confusion when used in other entry components/lazy-loaded modules.
See https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html for a better explanation