Create new instance of class that has dependencies, not understanding factory provider

Solution 1:

You can provide a factory function. This is different from a simple useFactory: ... provider like

{ 
    provide: 'TestModelFactory', 
    useFactory: () => {
        return (http, config) => { 
            return new TestModel(http, config);
        };
    },
    deps: [Http, CONFIG];
}

and then use it like

@Injectable()
export class TestService {

   constructor(@Inject('TestModelFactory' testModelFactory) {}

   getTestsModels(): Observable<TestModel[]> {
        let url = this.config.apiUrl + "test";
        return this.http.get(url)
                        .map( (response) => response.json() )
                        .map( (results) => {
                            return results.map( (current) => {
                                let tm = testModelFactory();
                                tm.xxx // assign data
                            })
                        })
                        .catch(this.handleError);
    }
}

You can also support per instance parameters like

{ 
    provide: 'TestModelFactory', 
    useFactory: (json) => {
        return (http, config) => { 
            return new TestModel(http, config, json);
        };
    },
    deps: [Http, CONFIG];
}

and then use it like

@Injectable()
export class TestService {

   constructor(@Inject('TestModelFactory' testModelFactory) {}

   getTestsModels(): Observable<TestModel[]> {
        let url = this.config.apiUrl + "test";
        return this.http.get(url)
                        .map( (response) => response.json() )
                        .map( (results) => {
                            return results.map( (current) => {
                                let tm = testModelFactory(result);
                            })
                        })
                        .catch(this.handleError);
    }
}

But you don't need to use DI. You already inject Http and CONFIG into your TestService. You can just

@Injectable()
export class TestService {

    constructor(private http: Http, @Inject(CONFIG) private config) {}

    getTestsModels(): Observable<TestModel[]> {

        let url = this.config.apiUrl + "test";

        return this.http.get(url)
                        .map( (response) => response.json() )
                        .map( (results) => {
                            return results.map( (current) => {
                                return new TestModel(http, config);
                            })
                        })
                        .catch(this.handleError);

    }

    private handleError(err): Observable<any> {

        let errMessage = err.message ? err.message : err.status ? `${err.status} - ${err.statusText}` : 'Server Error';

        return Observable.throw(new Error(errMessage));

    }
}

In every case you need to provide some way to initialize TestModel from result for example by passing the JSON to the constructor and initialize the members of TestModel from the passed JSON.

See also Angular2: How to use multiple instances of same Service?

Solution 2:

First of all, you are mixing two separate concerns here: one is holding data, which is your TestModel's concern, and another is saving that data, which isn't. This second concern should be implemented in TestService instead, it's its concern to talk to the server, so let it do its job.

Then, angular injectables are intended to be singletons. Quite obvious that data objects are not singletons, so you should not inject them through DI. What registered with DI is intended to be a service working with data objects, not data objects themselves. You can operate data objects directly or create some factory service which will create them for you being itself a singleton. There are plenty of ways to achieve that without DI.

You can find more details about angular2 DI here. It's pretty long but luckily not very complicated.

Solution 3:

Thanks to everyone above, This is a working plunker I used. Hope it helps

http://plnkr.co/edit/NxGQoTwaZi9BzDrObzyP

import {Component, NgModule, VERSION, Injectable, Inject} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {HttpClient} from '@angular/common/http'
import {HttpModule} from '@angular/http'

@Injectable()
export class HttpService{

  token = 'hihaa';
 constructor(){
 } 

 myFunction(value){
 console.log(value)

 }
}


export class Country{
  constructor(value,public httpService: HttpService){

    console.log(value,this);
  }

  classes(){

    this.httpService.myFunction('BGGGG')
  }
}


@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
    </div>
  `,
})
export class App {
  name:string;
  country:any;

  constructor(
    @Inject('CountryFactory') countryFactory
    ) {
    this.name = `Angular! v${VERSION.full}`;
    this.country = countryFactory(3);
    this.country.classes();
  }
}

export let CountryProvider = { provide: 'CountryFactory',
    useFactory: (httpService) => {
      return (value) =>{
        return new Country(value,httpService)
      };
    },
    deps: [HttpService]
  }

@NgModule({
  imports: [ BrowserModule,HttpModule ],
  declarations: [ App ],
  bootstrap: [ App ],
  providers: [
    HttpService,
    CountryProvider

  ]
})
export class AppModule {}