how to share objects between sibling components using services? - javascript

I need to call a service on a component, and ideally this service would fetch info in another 2 or 3 components (which already are communicated with the database, etc). I need to be able to share objects.
I've created a service called DashService, like this:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class DashService {
constructor() { }
}
I've linked it to the component that will us it, and I also imported and added the service name in the 'providers' part of the NgModule.
Added:
Import { OnInit } from '#angular/core';
and
implements OnInit{...}
on the module that will send info to the service.
I dont know how to go forward. How can I share an object that exists on a component, to my service? I fail to set the Constructor correctly (on the component sending the info)
When I try
public constructor( private DashService: DashService)
I get an error telling me 'DashService refers to a value but is being used as a type'
Thank you for your help.

Because you are calling the service as the type of the service!
Just change the name, usually the first letter of the service is in lowercase
public constructor( private dashService: DashService)
https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service
here you can find an example how to communicate between components.In general for parent/child components you can use #Input / #Output decorator.
For others case you can use a service ( or a any state management)

Riccardo Gai is right, you should do that to use a service,
public constructor( private dashService: DashService)
But to answer your question, in order to be able to share data between components through a service, you should create an object in that service, for example a public object, then you can access that object through the service.
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class DashService {
objectToShareHere: any;
constructor() { }
}
I think anyway, the best way of this to work is to have get and set methods and access the object through them (having a private obj). Like this:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class DashService {
private objectToShareHere: any;
constructor() { }
setPrivateObj(obj) {
this.objectToShareHere = obj;
}
getPrivateObj() {
return this.objectToShareHere;
}
}
If the data you need to share is from a BE, you can use a Subject object (see RxJs subject here)

Related

Emit method of EventEmitter not emitting values to subscribers

So I am trying to connect two components through a service, LoaderComponent and AppComponent with LoaderService, so that when app fetches data a loader shows up. But when I try to use an EventEmitter to emit changes to the components they don't get the changes but when the service subscribes
to itself, it can get the changes
LoaderService.ts
import { EventEmitter, Injectable } from '#angular/core';
#Injectable()
class LoaderService {
#Output change: EventEmitter<number> = new EventEmitter<number>();
private state: number = 0;
constructor() {
this.change.subscribe(state => console.log(state));
this.setState(1)
}
setState(state: number) {
this.state = state;
this.change.emit(this.state);
}
}
// Shows state when but outside of the service event is not detected, also tried EventEmitter from from events
I expect to get events from the LoaderService to subscribers
You need to use LoaderService in some component for angular to create it, if we do not use the service any where angular will automatically discard it. Inject LoaderService in app component like below:
constructor(private _loadService: LoaderService) {} and then you will see the console.log().
Also, it is recommended to use either Subject or Behavior Subject from Rxjs instead of Output in a service.
First thing, EventEmitters and Outputs don't belong in a service.
I will refactor to use subjects and also protect your subject by making it private and exposing a public observable, this limits how your subject state can be modified, this is not required but is generally considered good practice:
import { Injectable } from '#angular/core';
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';
#Injectable()
class LoaderService {
private change: Subject<number> = new Subject<number>();
change$: Observable<number> = this.change.asObservable();
private state: number = 0;
constructor() {
this.change$.subscribe(state => console.log(state));
this.setState(1)
}
setState(state: number) {
this.state = state;
this.change.next(this.state);
}
}
Second, this is likely an issue with how you provide your service. If you have an app component template like:
<loader-component></loader-component>
<loader-component></loader-component>
with 2 loader components side by side, and the loader component has a providers array like:
providers: [LoaderService]
then these 2 loaders are receiving different copies of the same service as they each provide and inject their own, so they will not see each other's events.
To remedy this, you provide instead in the app component (and not in the loader component) so they have the same copy of the service, because then the parent is providing the service that each of them inject. If you were to provide in both app component and loader component, they would all receive a different copy.
If you provide at root (module level) then every component that injects the service (and doesn't provide it's own) will receive the same copy of that service.
The appropriate place to provide a service is dependent on your app's needs and the function of the particular service.

Angular 6 Services and Class Inheritance

Angular 6 now has injectable providers which is the new recommended way of injecting services, and it works really well except I'm having a problem when using a service which extends another service. So as an example, suppose I have
#Injectable({
providedIn: 'root'
})
export class ParentAppService { ... }
#Injectable({
providedIn: 'root'
})
export class ChildAppService extends ParentAppService { ... }
The problem is that no matter what I ask for in a component, the parent class is always injected.
So if you ask for
constructor(private childAppService: ChildAppService) { ... }
you will still be provided an instance of ParentAppService, which isn't expected.
A very simple workaround would be to just register the providers in your module the old fashioned way, and this works:
#NgModule({
providers: [AppService, ChildAppService]
})
But that's basically the old way of doing things, and doesn't have the benefits of better tree-shaking and cleaner testing like the new providedIn registration process.
So my question is, what's the right way to do this? Is there a better way to register a provider so I can get the desired behavior (maybe specifying a provider token somehow?).
I've setup a super simple stackblitz example to show what's going on.
https://stackblitz.com/edit/angular-lacyab?file=src%2Fapp%2Fapp.component.ts
You'll notice it says "Hello I AM APP SERVICE!" even though the component asked to be provided the child service. If in the app module we register the providers the old way (see the commented out code), all of a sudden the proper provider gets injected.
Thanks for your help!
Update:
There is already a pull request for that https://github.com/angular/angular/pull/25033
Original version
The problem: seems new angular treeshakable services don't respect inheritance:
First angular defines(1) ngInjectableDef property on AppService function. Then you inherit ChilAppService from AppService so that child class contains all properties from parent class. And finally when angular tries to define(2) ngInjectableDef property on ChildAppService it can't because it already exists(3) thanks to javascript prototypical inheritance.
In order to fix it you can either
1) workaround it through defining undefined ngInjectableDef property on child service so that it won't read already filled from parent class property and angular will be able to define ngInjectableDef on child class:
#Injectable({
providedIn: 'root'
})
export class ChildAppService extends AppService {
static ngInjectableDef = undefined;
constructor() {
super();
this.name = 'CHILD SERVICE';
}
}
Forked stackblitz
2) or report issue in github
3) or use composition instead of inheritance as was suggested in comments.

Angular: call function from other component

I'm trying to make two angular components and I want to call a function from the first component in the second component. When I try this I get following error message: Cannot red property 'functionName' of undefined. How can this be solved?
Here a link of an example: https://stackblitz.com/edit/angular-rre4gb
That's because the component you want to call its function, is not instantiated.
for component communication you can use a service instead:
Service
#Injectable()
export class MyService {
myCustomFunction(){
}
}
Component
in your component:
#Component({
selector: 'my-component',
providers: [ MyService ]
})
export class MyComponent {
// inject your service to make it available
constructor(private service: MyService){}
doStuff(){
// call function which is located in your service
this.service.myCustomFunction();
}
}
As others have stated, I would prefer a shared service with a Subject among these components.
service:
#Injectable()
export class SharedService {
mySubject = new Subject();
}
WorldComponent (subscriber):
export class WorldComponent {
constructor(private sharedService: SharedService){
this.sharedService.mySubject.subscribe((data)=>{
this.worldFunction();
})
}
HelloComponent(publisher):
public helloFunction() {
alert('Hello');
this.sharedService.mySubject.next(true);
}
You can find the updated example here: https://stackblitz.com/edit/angular-rnvmkq?file=app%2Fworld.component.ts
The best way to share information between multiple components is generally through a service.
Create a separate file: file.service.ts
Provide the service in the app.module.ts file
Inject the service into each component. Then you'll have access to the variables in both components
See this: https://angular.io/tutorial/toh-pt4
the reason of the error is that the hello component is not imported, but instead of calling a component from another, you should use a service in between, as other answers already suggested.

Angular2: Service with Model - "no provider for model"

What I'm trying to do is create a service that uses a model to show an alert. The alert-model should be necessary nowhere else but in that service but I am not able to make this work. My service:
import {Injectable, Inject} from "angular2/core";
import {AlertModel} from "../models/alert.model";
#Injectable()
export class AlertService {
constructor(#Inject(AlertModel) alertModel: AlertModel) {
}
public alert(){
this.alertModel.message = 'success';
//...
}
}
But I keep getting this error:
Uncaught (in promise): No provider for AlertModel! (UserComponent -> AlertService -> AlertModel)
I'm new to angular and I do not understand this. What am I missing? Thanks in advance!
You need to provide the AlertModel somewhere
bootstrap(AppComponent, [AlertModel])
or in the root component (preferred):
#Component({
selector: 'my-app',
providers: [AlertModel],
...
})
Ensure AlertModel has the #Injectable() decorator and all its constructor parameters are provided as well (if it has any)
#Inject(AlertModel) is redundant if the type of the constructor parameter is already AlertModel. #Inject() is only necessary if the type differs or if AlertModel doesn't have the #Injectable() decorator.
constructor(#Inject(AlertModel) alertModel: AlertModel) {
You have this error since there is no provider for the AlertModel class visible from the UserComponent component (that calls the service). You can define either this class in the providers attribute of the component either when bootstrapping your application.
See the question to know more about how hierarchical injectors works and how to inject things into services:
What's the best way to inject one service into another in angular 2 (Beta)?
Since the AlertModel class seems to be a model class I don't think that you need to inject it. You can simply import the class and instantiate it:
#Injectable()
export class AlertService {
alertModel: AlertModel = new AlertModel();
public alert(){
this.alertModel.message = 'success';
//...
}
}

Inserting multiple services into a service - angular2

I am trying to inject a self created service & the angular2 Http service into my custom HttpRest service.
using
#Inject(Http) public _http: Http
worked fine, but when I try to inject another self made service i get following Error:
EXCEPTION: Cannot resolve all parameters for 'HttpRest'(Http #Inject(Http), undefined #Inject(undefined)). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'HttpRest' is decorated with Injectable.
For some reason
UserIds is undefined, even though the import is succesful.
My custom service:
#Injectable()
export class UserIds{
private _signature_id:string;
private _role_id:number;
get signature_id():string{
return this._signature_id;
}
set signature_id(id:string){
this._signature_id = id;
}
get role_id():number{
return this._role_id;
}
set role_id(id:number){
this._role_id = id;
}
}
The Custom HttpRest service im injecting both Http & UserIds into:
#Injectable()
export class HttpRest{
groups;
constructor(
#Inject(Http) public _http: Http,
#Inject(UserIds) public _ids: UserIds
){}
...
}
NOTE! when I remove
,
#Inject(UserIds) public _ids: UserIds
I dont get the Error.
What am I missing ?
UPDATE
The problem is actually that UserIds is undefined in the constructor params for some unknown reason that im trying to understand, so the title of this question becomes irrelevant. It should be "Imported service is undefined in constructors params".
Please reffer to my answer on this question further down this post.
UPDATE:
Please reffer to a question that discusses this issue.
Using index.ts file to export class causes undefined in injected constructor
#Inject(...) in #Inject(Http) public _http: Http is redundant when the parameter to #Inject() is the same as the type of the parameter.
#Injectable()
export class HttpRest{
groups;
constructor(public _http: Http, public _ids: UserIds
){}
...
}
You need to provide Http and UserIds so DI is able to resolve the dependency. Http is included in HTTP_PROVIDERS
#Component({
selector: '...',
providers: [HTTP_PROVIDERS, UserIds],
template: ...
})
export class AppComponent {
}
Ensure you have everything imported correctly
import {HTTP_PROVIDERS, Http} from 'angular2/http';
Ok so I found the problem, and it has not to do with Inject.
The problem was that im using an index file to export services, as mentioned in the angular 2 style guide (https://github.com/mgechev/angular2-style-guide/blob/master/old/README.md#directory-structure), and for some reason importing this specific service from the index causes an undefined value when injected into the constructor.
When I reffernced the source directly and not trough the index file, for some reason that is unknown to, resolved the Error.
The import before the fix looked like this:
import {UserIds} from "../index";
Which worked with all other services and components.
The import using the direct source file:
import {UserIds} from "../user_ids/user_ids.service";
For some reason this solved my problem, but i want to stay consistent with the index.ts encapsulation.
If anyone has an idea why this could happen I'll be happy for updates.
You need to register your UserIds service at the root level (main.ts) or in the app.component.ts, which is the highest level in the hierarchy:
#Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>',
providers: [ UserIds ]
})
export class AppComponent { }

Categories

Resources