简体   繁体   中英

Angular2 unable to map a single JSON object?

How do you map and use a JSON reponse that is a single object, rather than an array?

Recently, I started adding a new feature to a project I'm working on that should be taking a JSON response from an API and filling out a simple template with data from it. Shouldn't be difficult, right? Well, no... and yet, yes...

Mock version of the JSON response:

{
    "id": 1,
    "name": "Acaeris",
}

profile.service.ts

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Profile } from './profile';

/**
 * This class provides the Profile service with methods to read profile data
 */
@Injectable()
export class ProfileService {
    /**
     * Creates a new ProfileService with the injected Http.
     * @param {Http} http - The injected Http.
     * @constructor
     */
    constructor(private http: Http) {}

    /**
     * Returns an Observable for the HTTP GET request for the JSON resource.
     * @return {Profile} The Observable for the HTTP request.
     */
    get(): Observable<Profile> {
        return this.http.get('assets/profile.json')
            .map(res => <Profile>res.json())
            .catch(this.handleError);
    }

    /**
     * Handle HTTP error
     */
    private handleError (error: any) {
        let errMsg = (error.message) ? error.message :
            error.status ? `${error.status} - ${error.statusText}` : 'Server error';
        console.error(errMsg);
        return Observable.throw(errMsg);
    }
}

profile.component.ts

import { Component, OnInit } from '@angular/core';
import { ProfileService } from '../services/profile/profile.service';
import { Profile } from '../services/profile/profile';

/**
 * This class represents the lazy loaded ProfileComponent
 */
@Component({
    moduleId: module.id,
    selector: 'sd-profile',
    templateUrl: 'profile.component.html',
    styleUrls: ['profile.component.css'],
})
export class ProfileComponent implements OnInit {

    errorMessage: string;
    profile: Profile;

    /**
     * Creates an instance of the ProfileComponent with the injected
     * ProfileService
     *
     * @param {ProfileService} profileService - The injected ProfileService
     */
    constructor(public profileService: ProfileService) {}

    /**
     * Get the profile data
     */
    ngOnInit() {
        this.getProfile();
    }

    /**
     * Handles the profileService observable
     */
    getProfile() {
        this.profileService.get()
            .subscribe(
                data => this.profile = data,
                error => this.errorMessage = <any>error
            );
    }
}

profile.ts

export interface Profile {
    id: number;
    name: string;
}

And I'm just trying to output it using {{profile.name}} but this ends up with the console showing a whole load of error messages and no output. If I try to check the contents of profile after it has loaded, it tells me it is undefined .

However, here's the confusing part. If I replace all the Profile references to Profile[] , wrap the JSON in an array, add *ngFor="let p of profile" abd use {{p.name}} everything works fine. Unfortunately, in the actual finished application I would not have control of the JSON format. So what am I doing wrong when trying to handle it as a single object in comparison to handling as an array of objects?

Looks like at expression {{profile.name}} profile variable is undefined at page rendering moment. You can try either add some getter like this:

get profileName(): string { return this.profile ? this.profile.name ? ''; }

and use at template {{profileName}} or you can use ngIf at template like this:

<div *ngIf="profile">{{profile.name}}</div>

or shorter (as drewmoore suggested at comment below):

<div>{{profile?.name}}</div>

When you are working with array it is the same situation - at first rendering time array is undefined. ngFor handles this for you and renders nothing. When async operation of getting 'profile items' is complete - UI is rerendered again with correct values.

The map function returns Observables which are a collection of elements. It basically work the same way as the map function for arrays.

Now to solve you can replace the Profile references by Profile[] and use {{profile[0].name}} .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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