arow arow - 1 month ago 5
TypeScript Question

How to pass values from one class's method to another class's method in Typescript?

I'm new to Object Oriented programming and am assuming this should be an easy concept for the seasoned OO programmers, but I'm certainly struggling with it.

In my Angular2 app I have a HttpService class as shown below:

http.service.ts

@Injectable()
export class HttpService {
constructor(private http: Http) { }
addLeaf(parentId, label, name){
var headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post('http://localhost:8000/addleaf/',
{'parentId':parentId,'label':label, 'name':name},
{ headers: headers })
.map(res => res).subscribe();
}


I try to call this method from within another class as below:

leaf.ts

import { HttpService } from './http.service';

export class Leaf{
name: string;
...
http: Http; // very unsure about these two lines
private httpService: HttpService = new HttpService(this.http)
constructor(input){
this.name = input.name;
...
add(){
//what should go here?
this.httpservice.addLeaf(this.id, this.label, this.name);
//error -> cannot read property 'post' of undefined
}


Reading this, I tried creating an instance of the HttpService class but I get the error that the post function does not exist. Also no luck putting the httpService in the constructor.

I call the method in my html like this:

(click)="leaf.add()"


EDIT: following @peeskillet's answer I modified leaf.ts and added leaf.component.ts as shown:

leaf.ts

export class Leaf{
name: string;
...
constructor(input){
this.name = input.name;
...
add(){
//what should go here?
}
}


leaf.component.ts

@Component({
providers: [HttpService],
})

export class LeafComponent {
leaf: Leaf;
constructor(private httpService: HttpService) {
this.httpService.addLeaf(this.leaf.id, this.leaf.type, this.leaf.name)
}
}


service works fine if I write pre-defined strings in place of the params, but still not sure how I can pass the parameters of the clicked leaf to this.

Answer

With Angular, we use dependency injection and Inversion of Control. What this means is that we do not create the service ourselves, but let Angular create it. Then we just ask for the service, then Angular will resolve any dependencies that service has. Take for example

@Injectable()
class Service {
  constructor(private http: Http) {}
}

Here, Service has a dependency in Http. Http is not something that we can just grab out of thin air. We can't just do

let service = new Service(new Http());

Http is also dependent on some other services. Here what its constructor looks like

class Http {
  constructor(backend: ConnectionBackend, options: RequestOptions) {} 
}

You might think that maybe you can just instantiate it with the ConnectionBackend and RequestOptions

new Http(new ConnectionBackend(), new RequestOptions())`

But you can't do this either, as ConnectionBackend also has required dependencies. It's for this reason that we use Inversion of Control. We just add the service to a container, and when ask for the service, Angular look look up service, as see that it requires, Http, and see that Http requires ConnectionBackend and RequestOptions, etc, and Angular will create all the items, looking in its registry for all those items and putting them all together like Voltron. Then it will give us the service, fully populated.

So add our service to the container, we first need to add the Injectable decorator on the service

@Injectable()
class Service {
  constructor(private http: Http) {}
}

Then we need to add it to the @NgModule.providers

@NgModule({
  imports: [ HttpModule ],
  providers: [ Service ]
})
class AppModule {}

Now, whenever we ask for Service, it will be fully populated with the Http (which is in the HttpModule).

How we ask for the service is through the constructor of either another service or component (directive, pipe, etc)

@Component({
})
class MyComponent {
  constructor(private service: Service) {}
}

By seeing the Service type as a constructor argument, Angular knows to look up the Service in its container, then pass it through to us. This is the basics of Dependency Injection and Inversion of Control.

In your case of Leaf. If is is meant to be a service, then you can do the same

@Injectable()
class Leaf {
  constructor(private service: Service) {}
}

@NgModule({
  imports: [ HttpModule ],
  providers: [ Leaf, Service ]
})
class AppModule {}

If you don't want to add Leaf as a provider, you don't need to. Just do

@Component({})
class MyComponent {
  leaf: Leaf;

  constructor(private service: Service) {
    this.leaf = new Leaf(service);
  }
}