在两个角度组件和服务之间重用属性和方法的正确逻辑是什么?

时间:2017-08-23 16:37:19

标签: angular typescript typescript2.0

我有2个Angular Components ProductComponent和ImportListComponent 我为Product创建了一个模型如下:

import { Image } from './image.model';
import { Category } from './category.model';

export class Product {
  constructor(
    public id: number, 
    public name: string, 
    public priceRange: string, 
    public description: string, 
    public imageUrl: string,
    public initialPrice?: number,
    public importListPrice?: number,
    public category?: Category,
    public inStock?: boolean,
    public amountInStock?: number,
    public sku?: string, 
    public images?: Image[]
  ) {}
}

我还为ImportList创建了一个模型,该模型扩展了产品模型,如下所示:

import { Product }  from './product.model';
import { Image }    from './image.model';
import { Category } from './category.model';

export class ImportList extends Product {
  constructor(
    id: number, 
    name: string, 
    priceRange: string, 
    description: string, 
    imageUrl: string, 
    initialPrice?: number, 
    importListPrice?: number, 
    category?: Category, 
    inStock?: boolean, 
    amountInStock?: number, 
    sku?: string, 
    images?: Image[]
  ) {
    super(id, name, priceRange, description, imageUrl, initialPrice, importListPrice);
  }
}

Product和ImportList之间的区别在于,在将价格,描述和名称添加到ImportList并推送到商店之前,可以更改价格,描述和名称。 我们不希望更改的属性影响或改变原始产品。因此需要创建单独的ImportList。系统上的每个用户可以具有不同的ImportList。但他们可以访问相同的基础产品。 从API获取新产品的服务和逻辑如下:

@Injectable()
export class ProductService {
  private productsUrl = 'api/products'; // URL to web api 

  constructor(private http: Http) {}

  getProducts(): Promise<Product[]> {
    return this.http.get(this.productsUrl)
                .toPromise()
                .then(response => response.json().data as Product[])
                .catch(this.handleError);
  }
}

如何使模型类中的属性可重用? 如何通过ImportListService和ImportListComponent类可重用的ProductComponent中的方法使ProductService中的方法可重用?

因为我试图在ImportListComponent中使用ImportList模型只是为了这样的测试:

export class ImportListComponent {
  importList : ImportList[
    new ImportList()
  ];
}

我有一个令人困惑且不太清楚的错误Type 'new () => ImportList' cannot be used as an index type import ImportList。即使已导入ImportList。怎么解决这个问题?

2 个答案:

答案 0 :(得分:1)

由于类没有自定义逻辑或方法,我只需定义接口,并为importlist提供Product类型的新属性,以便它可以存储其原始产品的副本

export interface Product {
    id: number, 
    name: string, 
    priceRange: string, 
    description: string, 
    imageUrl: string,
    initialPrice?: number,
    importListPrice?: number,
    category?: Category,
    inStock?: boolean,
    amountInStock?: number,
    sku?: string, 
    images?: Image[] 
}

export interface ImportList {
    id:number,
    name:string.
    priceRange: string,
    description: string,
    imageUrl: string
    initialPrice: number; 
    importListPrice: number;
    originalProduct?: Product;
}

使用Angular4 HttpClient,您可以键入检查JSON是否正在读取:

 http.get<ImportList>('/apipath/here').subscribe(data => { 
     this.productList = data;
 });

这使得JSON足够接近ImportList接口以正确键入。如果需要事先修改,只需使用get()而不是类型化的get

答案 1 :(得分:1)

好的 - 接口速成课程,如果你决定走那条路。

 importList : ImportList[ new  

你如何开始它的两个问题。您无法实例化数组并在同一声明中键入它。你必须做

importList : ImportList[] =  [  ...declaration here... ]  

另一个问题是尝试使用&#39; new&#39;我提到的关键字。只是按字面意思构建对象。如果您想在一个声明中完成所有操作,它将如下所示:

importList : ImportList[] = [
      { id: 1, 
        name: 'somename',
        priceRange: '10$-15$,
        description: 'something',
        imageurl: 'someurl',
        originalProduct: this.variableNameOfOtherProduct, 
      },
      { id: 2, 
        name: 'othername'.    ... and so on and so on... 
      }
 ]

上述实际上不会以我编写接口的方式工作,因为它仍然需要初始价格和导入价格,这两个参数都没有被标记为可选。打字稿会出现错误&#34;无法设置类型 - 导入列表 - 属性&quot; initialPrice&#39;和&#39; importPrice&#39;缺少&#39;或类似的东西。

因此,您也可以在界面中将这两个参数设置为可选,或者确保在创建项目时提供它们。如果您查看许多角度核心接口,它们通常会将每个参数设置为可选。这允许根据需要以递增方式添加到对象的极大灵活性,但仍然具有强大的类型检查。我在我的界面中做同样的事情,我经常将每个参数设置为可选,除了它们中的一个或两个,唯一需要的绝对最小值。在你的情况下,可能是id。因此,除了那个名称之外,其他每个属性名称都可能在其名称后面有一个问号,并且您在创建时只需要必需来指定ID,并且可以在以后添加其他属性。从字面上键入每个对象的属性名称而不是仅仅包含它们的值可能看起来很麻烦,但它使代码无限可读/可维护。

有一点需要注意的是,如果您尝试在尚未实例化的类型的对象上引用属性,那么打字稿将会混淆。所以如果你创建了:

     [
        { id: 85, name 'somename'  }
       ...
     ]

作为this.importList数组中的第一项,(这假设除了id和name之外的所有内容都是可选的),然后你尝试通过编写

添加priceRange属性
 this.importList[0].priceRange = '10$ - 15$'; 

它会变得混乱,因为该物业尚未创建。在这些情况下,您只需使用javascript&#39;括号表示法&#39;用于向现有对象添加新属性。它与引用数组值的方式类似,但使用属性的字符串名称而不是内部的数字。

简单的例子,如果你有一些对象

 this.bob : BobInterface = { id: 1 } ; 

并希望添加一个名为&#39; age&#39;即使可以在Bob界面中指定年龄属性,它也没有&#39;已创建,所以你不能

 this.bob.age = 50;

你必须写

 this.bob['age'] = 50;

这会同时实例化并分配属性。

因此,在您的情况下,如果您需要在声明后向列表中添加其他属性,您可以编写类似

的内容
  this.importList[0]['priceRange']= '10$ - 15$';

第一组括号是指定我们引用的importList的哪个数组项,第二组括号是相应的ImportList项的属性。

在声明了这样的属性后,你可以写,

  this.importList[0].priceRange; 

和正常情况一样,打字稿会知道该怎么做。

SO - 回到这一点......要将现有产品添加到ImportList,您只需要将其标识符称为originalProduct属性的值。

如果您在没有它们的情况下初始化列表,可以稍后通过以下方式添加它们:

this.importList[0]['originalProduct']= referenceToProductObject;

但如果您在最初声明数组值时将其全部包含在内,那么您只需编写

 [
  ....
  {... priceRange: '10$ - 15$', originalProduct: this.originalProductList[25]  .... } 
 ....
 ]

在上面的示例中,我假设originalProduct位于索引#25的不同数组中,它可以在任何地方,只要它是Product类型的对象。

我希望这是有道理的。一开始bc类倾向于提供更多结构,这可能有点棘手 - 您通常知道类的所有属性都已实例化,因此您通常可以在没有括号表示的情况下引用它们。但是,使用像这样松散的接口可以成为处理对象的更强大的方法 - 获得极大的灵活性强类型检查。两全其美。

编辑:此外,也许是了解创建地图的好时机。在打字稿中。您可以像这样在

中声明组件中的对象
  importedProductsMap : { [index:string] : ImportList } = {};

以上可能非常强大。它创建一个对象并告诉typescript该对象将只保存ImportList对象,并且那些ImportList对象将全部由某个标识符引用,该标识符可以是任何字符串。对象中可能有100万个或零。没关系。只要它们是通过字符串命名的ImportList对象,就可以包含它们。这为数组提供了一个很好的替代方案,因为它们可以容纳许多项目,例如数组,但是您可以直接引用具有其ID或名称的单个项目(或者任何真实的,只要它对每个项目是唯一的),而不是它们的数字位置。一个数组,可以是非常arbirtary。

所以,如果你在for循环中,那么你通常会将ImportList对象添加到一个数组

 this.importList.push(aNewImportListObject);

您可以将它添加到上面定义的importedProductsMap中:

 this.importedProductsMap[aNewImportListObject.name] = aNewImportListObject;

这会将ImportList对象添加到您的地图中,使用自己的名称作为其标识符(在这种情况下,名称必须是唯一的,但仅举例来说,我们可以忘记这一点)。然后你可以像

那样引用它
 console.log(this.importedProductsMap.someName.id);

它会记录该对象的id属性。如果你有一个你经常提到和调整的对象列表,那么它使用地图比使用数组更容易。防止您不得不经常遍历列表并查找匹配项。

我不知道为什么我写了这一切,但我真诚地希望它有所帮助。

关于所有这一切的疯狂之处......它实际上并没有对编译器生成的结果代码产生一些影响。接口编译为空......它们基本上消失了,它们只是帮助您巧妙地与对象交互,编写更多结构化代码,并充分利用静态类型检查。