Angular 2 Typescript:是否有可能將介面作為參數傳入函數?
我有以下問題:我從 JSON API 拉取數據。目前我為每個數據模型(例如文章、用戶等)都有一個服務,並且為每個數據模型都有一個模型類。但這樣做很瘋狂,也不太可維護。所以我想重構,使我對每個數據模型都有一個接口和模型類,並有一個統一的 DataAPIService。
問題是,DataAPIService 中查詢 API 的函數不應該返回 JSON,而應該返回所查詢類型的物件或物件集合。因此,我需要一種方法將介面或類型傳遞到服務的查詢方法中,以便初始化此類型的新物件。
這有可能嗎?我說得通嗎?這裡有一些程式碼可以幫助理解我的意思,並展示我目前的進度。
import { Injectable } from '@angular/core';
import { AuthHttp } from 'angular2-jwt';
import 'rxjs/add/operator/map';
import { Config } from '../config/env.config';
@Injectable()
export class DataAPIService {
constructor(
private authHttp: AuthHttp
) {}
// This function will be called to retrieve data (for example from a Component).
// I want to pass in the object type or interface so that I only have one
// getIndex() function and not one for every data type.
getIndex(page:number = 1, object_name:string, object_type) {
return this.authHttp.get(Config.API_ENDPOINT + '/' + object_name + '?page=' + page)
.map(res => res.json())
.map(res => {
return this.fromJson(res.data, object_type);
});
}
// This function just checks which attributes are present in the object type
// and fills a new object of this type with the values from JSON.
// This is partly pseudo-code since I don't know how to solve this.
fromJson(input_json: any, object_type) {
// The next line is obviously not working. This is what I'm trying to figure out
var object:object_type = new object_type();
var json_attributes = input_json.attributes;
for (var key in json_attributes) {
if (object.hasOwnProperty(key)) {
object[key] = json_attributes[key];
}
}
object.id = input_json.id;
return object;
}
}
答案#1
你可以使用泛型來處理(如果你不知道這是什麼,我建議你搜尋這個)。
@Injectable()
export class DataAPIService {
constructor(
private authHttp: AuthHttp
) {}
// This function will be called to retrieve data (for example from a Component).
// I want to pass in the object type or interface so that I only have one
// getIndex() function and not one for every data type.
getIndex<T>(page:number = 1, object_name:string): Observable<T> {
return this.authHttp.get(Config.API_ENDPOINT + '/' + object_name + '?page=' + page)
.map(res => res.json());
}
只需在你的方法中添加 T 泛型,你就可以將返回類型定義為 T 類型值的 Observable。res.json() 只會創建一個對象,如果將它返回給此方法的調用者,他將只會看到 T 類型值的 Observable。無需為接口編寫如此特定的解析函數。
答案#2
這就是我如何解決整個問題的方式。對我來說重要的是,得到的物件不是一般的物件,而是例如 Post 類型的物件。我也想使用介面,並且希望物件的初始化能夠簡單。
首先,我有一個所有數據模型都繼承的基類。
base-model.model.ts
import * as _ from 'lodash';
export class BaseModel {
public id: string;
[key: string]: any;
constructor(private data?: any) {
// This basically does the initialization from a variable json object.
// I later on just pass the json data into the constructor.
if (data) {
this.id = data.id;
_.extend(this, data.attributes);
}
}
}
現在繼承自基礎模型的實際模型:
member.model.ts
// The actual model. It has an interface and extends the base class
// (so that the main logic is just in one place - DRY)
import { BaseModel } from './base-model.model';
interface MemberInterface {
email:string;
name:string;
}
export class Member extends BaseModel implements MemberInterface {
email:string;
name:string;
constructor(data?: any) {
super(data);
}
}
讓我們使用它。使用一個從 API 提取數據的服務
import { Injectable } from '@angular/core';
import { AuthHttp } from 'angular2-jwt';
import 'rxjs/add/operator/map';
import { Config } from '../config/env.config';
@Injectable()
export class MemberService {
constructor(public authHttp: AuthHttp) {}
// Calls the API and returns the result.
// authHttp works very similar to http. Just with added JWT protection
// check it out on GitHub: angular2-jwt
getIndex(page:number = 1):any {
let url = [Config.API_ENDPOINT, 'members', '?page='].join('/');
return this.authHttp.get(url + page)
.map(res => res.json())
.map(res => {
return res;
});
}
// Simpler example when just getting one entry
getOne(id: string):any {
let url = [Config.API_ENDPOINT, 'members', id].join('/');
return this.authHttp.get(url)
.map(res => res.json())
.map(res => {
return res;
});
}
}
最後,讓我們一起使用 Model 類和 Service
import { Component, OnInit } from '@angular/core';
import { MemberService } from '../shared/index';
import { Member } from '../shared/models/member.model';
@Component({
moduleId: module.id,
selector: 'app-member-list',
templateUrl: 'member-list.component.html',
styleUrls: ['member-list.component.css']
})
export class MemberListComponent implements OnInit {
private members: Array<Member>;
private member: Member;
constructor(private memberService: MemberService) {
this.members = [];
this.member = new Member();
}
ngOnInit():any {
// Pull the list on initialization
this.getIndex(1);
}
// For the index
getIndex(page:number = 1):Array<Member> {
this.memberService.getIndex(page).subscribe(
res => {
this.members = [];
for(let i = 0; i < res.data.length; i++) {
let member = new Member(res.data[i]);
this.members.push(member);
}
},
err => console.log(err)
);
}
// Simpler version with just one entry
getOne():any {
this.memberService.getIndex(page).subscribe(
res => {
this.member = new Member(res.data.attributes);
},
err => console.log(err)
);
}
}
