Búsqueda de sitios web

javascript - Angular2 - Escribiendo datos suscritos a una interfaz


Tengo un componente que se está suscribiendo a datos de un servicio. Este servicio devuelve un BehaviorSubject al que puedo suscribirme. En el objeto de datos devuelto, hay varios arrays que componen la Etiqueta/Valor de mis menús desplegables.

Estoy intentando convertir cada uno de esos arrays a su propia interfaz y luego usarlos dentro de mi interfaz de usuario.

Componente:

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

}

Interfaz:

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

Servicio:

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

Objeto de Datos:

Esto :

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

Devoluciones :

Mi 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>

El Problema:

En mi componente, está lanzando un error de indefinido debido a que la suscripción no tiene los datos en el momento en que intenta asignarlos a la variable. Creo que hay algo mal en la forma en que me estoy suscribiendo a estos datos que no me permite asignar estos datos. Específicamente, está diciendo que segments es indefinido.

La razón por la que estoy intentando asignar segments directamente desde el objeto de resultados es porque quiero poder typearlo. Habrá muchos más menús desplegables que definiré una vez que resuelva este problema también.

Mi objetivo final en esto es que cada uno de los arreglos en el objeto que recibí esté tipado y tenga su propia interfaz. Preferiría solo tener que hacer una llamada al servicio que obtiene el objeto de datos y luego, a partir de ahí, asignarlo a sus propios objetos individuales, como estoy intentando hacer con los segments.

Muchas personas han señalado que es un problema de async debido a que la suscripción no tiene los datos en el momento en que los estoy asignando. Simplemente no estoy seguro de cómo resolver eso en mi configuración actual.

He probado un async pipe, ngIf y ?. de las sugerencias y todavía resulta en el error undefined.

Respuesta #1

Debido a que tus datos se están obteniendo de manera asincrónica, el HTML no recibe los datos y, por lo tanto, tus skillsets serán nulos cuando se construya el componente. Hay varias formas de manejar esto.

Método 1 : Usa una tubería async en tu html:

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

Método 2: Usar el operador Elvis ?.

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

Método 3: Usar *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>

Editar :

Aún necesitas inicializar tus skillsets en tu constructor. Typescript no puede saber mágicamente tu valor por defecto Skillsets.

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

O puedes hacerlo a nivel de tu propiedad:

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

También puedes hacer una conversión de tipo en tu respuesta, si quieres, usando 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('');
    }
} 

Respuesta #2

Necesitas inicializar el arreglo SkillSets

SkillSets[] = [];