I've added a new Service to an existing Angular project with:
$ ng generate service utils/new
Now I tried to move some methods from AppService to NewService.
Both services have the same constructor:
#Injectable({
providedIn: 'root'
})
export class AppService {
constructor(private http: HttpClient) {}
And
#Injectable({
providedIn: 'root'
})
export class NewService {
constructor(private http: HttpClient) { }
Now in a Component I try to use NewService instead of AppService (I simply replace AppService with NewService).
#Component({
//...
})
export class MyComponent implements OnInit {
constructor(private newService: NewService) {
newService.doSomething(...);
}
ngOnInit(): void {
}
It compiles. But I get a runtime error: ERROR TypeError: n.appService is undefined
I could not understand what the debugger was saying so I made a guess: I added private appService: AppService to the constructor, although it is not being used at all in the code of MyComponent. So now I have this:
#Component({
//...
})
export class MyComponent implements OnInit {
constructor(private appService: AppService, private newService: NewService) {
newService.doSomething(...);
}
ngOnInit(): void {
}
Now it compiles, and I also don't get any runtime error.
This looks strange and counterintuitive to me. What did I miss here?
Do I need some configuration setting to declare the existence of NewService?
This happened because in the html view of MyComponent was a reference to an AppService method. Strangely, the project still compiled and only failed at runtime.
Usually when I refer in html views to entities that are not recognized, I get a compilation error.
Related
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 {
}
}
I've came across this specfic problem in my Angular project architecture:
I have one component that need to load different service depending on current URL. It's done by resolving service like in example below.
ChartRoutingModule
const routes: Routes = [
{
path: 'doctorspecialities',
component: ChartComponent,
resolve: {
service: DoctorSpecialitiesServiceResolver,
},
},
]
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [DoctorSpecialitiesServiceResolver],
})
export class ChartRoutingModule {}
When user enter specific url the service is resolved:
DoctorSpecialitiesServiceResolver
#Injectable()
export class DoctorSpecialitiesServiceResolver implements Resolve<any> {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return new DoctorSpecialitiesService();
}
}
And the service is in ActivatedRoute. Everything works fine for now. But I need to inject HttpClient into this service. Adding private _http: HttpClient in constructor of DoctorSpecialitiesService results in this error:
An argument for '_http' was not provided.
I think I should pass this dependency in Resolver but I have no idea where should I declare it. Couldn't find anything that helps me. Could you give me a hand with this?
You can have the HttpClient injected in your resolver, then pass that to the DoctorSpecialitiesService constructor like so:
#Injectable()
export class DoctorSpecialitiesServiceResolver implements Resolve<any> {
constructor(private http: HttpClient) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return new DoctorSpecialitiesService(this.http);
}
}
My service located in src/core/services/api.service.ts
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor() { }
test() {
console.log('hello from services');
}
}
I am trying to invoke this service from a component which in another module.
home.component.ts
import { Component, OnInit } from '#angular/core';
import {ApiService} from './core/services/api.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor(private api: ApiService) { }
ngOnInit() {
console.log(this.api);
}
}
But I am getting an empty object like this ApiServiceĀ {}
So it is returning an object and that means that the service is initialized correctly, instead of console.log(this.api); try console.log(this.api.test()), you should see hello from services in the console.
Same thing happened for me in Angular 8.
I added public in front of the function then it started working:
public test()
I had some unused imports in the service.
Removing those imports and restarting ng serve worked for me.
Calling Components:
<some-component></some-component>
<some-component></some-component>
Component:
#Component ({
selector : 'some-component',
})
export class SomeComponent implements OnInit {
constructor (private someService : SomeService) {}
ngOnInit(): void {
this.someService.register();
}
}
Service:
#Injectable()
export class SomeService{
private targets: Array;
constructor (private http: Http) {}
register(){
this.targets.push('x');
getData();
}
getData(){
console.log(targets);
let params = this.targets.join(); // 'x,x'
return this.http.get(params);
}
}
console:
['x']
['x','x']
// what I need to do to whit until the last one and make the get request.
Hello as you can see in my code I need to call a component many times with the same injected service and not to repeat the get request just making the Get request once for all the components
First approach
import { Input } from '#angular/core';
#Component ({
selector : 'some-component',
})
export class SomeComponent implements OnInit {
#Input() isLast = false;
constructor (private someService : SomeService) {}
ngOnInit(): void {
this.someService.register();
if (this.isLast) {
this.someService.getData();
}
}
}
Delete the this.getData() inside your service's register()
Then in the template where you drop SomeComponents in
<some-component></some-component>
<some-component></some-component>
<some-component [isLast]='true'></some-component>
Second approach
#Component ({
selector : 'some-component',
})
export class SomeComponent implements OnInit {
constructor (private someService : SomeService) {}
ngOnInit(): void {
this.someService.register();
}
}
Delete the this.getData() inside your service's register()
Then inside the component class of the template where you insert your SomeComponents, let's say this parent component is AppComponent
.
.
.
export class AppComponent ... {
constructor (private someService : SomeService) {}
// import AfterViewInit from angular core package
ngAfterViewInit(): void {
this.someService.getData();
}
}
I developed a custom error handler which implements Angular2 Error Handler class. My custom error handler uses a logger service to log errors. The code looks like as follows:
export class CustomErrorHandler implements ErrorHandler {
constructor(private logger: LoggerService) {}
handleError(error: any): void {
logger.error('....');
}
}
However, since the logger service uses Angular2 router, I cannot inject the logger service to the custom error handler! Running the above code throws the following exception!
Error: Provider parse errors:āµCannot instantiate cyclic dependency!
You need to inject manually, to avoid the cyclic dependency problem because this class is created before the providers, your code should be:
import { Injectable, Injector } from '#angular/core';
import { Logger } from '...';
#Injectable()
export class CustomErrorHandler implements ErrorHandler {
constructor(private injector: Injector) {}
handleError(error: any): void {
const logger = this.injector.get(Logger);
logger.error('....');
}
}