【问题标题】:Angular 2 component directive not working with serviceAngular 2 组件指令不适用于服务
【发布时间】:2026-02-22 17:00:01
【问题描述】:

我是 Angular 2 的初学者,我正在构建我的第一个应用程序,同时遵循 Pluralsight 课程。 我正在使用打字稿。 我有一个特定的组件(名为:property-detail.component.ts),我试图在其中引用来自服务(property.service.ts)的指令。 我在根模块中注册服务,并使用“contructor”方法将服务注入到组件中。 在一个组件(property-list.component.ts)中,我调用了一个产品列表,这很好用。 然后我尝试在(property-detail.component.ts)中调用相同的服务,我只想显示单个属性的详细信息,但是当我运行应用程序时,出现以下错误

错误信息

    {node_modules/@angular/core/bundles/core.umd.js:3462 EXCEPTION: Uncaught (in promise): Error: Error in app/property/property-detail.component.html:9:42 caused by: Cannot read property 'propertyName' of undefined
TypeError: Cannot read property 'propertyName' of undefined
    at _View_PropertyDetailComponent0.detectChangesInternal (PropertyDetailComponent.ngfactory.js:284:62)}

我正在为整个应用程序使用单个应用程序模块 (app.module.ts),以便为自己简化它。

这里是组件和模块:

app.module.ts

import { NgModule } from '@angular/core';
//brownermodule exposes the ngIg and ngFor directive
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { PropertyListComponent } from "./property/property-list.component";
import { PropertyCityFilterPipe } from "./property/property-cityfilter.pipe";
import { StarComponent } from "./shared/star.component";
import { PropertyDetailGuard } from "./property/property-guard.service";
import { PropertyDetailComponent } from "./property/property-detail.component";
import { WelcomeComponent } from "./home/welcome.component";
import { PropertyService } from "./property/property.service";





@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        RouterModule.forRoot([
            { path: 'property', component: PropertyListComponent },
            { path: 'property/:id', 
                canActivate: [PropertyDetailGuard],
                component: PropertyDetailComponent
            },
            { path: 'welcome', component: WelcomeComponent },
            { path: '', redirectTo: 'welcome', pathMatch: 'full' },
            { path: '**', redirectTo: 'welcome', pathMatch: 'full' }

        ])

    ],
    declarations: [
        AppComponent,
        PropertyListComponent,
        PropertyCityFilterPipe,
        StarComponent,
        PropertyDetailComponent,
        WelcomeComponent,


    ],

    providers: [PropertyDetailGuard, PropertyService],
    bootstrap: [ AppComponent ]
})
export class AppModule { }

property.service.ts

import { Injectable,    } from "@angular/core";
//import { IProperty } from "./property";
import { Http, Response } from "@angular/http";
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/catch";
import "rxjs/add/operator/do";


import "rxjs/add/operator/map";

import { IProperty } from "./property";


@Injectable()
export class PropertyService {
    private _propertyUrl = 'api/property/property.json';

    constructor(private _http: Http) { }

    getPropertys(): Observable<IProperty[]> {
        return this._http.get(this._propertyUrl)
            .map((response: Response) => <IProperty[]>response.json())
            .do(data => console.log('All: ' + JSON.stringify(data)))
            .catch(this.handleError);
    }

    getProperty(id: number): Observable<IProperty> {
        return this.getPropertys()
            .map((property: IProperty[]) => property.find(p => p.propertyId === id));
    }

    private handleError(error: Response) {
        // in a real world app, we may send the server to some remote logging infrastructure
        // instead of just logging it to the console
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

property-list.component.ts

import { Component, OnInit } from "@angular/core";
import { IProperty } from "./property";
import { PropertyService } from "./property.service";


@Component({
    moduleId: module.id,
    templateUrl: 'property-list.component.html',
    styleUrls: ['property-list.component.css']
    })



export class PropertyListComponent implements OnInit {
    pageTitle: string = 'Rental Listings';
    imageWidth: number = 120;
    imageMargin: number = 2;
    listFilter: string;
    errorMessage: string;

    property: IProperty[];

    constructor(private _propertyService: PropertyService) {

    }



    ngOnInit(): void {
        this._propertyService.getPropertys()
            .subscribe(property => this.property = property,
                        error => this.errorMessage = <any>error);


    }

    onRatingClicked(message: string): void {
         this.pageTitle = "Property: " + message;
    }


}

property-detail.component.ts

import { Component, OnInit, OnDestroy } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";

import { Subscription } from "rxjs/Subscription";

import { IProperty } from "./property";
import { PropertyService } from "./property.service";



@Component({
    templateUrl: 'app/property/property-detail.component.html'
})


export class PropertyDetailComponent implements OnInit, OnDestroy {
    pageTitle: string = 'Property Detail';
    property: IProperty;
    errorMessage: string;
    private sub: Subscription;

    constructor(private _route: ActivatedRoute,
                private _router: Router,
                private _propertyService: PropertyService) {
    }


    ngOnInit(): void {
        this.sub = this._route.params.subscribe(
            params => {
                let id = +params['id'];
                this.getProperty(id);
            });

    }

    ngOnDestroy() {
        this.sub.unsubscribe();
    }

    getProperty(id: number) {
        this._propertyService.getProperty(id).subscribe(
            property => this.property = property,
            error => this.errorMessage = <any>error);
    }

    onBack(): void {
        this._router.navigate(['/property']);
    }

    onRatingClicked(message: string): void {
        this.pageTitle = 'Property Detail: ' + message;
    }
}

property-detail.component.html(你会看到我只插入了一个指令 - {{property.propertyName}} 来简化事情

<div class='panel panel-primary'>
    <div class='panel-heading' style='font-size:large'>
        {{pageTitle}}
    </div>
    <div class='panel-body'>
        <div class='row'>
            <div class='col-md-6'>
                <div class='row'>
                    <div class='col-md-3'>Name:</div>
                    <div class='col-md-6'>{{property.propertyName}}</div>
                </div>
                <div class='row'>
                    <div class='col-md-3'>Code:</div>
                    <div class='col-md-6'></div>
                </div>
                <div class='row'>
                    <div class='col-md-3'>Description:</div>
                    <div class='col-md-6'></div>
                </div>
                <div class='row'>
                    <div class='col-md-3'>Availability:</div>
                    <div class='col-md-6'></div>
                </div>
                <div class='row'>
                    <div class='col-md-3'>Price:</div>
                    <div class='col-md-6'></div>
                </div>
                <div class='row'>
                    <div class='col-md-3'>5 Star Rating:</div>
                    <div class='col-md-6'>
                        <!--<cx-star [rating]='property.starRating'
                                 (ratingClicked)='onRatingClicked($event)'>
                        </cx-star>-->
                    </div>
                </div>
            </div>
            <!--<div class='col-md-6'>
                <img class='center-block img-responsive'
                     [style.width.px]='200'
                     [style.margin.px]='2'
                     [src]='property.imageUrl'
                     [title]='property.productName'>
            </div>-->
        </div>
    </div>
    <div class='panel-footer'>
        <a class='btn btn-default' (click)='onBack()' style='width:80px'>
            <i class='glyphicon glyphicon-chevron-left'></i> Back
        </a>
    </div>
</div>

我们将非常感谢您在这方面的任何帮助

【问题讨论】:

  • 在顶部的div标签中添加指令*ngIf="property",它只会在属性收费时显示模板
  • 我确实有这个,但由于该属性根本没有加载,所以我删除了它。
  • 把 *ngIf="property" 放在标签
    {{property.propertyName}}
    else 在订阅者返回值之前和渲染模板property.propertyName 未定义
  • 非常感谢,成功了!!!

标签: angular2-services angular2-directives


【解决方案1】:

将 *ngIf="property" 放在标签 {{property.propertyName}} 内,否则在订阅者返回值之前以及渲染模板时,propertyName 未定义 – alehn96 14 小时前

【讨论】: