我被一个问题折磨了:我应该在角工程中将计算出的属性放在哪里?
例如: 我有模型,提供服务的模型和提供模型的组件。
person.model.ts:
export class Person {
firstName: string;
lastName: string;
}
person.service.ts:
export class PersonService {
// inject http: HttpClient
get(id) {
return this.http.get<Person>(`api-endpoint/person/${id}`);
}
}
person.component.ts
@Component({
selector: 'app',
template: `
<div>
<input [value]='person.firstName'>
<input [value]='person.lastName'>
</div>
`,
providers: [ PersonService ]
})
export class AppComponent {
person: Person;
// inject personService: PersonService
ngOnInit() {
personService.get(1).subscribe(p => this.person = p);
}
}
现在我需要fullName
才能将其显示在输入字段下的模板中。
选项1 。如果您用Google搜索“计算的角度属性”,则很有可能会在组件本身中找到带有计算的属性的示例。
@Component({
selector: 'app',
template: `
<div>
<input [value]='person.firstName'>
<input [value]='person.lastName'>
<span>{{ fullName }}</span>
</div>
`,
providers: [ PersonService ]
})
export class AppComponent {
person: Person;
get fullName() {
return `${this.person.firstName} ${this.person.lastName}`
}
// inject personService: PersonService
ngOnInit() {
personService.get(1).subscribe(p => this.person = p);
}
}
但这是这种代码的正确位置吗? 如果我们想在其他组件,服务等中重用此属性怎么办?
选项2。我个人想扩展person.model.ts
。
export class Person {
firstName: string;
lastName: string;
get fullName(): string {
return `${this.firstName} ${this.lastName}`
}
}
@Component({
selector: 'app',
template: `
<div>
<input [value]='person.firstName'>
<input [value]='person.lastName'>
<span>{{ person.fullName }}</span>
</div>
`,
providers: [ PersonService ]
})
export class AppComponent {
person: Person;
// inject personService: PersonService
ngOnInit() {
personService.get(1).subscribe(p => this.person = p);
}
}
但是随后我们面临另一个问题。我们的personService
返回的对象根本没有这个吸气剂。
那我该怎么办?我是否需要创建新的person.model.ts
实例,然后为其分配响应,还是根本需要另一个模型,例如person.view-model.ts
?
感谢您的时间:D
答案 0 :(得分:0)
我将添加一个我认为非常有用的模式。我个人喜欢保持关注的分离-即将所有Person
相关的计算属性保留在PersonService
中,并直接从服务中使用这些属性。特别是在其他地方使用它们时。
在您的PersonService
中,添加一个BehaviourSubject
,您可以将其绑定到其他模板中:
// person.serice.ts
private readonly _fullName: BehaviorSubject<string> = new BehaviorSubject('');
public readonly fullName$ = this._fullName.asObservable();
public get fullName(): string {
return this._fullName.getValue();
}
public set fullName(p: Person): string {
this._fullName.next(p.firstName + p.lastName);
}
// inject http: HttpClient
get(id) {
return this.http.get<Person>(`api-endpoint/person/${id}`)
.pipe(tap((p: Person) => this.fullName = p);
}
在您的组件中:
// person.component.ts
@Component({
selector: 'app',
template: `
<div>
<input [value]='person.firstName'>
<input [value]='person.lastName'>
<span>{{ fullName$ | async }}</span>
</div>
`,
providers: [ PersonService ]
})
export class AppComponent {
person: Person;
fullName$: Observable<string>;
// inject personService: PersonService
ngOnInit() {
this.fullName$ = this.personService.fullName$;
this.personService.get(1).subscribe(p => this.person = p);
}
}
答案 1 :(得分:0)
因此,对于选项2,您需要将来自服务器的数据封装到模型中。这是执行此操作的一种可能方法:
person.model.ts
export class Person {
firstName: string;
lastName: string;
constructor(obj) {
Object.assign(this, obj);
}
get fullName(): string {
return this.firstName + this.lastName;
}
}
当您实现此功能或使用其他一些保护逻辑(请检查扩展解决方案)时,可以在服务中执行以下操作:
person.service.ts
export class PersonService {
...
get(id): Observable<Person> {
return this.http.get<Partial<Person>>(`api-endpoint/person/${id}`).pipe(
map((response: Partial<Person>) => new Person(response)),
);
}
}
Partial<Person>
类型,因为它没有动态属性
尽管此基本解决方案容易受到运行时错误的影响。例如,如果初始化对象的某些字段与类定义冲突,并且它也不在意obj是否具有其他字段,那么您可能想要添加更多逻辑,例如:
filtered-object.helper.ts
import { keys } from 'ts-transformer-keys'; // enables to take out keys from interface
export function filteredObjec<T>(obj) {
const allowed = keys<T>();
return Object.keys(obj)
.filter(key => allowed.includes(key))
.reduce((obj, key) => {
obj[key] = raw[key];
return obj;
}, {});
}
请记住,这需要安装ts-transformer-keys
person.model.ts
import { filteredObjec } from 'path/to/filtered-object.helper';
...
constructor(obj) {
Object.assign(this, filteredObjec<Person>(obj));
}
或者,您可以创建自己的装饰器来描述字段,我在此不提供示例,因为这会非常繁琐。
答案 2 :(得分:0)
我已经成功使用了npm模块很长时间了:
https://www.npmjs.com/package/class-transformer
如果可以接受模型的类,可以节省生命,可以处理类的深层嵌套,以及许多其他不重要的情况。
不幸的是,我们的团队决定为模型使用接口,所以我现在要删除所有使用该模块的代码。