简体   繁体   English

Angular 2 Typescript:是否可以将接口作为参数传递给函数?

[英]Angular 2 Typescript: Is it possible to pass an interface as a parameter into a function?

I have the following problem: I pull data from a JSON API. 我有以下问题:我从JSON API中提取数据。 I have at the moment a service for every data model (eg. articles, users, etc) and a model class for every data model. 我目前为每个数据模型(例如文章,用户等)提供服务,并为每个数据模型提供模型类。 But this is insane and not really maintainable. 但这是疯狂的,不能真正维持。 So I would like to refactor so that I have an interface and a model class for every data model and one unified DataAPIService . 所以我想重构,以便为每个数据模型和一个统一的DataAPIService提供接口和模型类。

The problem is, that the functions in DataAPIService that query the API should not return JSON but objects or collections of objects of the type that has been queried. 问题是,查询API的DataAPIService中的函数不应返回JSON,而是返回已查询类型的对象或对象集合。 So I need a way to pass the interface or type into the query method of the service to then initialize a new object of this type. 所以我需要一种方法将接口或类型传递给服务的查询方法,然后初始化这种类型的新对象。

Is this possible? 这可能吗? Do I make sense? 我有道理吗? Here is some code to help understand what I mean and show my current progress. 这里有一些代码可以帮助理解我的意思并展示我目前的进展。

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;
  }

}

What you could do is work with generics (if you do not know what these are, I recommend googling this). 你可以做的是使用泛型(如果你不知道它们是什么,我建议使用谷歌搜索)。

@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());
  }

By just adding the T generic to your method, you can define the return type to be an Observable of values with type T. The res.json() will just create an object and if it is returned to the caller of this method, he'll just see an observable of values with type T. No need to write such a specific parsing function to an interface. 通过将T generic添加到您的方法中,您可以将返回类型定义为类型为T的值的Observable.res.json()将只创建一个对象,如果它返回给此方法的调用者,则我只是看到类型为T的值的可观察值。无需为接口编写这样的特定解析函数。

This is how I have solved the whole thing. 这就是我解决整个问题的方法。 It was important for me that the resulting object is not a generic object but for example of the type Post. 对我来说重要的是,生成的对象不是通用对象,而是Post类型的对象。 I also wanted to use interfaces and I wanted the initialization of the object to be easy. 我也想使用接口,我希望对象的初始化很容易。

First, I have a base class from which all data models inherit. 首先,我有一个基类,所有数据模型都从该基类继承。

base-model.model.ts 基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);
    }
  }
}

Now the actual model that inherits from the Base Model: 现在继承自Base Model的实际模型:

member.model.ts 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);
  }

}

Let's use it. 我们来使用吧。 With a Service that pulls data from an API 使用从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;
      });
  }


}

And finally let's use the Model class and the Service together 最后让我们一起使用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)
    );
  }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM