I have a service:
#Injectable()
export class MyService implements IMyService {
myServiceArray: Array<string> = ["hi", "hello", "yoyo"];
}
This Service is injected in a Component that updates the array of string with ngModel. When I try to print the array from the Component or from the Service everything is working fine (aka the array is updated with the ngModel).
I also #Inject such Service in another 1.
#Injectable()
export class AnotherService implements IAnotherService {
constructor(public myService: MyService) {
}
printValues() {
console.log(this.myService.myServiceArray);
}
}
When I call printValues(), ["hi", "hello", "yoyo"] is printed even if I updated the values of the array with a model!
What am I doing wrong?
EDIT:
The code of the component is the following.
#Component({
selector: 'app-root',
providers: [MyService],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppCustomerData implements IAppCustomerData {
constructor(public myService: MyService) {
}
}
You have to look that you inject the same instance of the service, if you have multiple instances of the service each one has a own array in it.
I would try to use the local storage to store the data in the service, than you can have multiple instances and each one can edit or return the same value:
How to store token in Local or Session Storage in Angular 2?
Maybe this answers your problem:
How do I create a singleton service in Angular 2?
Related
I have a module in Angular that is structured likes this:
moduleName
componentA
componentB
Now componentA and componentB are very similar, as they share some attributes and methods, e.g.:
protected available: boolean = true;
As I don't want to repeat myself, I've created a base class, that stores all this:
export abstract class BaseComponent {
protected available: boolean = true;
}
And both controllers inherit from that class:
import { BaseComponent } from '../base.component';
export class ComponentA extends BaseComponent implements OnInit {
constructor() {
super();
}
ngOnInit() {
console.log(this.available);
}
}
This works just fine. However, when I research this soultion a lot of people are saying:
Don't use inheritance, use composition in this case.
Alright, but how can I use composition instead? And is the gain really that big over the current solution?
Thanks a lot for your time.
For composing objects in angular you need to have a reference to that object inside of your class, which shares data and functionality. To do that you need to use Angular services, and inject them to your class, and there should be 1 instance of service per component.
Create a new service by running ng g s my-service, remove providedIn: 'root' from your service annotation (We want to provide instance per component)
Add public available: boolean = true; to the service
provide the service through the components, in #Component configs on your components
inject the service in your both component constructors, constructor(private myService:MyService)
Now you have a composition that keeps data and functionality
#Component({
selector: 'app',
templateUrl: './app.my-component.html',
styleUrls: ['./app.my-component.css'],
providers: [MyService]
})
export class MyComponent {
constructor(private myService: MyService) {
}
}
If you create same components with big part same logic. you can use inheritance for example controlSelectComponent and controlInputComponent stackblitz example
For composition you need to create service and provide it to both components. But you dont keep component state in service becose all service are singletone. And when one component change state another component crash.
You also can provide service to each component in providers section
#Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [MyService]
})
export class AppComponent {
constructor(private myService: MyService) {
}
}
But in case with saving state in service is not the best solution
Conclusion
Use services and composition for share helper methods between components.
Use abstract class and inheritance for components with same logic and state changes.
I would also recommend to read about Composition over Inheritance. The syntax(InversifyJs) is very similar that Angular uses. Please see this blog
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.
I have a component that uses a service. The component looks something like this:
#Component({
moduleId: module.id,
selector: 'test',
providers: [HTTP_PROVIDERS, TestService]
})
export class TestComponent implements OnInit {
constructor(private _testService:TestService) {
}
As you can see, I added the HTTP_PROVIDERS provider in my component. This worked since the DI is now aware of the http classes. However, it was my TestService that was really using the Http class, not my TestComponent.
#Injectable()
export class TestService {
constructor(private _http:Http) {
}
I felt that since it is the service using the Http class, it should be the one including the providers in itself. The TestComponent wouldn't know what providers TestService would need.
Since the service class doesn't have that component decorator, I'm not sure how I can actually add providers to it. How can I add providers to a Service class?
What you can do is,
Inject HTTP_PROVIDERS into bootstrap function ,
import {HTTP_PROVIDERS} from '#angular/http';
bootstrap(AppComponent,[HTTP_PROVIDERS]);
and in your service,
import {Http} from '#angular/http';
#Injectable()
export class TestService {
constructor(private _http:Http) {}
}
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';
//...
}
}
I'm reading lots of articles on this matter like this, and this and also this but each one of these articles starts from a situation in which the NG1 service is a class and can be exported.
I'm in a very different situation, i often have multiple services in the same file and they are defined in a very old style manner like
angular.module('App.services').factory('SessionService',
function() {
var defer = $q.defer();
[...]
}
);
No class, no export.
And this stuff is directly linked in the page with an old fashioned <script src="...">
At the same time i'm trying to create new directives in Angular2 and these directives need those old fashioned services.
I get i should be able to write something like this
import {Injector,Component, Directive, ElementRef, Input, View} from 'angular2/core';
var injector = Injector.resolveAndCreate([
SessionService
]);
var SessionService = injector.get(SessionService);
#Component({
selector: 'nav-bar'
})
#View({
templateUrl: '/app/components/navbar/navBar.html'
})
export class navBar {
constructor() {
}
}
but of course SessionService object is not found.
How can i get out of this mess?
[Additional Info]
Using babel as transpiler
angular2-annotations plugin added
A great article to understand the difference between Annotations and Decorators in angular2: http://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html
You simply need to leverage #Inject:
#Component({
selector: 'nav-bar'
templateUrl: '/app/components/navbar/navBar.html'
})
export class navBar {
constructor(private #Inject('SessionService') sessionService) {
}
}
See this plunkr for more details: http://plnkr.co/edit/U6ygjUUQ04mTGAAaC1pZ?p=preview
You can notice that with factory you can't use classes. It's only possible with services...
If you use ES6 only, you could try this:
#Component({
selector: 'nav-bar'
templateUrl: '/app/components/navbar/navBar.html'
})
export class navBar {
constructor(sessionService) {
}
static get parameters() {
return [[ new Inject('...'), new Inject('...') ]];
}
}