创建具有依赖关系的类的新实例,不了解工厂提供者 [英] Create new instance of class that has dependencies, not understanding factory provider
问题描述
我已经为此研究了一段时间,似乎找不到足够清晰的答案来理解.我有一个 TestComponent,它使用 TestService 从服务器获取一组 TestModel.当我获取这些测试模型时,它只是一个 json 文件,服务器正在读取该文件并使用正确的 mime 类型发回该文件.从服务器获取测试模型后,我将它们放在一个简单的选择元素下拉列表中.选择测试模型后,它会在嵌套组件 TestDetailComponent 中显示选定的测试模型.
这一切都很好,而且工作正常.当我从服务器中提取数据时,我一直遇到问题.由于 JavaScript 没有运行时检查,我们无法自动将 JSON 从服务器转换为 typescript 类,因此我需要使用检索到的 JSON 手动创建 TestModel 的新实例.
好的,问题来了.我需要调用新的 TestModel 并为其提供依赖项,但它需要是 TestModel 的新实例.我希望 TestModel 能够将自身保存并更新回服务器,因此它依赖于来自 @angular/core 的 Http 并且它依赖于我创建的配置类,该类使用 opaqueToken CONFIG.I 注入无法弄清楚如何获取 TestModel 的新实例.这是初始文件
测试组件:
import { Component, OnInit } from '@angular/core';从'./shared/test.service'导入{TestService};从'./shared/test.model'导入{TestModel};import { TestDetailComponent } from './test-detail.component';@零件({选择器:测试组件",templateUrl: 'app/test/test.component.html',样式网址:[],提供者:[TestService],指令:[TestDetailComponent]})导出类 TestComponent 实现 OnInit {测试:TestModel[] = [];selectedTest:测试模型;构造函数(私有测试服务:TestService){};ngOnInit() {this.testService.getTestsModels().subscribe((测试) => {控制台日志(测试);this.tests = 测试});}}
测试组件模板:
测试细节组件:
import { Component, Input } from '@angular/core';从'@angular/common'导入{JsonPipe};从'./shared/test.model'导入{TestModel};@零件({选择器:'测试细节',templateUrl: 'app/test/test-detail.component.html',管道:[JsonPipe]})导出类 TestDetailComponent {@Input() 测试;}
TestDetailComponent 模板
{{test |json}}</p>
测试模型
import { Injectable, Inject } from '@angular/core';从@angular/http"导入 { Http, Response, Headers, RequestOptions };从 'rxjs/Rx' 导入 { Observable };从'./../../config/constants'导入{配置};@Injectable()导出类 TestModel {测试":数量;那个":字符串;a":字符串;构造函数(私有http:Http,@Inject(CONFIG)私有配置){}save(): Observable{让 url = this.config.apiUrl + "test";让 body = JSON.stringify({测试:this.testing,这个:这个.那个,a:这个.a});let headers = new Headers({'Content-Type': 'application/json'});let options = new RequestOptions({headers: headers});返回 this.http.post(url, body, options).map( (响应) => response.json() ).map( (结果) => {结果.map((聚合,当前)=> {聚合.push(<TestModel>current);返回总和;}, new Array())}).catch(this.handleError);}更新() {让 url = this.config.apiUrl + "test";让 body = JSON.stringify({测试:this.testing,这个:这个.那个,a:这个.a});let headers = new Headers({'Content-Type': 'application/json'});let options = new RequestOptions({headers: headers});返回 this.http.put(url, body, options).map( (响应) => response.json() ).map( (结果) => {结果.map((聚合,当前)=> {聚合.push(<TestModel>current);返回总和;}, new Array())}).catch(this.handleError);}private handleError(err): Observable然后,如果我将 Http 添加到 ReflectiveInjector,我只会收到另一个连接后端错误,我假设它会继续进入依赖链,直到我们找到底部.
抱歉,帖子太长,任何帮助将不胜感激!
解决方案 你可以提供一个工厂函数.这与简单的 useFactory: ...
提供者(如
)不同<代码>{提供:'TestModelFactory',useFactory: () =>{返回(http,配置)=>{返回新的测试模型(http,配置);};},deps: [Http, CONFIG];}
然后像这样使用
@Injectable()导出类 TestService {构造函数(@Inject('TestModelFactory' testModelFactory){}getTestsModels(): Observable{让 url = this.config.apiUrl + "test";返回 this.http.get(url).map( (响应) => response.json() ).map( (结果) => {返回 results.map( (当前) => {让 tm = testModelFactory();tm.xxx//分配数据})}).catch(this.handleError);}}
您还可以支持每个实例的参数,例如
<代码>{提供:'TestModelFactory',useFactory: (json) =>{返回(http,配置)=>{返回新的 TestModel(http, config, json);};},deps: [Http, CONFIG];}
然后像这样使用
@Injectable()导出类 TestService {构造函数(@Inject('TestModelFactory' testModelFactory){}getTestsModels(): Observable{让 url = this.config.apiUrl + "test";返回 this.http.get(url).map( (响应) => response.json() ).map( (结果) => {返回 results.map( (当前) => {让 tm = testModelFactory(result);})}).catch(this.handleError);}}
但您不需要需要使用 DI.您已经将 Http
和 CONFIG
注入到您的 TestService
中.你可以
@Injectable()导出类 TestService {构造函数(私有http:Http,@Inject(CONFIG)私有配置){}getTestsModels(): Observable{让 url = this.config.apiUrl + "test";返回 this.http.get(url).map( (响应) => response.json() ).map( (结果) => {返回 results.map( (当前) => {返回新的测试模型(http,配置);})}).catch(this.handleError);}private handleError(err): Observable{让 errMessage = err.message ?错误消息:错误状态?`${err.status} - ${err.statusText}`:服务器错误";返回 Observable.throw(new Error(errMessage));}}
在每种情况下,您都需要提供某种方法来从 result
初始化 TestModel
,例如通过将 JSON 传递给构造函数并初始化 TestModel
来自传递的 JSON.
I've been working on this for a while now and can't seem to find an answer clear enough to understand. I have a TestComponent that grabs an array of TestModels from a server using TestService. When I grab these test models it is just a json file that the server is reading and sending back with the correct mime type. Once the test models are grabbed from the server, I put them in a simple select element drop down. When a test model is selected it display the selected test model in a nested component, TestDetailComponent.
That is all well and good and is working fine. I keep running into issues when I am pulling in the data from the server. Since JavaScript has no runtime checking we can't automatically cast the JSON from the server to a typescript class so I need to manually create a new instance of the TestModel with the retreived JSON.
Okay so here is the problem. I need to call new TestModel and give it its dependencies but it needs to be a new instance of TestModel. I want the TestModel to be able to save and update itself back to the server so it has a dependency on Http from @angular/core and it has a dependency on a config class I made that the angular injects with an opaqueToken, CONFIG.I can't figure out how to get new instances of TestModel. Here are the initial files
TestComponent:
import { Component, OnInit } from '@angular/core';
import { TestService } from './shared/test.service';
import { TestModel } from './shared/test.model';
import { TestDetailComponent } from './test-detail.component';
@Component({
selector: "test-component",
templateUrl: 'app/test/test.component.html',
styleUrls: [],
providers: [TestService],
directives: [TestDetailComponent]
})
export class TestComponent implements OnInit {
tests: TestModel[] = [];
selectedTest: TestModel;
constructor(private testService: TestService) {};
ngOnInit() {
this.testService.getTestsModels().subscribe( (tests) => {
console.log(tests);
this.tests = tests
});
}
}
TestComponent template:
<select [(ngModel)]="selectedTest">
<option *ngFor="let test of tests" [ngValue]="test">{{test.testing}}</option>
</select>
<test-detail *ngIf="selectedTest" [test]="selectedTest"></test-detail>
TestDetailComponent:
import { Component, Input } from '@angular/core';
import { JsonPipe } from '@angular/common';
import { TestModel } from './shared/test.model';
@Component({
selector: 'test-detail',
templateUrl: 'app/test/test-detail.component.html',
pipes: [JsonPipe]
})
export class TestDetailComponent {
@Input() test;
}
TestDetailComponent template
<p style="font-size: 3em;">{{test | json}}</p>
TestModel
import { Injectable, Inject } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { CONFIG } from './../../config/constants';
@Injectable()
export class TestModel {
"testing": number;
"that": string;
"a": string;
constructor(private http: Http, @Inject(CONFIG) private config) {}
save(): Observable<TestModel[]> {
let url = this.config.apiUrl + "test";
let body = JSON.stringify({
testing: this.testing,
this: this.that,
a: this.a
});
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http.post(url, body, options)
.map( (response) => response.json() )
.map( (results) => {
results.map( (aggregate, current) => {
aggregate.push(<TestModel>current);
return aggregate;
}, new Array<TestModel>())
}).catch(this.handleError);
}
update() {
let url = this.config.apiUrl + "test";
let body = JSON.stringify({
testing: this.testing,
this: this.that,
a: this.a
});
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http.put(url, body, options)
.map( (response) => response.json() )
.map( (results) => {
results.map( (aggregate, current) => {
aggregate.push(<TestModel>current);
return aggregate;
}, new Array<TestModel>())
}).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));
}
}
Test Service
import { Injectable, Inject } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { CONFIG } from './../../config/constants';
import { TestModel } from './test.model';
@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 <TestModel>current; // <<<--- here is the error
})
})
.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));
}
}
I have tried using the ReflectiveInjector so TestService becomes this:
import { Injectable, Inject, ReflectiveInjector } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { CONFIG } from './../../config/constants';
import { TestModel } from './test.model';
@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 ReflectiveInjector.resolveAndCreate([TestModel]).get(TestModel);
})
})
.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));
}
}
But then I just get the error:
Then if I add Http to the ReflectiveInjector I just get another connection backend error and I am assuming that would keep going to the dependency chain till we found the bottom.
Sorry for the long post, any help would be appreciated!
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?
这篇关于创建具有依赖关系的类的新实例,不了解工厂提供者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!