Angular2 - 将订阅数据键入接口

时间:2017-07-11 04:42:14

标签: javascript angular typescript

我有一个订阅服务数据的组件。此服务返回我可以订阅的BehaviorSubject。在返回的数据对象中,有几个数组构成了我的下拉列表的标签/值。

我正在尝试将每个数组的类型转换为自己的接口,然后在我的UI中使用它们。

组件:

import { Observable } from 'rxjs/Rx';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Component, OnInit } from '@angular/core';
import { MassEmpService } from '../shared';
import { Segments } from '../definitions';

@Component({
    selector: 'app-transition',
    templateUrl: './transition.component.html',
    styleUrls: ['./transition.component.css']
})
export class TransitionComponent implements OnInit {

    effectiveStartDate: string;
    error: string;
    transitionFields: any[];
    fieldsLoaded = false;
    transitionForm: FormGroup;
    segments: Segments[] = [];

    constructor(
        private fb: FormBuilder,
        private _massEmpService: MassEmpService
    ) {
    }

    ngOnInit() {
        this.createForm();
    }

    // Generate the form
    createForm() {

        // Create our form for the transition options
        this.transitionForm = this.fb.group({
            changeType: ['', Validators.required],
            effectiveStartDate: ['', Validators.required],
            effectiveEndDate: [''],
            hierarchy: ['-1'],
            segment: ['-1'],
            supervisor: ['-1'],
            budgetMarket: ['-1'],
            incentivePlan: ['-1'],
            role: ['-1'],
            primaryLanguage: ['-1'],
            secondaryLanguage: ['-1'],
        });

        // Get our form data to populate
        this.fetchFieldData();
    }

    // Fetch the data from our service and set our array to its value received
    fetchFieldData() {

        this._massEmpService.transitionFields.subscribe((results) => {
            this.segments = results.segments.options;
        });

        this._massEmpService.fetchTransitionFields('');
    }

}

接口

export interface Segments {
    SegmentID: number;
    SegmentName: string;
}

服务

public transitionFields: BehaviorSubject<any> = new BehaviorSubject(null);

/**
 * Return an object of the input field data to render the drop-downs.
 * If provided a date, use this to find inputs that will either be
 * available or removed on that date.
 *
 * @param {any} effectiveStartDate
 * @returns
 * @memberof MassEmpService
 */
fetchTransitionFields(effectiveStartDate) {
    return this._http.post(this.baseUrl + '/fetchTransitionFields', { "effectiveStartDate": effectiveStartDate }, { "headers": this.headers })
        .map((result: Response) => result.json())
        .subscribe((results) => this.transitionFields.next(results.data));
};

数据对象:

这:

this._massEmpService.transitionFields.subscribe((results) => {
  console.log(results);
});

返回:

enter image description here

我的HTML:

<select name="segment" id="segment" formControlName="segment" class="form-control input-sm">
  <option value="-1" selected="selected">No Segment Change</option>
  <option *ngFor="let s of segments" value="{{ s.SegmentID }}">{{ s.SegmentName }}</option>
</select>

问题:

在我的组件中,由于订阅在尝试将数据分配给var时没有数据,因此抛出了未定义的错误。我相信我订阅这些数据的方式有问题,不允许我分配这些数据。具体来说,它是segments未定义。

我尝试直接从结果对象分配segments的原因是因为我希望能够type它。一旦我弄清楚这个问题,我会定义更多的下拉菜单。

我的最终目标是我收到的对象中的每个数组都将typed输出并拥有自己的接口。我宁愿只需要对获取数据对象的服务进行一次调用,然后从那里将其分配给自己的各个对象,就像我正在尝试使用segments一样。

许多人指出,由于我在分配时没有获得数据而导致async问题。我只是不确定如何在我当前的设置中解决这个问题。

我在建议中尝试了async pipengIf?.,但仍然会导致未定义的错误。

4 个答案:

答案 0 :(得分:2)

由于您的数据是异步检索的,因此HTML不会获取数据,因此构建组件时skillsets将为null。有几种方法可以解决这个问题。

方法1 :在html中使用async管道:

<select name="skillset" id="skillset" formControlName="skillSet" class="form-control input-sm">
    <option value="-1" selected="selected">No SkillSet Change</option>
    <option *ngFor="let s of skillSets | async" value="{{ s.SkillSetID }}">{{ s.SkillSetName }}</option>
</select>

方法2 :使用猫王操作员?.

<select name="skillset" id="skillset" formControlName="skillSet" class="form-control input-sm">
    <option value="-1" selected="selected">No SkillSet Change</option>
    <option *ngFor="let s of skillSets" value="{{ s?.SkillSetID }}">{{ s?.SkillSetName }}</option>
</select>

方法3 :使用*ngIf

<select *ngIf="skillSets" name="skillset" id="skillset" formControlName="skillSet" class="form-control input-sm">
    <option value="-1" selected="selected">No SkillSet Change</option>
    <option *ngFor="let s of skillSets" value="{{ s.SkillSetID }}">{{ s.SkillSetName }}</option>
</select>

编辑:

您仍需要在构造函数中初始化skillsets。打字稿不能神奇地知道你的默认值Skillsets

constructor(){
    this.skillSets = [];
}

或者你可以在你的财产层面上做到:

skillSets: SkillSets[] = []; //initialize to empty array.

如果需要,您还可以使用as

在回复中对其进行类型转换
export class DemoComponent {
    effectiveStartDate: string;
    error: string;
    transitionFields: any[];
    fieldsLoaded = false;
    transitionForm: FormGroup;
    skillSets: SkillSets[];

    constructor(){
        this.skillSets = [];
    }
    // Fetch the data from our service and set our array to its value received
    fetchFieldData() {

        this._massEmpService.transitionFields.subscribe((results) => {
            this.transitionFields = results;
            this.skillSets = results.segments.options as SkillSets[];
        });

        this._massEmpService.fetchTransitionFields('');
    }
} 

答案 1 :(得分:1)

您需要初始化SkillSets数组

{{1}}

答案 2 :(得分:0)

据我所知,你有两个选择:

首先只使用* ngIf,另一个是在构造函数中预定义类。

E.g:

export interface SkillSets {
    SkillSetID: number;
    SkillSetName: string;
    constructor() {
      this.SkillSetID = 0;
      this.SkillSetName = "Yet undefined";
    }
}

在组件内部,例如在构造函数中:

this.skillSets = new SkillSets();

答案 3 :(得分:0)

如果您想使用异步管道,请像这样设置您的技能

  skillSets = this._massEmpService.transitionFields();

请注意,您不需要订阅方法。这是使用异步管道并使代码更短的好处。 并在您的模板中。

  <option *ngFor="let s of skillSets | async" value="{{ s.SkillSetID }}">
  {{ s.SkillSetName }}</option>

最后,像这样更新你的skillSet数据类型

skillSets: Observable<SkillSets[]>;

因为你现在处理的是observable而不仅仅是一个普通的数组。

请注意,如果遇到问题,请尝试在构造函数或OnInit中设置skillSets,从那里调用fetchFieldData()。

更新1

由于您从更新中提供了大量新代码,因此请点击我的最新答案。

如果您使用异步管道,则不需要此代码

this.segments = results.segments.options;

这是您从服务中获取选项数据的方式。您担心的是,您获得了整个数据,而您只需要选择&#39;选项。数据。您需要更改服务中返回数据的方式。将其更改为此

return this._http.post(this.baseUrl + '/fetchTransitionFields', { "effectiveStartDate": effectiveStartDate }, { "headers": this.headers })
        .map((result: Response) => result.json().segments.options)

希望这有帮助